1use serde_json::{Value, json, Map};
2use std::iter::Peekable;
3use std::str::Chars;
4
5#[derive(Debug, PartialEq)]
6pub enum ReaderError {
7 UnexpectedChar(char),
8 UnterminatedString,
9 UnbalancedParenthesis,
10 InvalidNumber(String),
11}
12
13#[derive(Debug, Clone)]
14enum Val {
15 Literal(Value),
16 Symbol(String),
17}
18
19pub fn read(input: &str) -> Result<Value, ReaderError> {
20 let mut tokens = Tokenizer::new(input);
21 let mut results = Vec::new();
22
23 while let Some(token) = tokens.next_token()? {
24 results.push(parse_token_wrapped(token, &mut tokens)?);
25 }
26
27 let final_vals: Vec<Value> = results.into_iter().map(|v| v_to_json_literal(v)).collect();
28
29 if final_vals.len() == 1 {
30 Ok(final_vals[0].clone())
31 } else {
32 let mut do_block = vec![json!("do")];
33 do_block.extend(final_vals);
34 Ok(Value::Array(do_block))
35 }
36}
37
38pub fn read_program(input: &str) -> Result<Value, ReaderError> {
39 let program_val = read(input)?;
40 Ok(json!({
41 "state": { "client": {}, "server": {} },
42 "program": program_val
43 }))
44}
45
46fn v_to_json_expr(v: Val) -> Value {
47 match v {
48 Val::Literal(v) => v,
49 Val::Symbol(s) => {
50 if s == "true" { json!(true) }
51 else if s == "false" { json!(false) }
52 else if s == "null" { json!(null) }
53 else { json!(["var", s]) }
54 }
55 }
56}
57
58fn v_to_json_literal(v: Val) -> Value {
59 match v {
60 Val::Literal(v) => v,
61 Val::Symbol(s) => {
62 if s == "true" { json!(true) }
63 else if s == "false" { json!(false) }
64 else if s == "null" { json!(null) }
65 else { json!(s) }
66 }
67 }
68}
69
70fn parse_token_wrapped(first: Token, tokens: &mut Tokenizer) -> Result<Val, ReaderError> {
71 match first {
72 Token::LParen => {
73 let mut list = Vec::new();
74 let mut is_first = true;
75
76 while let Some(t) = tokens.peek_token()? {
77 if t == Token::RParen {
78 tokens.next_token()?;
79 return Ok(Val::Literal(Value::Array(list)));
80 }
81
82 let next_t = tokens.next_token()?.unwrap();
83 let val_wrapped = parse_token_wrapped(next_t, tokens)?;
84
85 let op_name = list.first().and_then(|v| v.as_str()).unwrap_or("");
86 let pos = list.len();
87
88 let final_val = match val_wrapped {
92 Val::Symbol(s) => {
93 if is_first {
94 json!(s)
95 } else {
96 if s == "true" { json!(true) }
98 else if s == "false" { json!(false) }
99 else if s == "null" { json!(null) }
100 else {
101 let is_literal_pos = (op_name == "set" || op_name == "def" || op_name == "call") && pos == 1;
103 if is_literal_pos {
104 json!(s)
105 } else {
106 json!(["var", s])
107 }
108 }
109 }
110 }
111 Val::Literal(v) => v,
112 };
113
114 list.push(final_val);
115 is_first = false;
116 }
117
118 if let Some(Value::String(op)) = list.first() {
120 if (op == "!" || op == "not" || op == "len") && list.len() == 1 {
121 list.push(json!(null));
122 }
123 }
124
125 Err(ReaderError::UnbalancedParenthesis)
126 }
127 Token::RParen => Err(ReaderError::UnbalancedParenthesis),
128 Token::LBracket => {
129 let mut list = Vec::new();
130 while let Some(t) = tokens.peek_token()? {
131 if t == Token::RBracket {
132 tokens.next_token()?;
133 return Ok(Val::Literal(Value::Array(list)));
134 }
135 let next_t = tokens.next_token()?.unwrap();
136 list.push(v_to_json_expr(parse_token_wrapped(next_t, tokens)?));
137 }
138 Err(ReaderError::UnbalancedParenthesis)
139 }
140 Token::RBracket => Err(ReaderError::UnbalancedParenthesis),
141 Token::LBrace => {
142 let mut map = Map::new();
143 while let Some(t) = tokens.next_token()? {
144 match t {
145 Token::RBrace => return Ok(Val::Literal(Value::Object(map))),
146 Token::Symbol(key) | Token::String(key) => {
147 if let Some(Token::Symbol(s)) = tokens.peek_token()? {
148 if s == ":" { tokens.next_token()?; }
149 }
150 let val_token = tokens.next_token()?.ok_or(ReaderError::UnexpectedChar('}'))?;
151 let val = v_to_json_expr(parse_token_wrapped(val_token, tokens)?);
152 map.insert(key, val);
153 }
154 _ => return Err(ReaderError::UnexpectedChar('?')),
155 }
156 }
157 Err(ReaderError::UnexpectedChar('}'))
158 }
159 Token::RBrace => Err(ReaderError::UnexpectedChar('}')),
160 Token::String(s) => Ok(Val::Literal(json!(s))),
161 Token::Number(n) => Ok(Val::Literal(json!(n))),
162 Token::Symbol(s) => Ok(Val::Symbol(s)),
163 }
164}
165
166#[derive(Debug, PartialEq, Clone)]
167enum Token {
168 LParen, RParen, LBrace, RBrace, LBracket, RBracket,
169 String(String),
170 Number(f64),
171 Symbol(String),
172}
173
174struct Tokenizer<'a> {
175 chars: Peekable<Chars<'a>>,
176 peeked: Option<Token>,
177}
178
179impl<'a> Tokenizer<'a> {
180 fn new(input: &'a str) -> Self {
181 Self { chars: input.chars().peekable(), peeked: None }
182 }
183
184 fn peek_token(&mut self) -> Result<Option<Token>, ReaderError> {
185 if self.peeked.is_none() {
186 self.peeked = self.next_token_inner()?;
187 }
188 Ok(self.peeked.clone())
189 }
190
191 fn next_token(&mut self) -> Result<Option<Token>, ReaderError> {
192 if let Some(t) = self.peeked.take() {
193 return Ok(Some(t));
194 }
195 self.next_token_inner()
196 }
197
198 fn next_token_inner(&mut self) -> Result<Option<Token>, ReaderError> {
199 loop {
200 while let Some(&c) = self.chars.peek() {
201 if c.is_whitespace() || c == ',' || c == ':' {
202 self.chars.next();
203 continue;
204 }
205 break;
206 }
207
208 if let Some(&'#') = self.chars.peek() {
209 while let Some(c) = self.chars.next() {
211 if c == '\n' { break; }
212 }
213 continue;
214 }
215 break;
216 }
217
218 let c = match self.chars.next() {
219 Some(c) => c,
220 None => return Ok(None),
221 };
222
223 match c {
224 '(' => Ok(Some(Token::LParen)),
225 ')' => Ok(Some(Token::RParen)),
226 '{' => Ok(Some(Token::LBrace)),
227 '}' => Ok(Some(Token::RBrace)),
228 '[' => Ok(Some(Token::LBracket)),
229 ']' => Ok(Some(Token::RBracket)),
230 '\'' | '"' => {
231 let quote = c;
232 let mut s = String::new();
233 let mut escaped = false;
234 while let Some(nc) = self.chars.next() {
235 if escaped {
236 s.push(nc);
237 escaped = false;
238 } else if nc == '\\' {
239 escaped = true;
240 } else if nc == quote {
241 return Ok(Some(Token::String(s)));
242 } else {
243 s.push(nc);
244 }
245 }
246 Err(ReaderError::UnterminatedString)
247 }
248 _ if c.is_digit(10) || (c == '-' && self.chars.peek().is_some_and(|&nc| nc.is_digit(10))) => {
249 let mut s = c.to_string();
250 while let Some(&nc) = self.chars.peek() {
251 if nc.is_digit(10) || nc == '.' {
252 s.push(self.chars.next().unwrap());
253 } else { break; }
254 }
255 let n = s.parse::<f64>().map_err(|_| ReaderError::InvalidNumber(s))?;
256 Ok(Some(Token::Number(n)))
257 }
258 _ => {
259 let mut s = c.to_string();
260 while let Some(&nc) = self.chars.peek() {
261 if nc.is_whitespace() || nc == '(' || nc == ')' || nc == '{' || nc == '}' || nc == '[' || nc == ']' || nc == ',' || nc == ':' {
262 break;
263 }
264 s.push(self.chars.next().unwrap());
265 }
266 Ok(Some(Token::Symbol(s)))
267 }
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_basic_read() {
278 let input = "(+ 1 2)";
279 let val = read(input).unwrap();
280 assert_eq!(val, json!(["+", 1.0, 2.0]));
281 }
282
283 #[test]
284 fn test_symbol_var_wrapping() {
285 let input = "(set client.count (+ client.count 1))";
286 let val = read(input).unwrap();
287 assert_eq!(val, json!(["set", "client.count", ["+", ["var", "client.count"], 1.0]]));
288 }
289
290 #[test]
291 fn test_nested_objects() {
292 let input = "(call api.post \"/url\" { status: \"ok\", val: (+ x 1) })";
293 let val = read(input).unwrap();
294 assert_eq!(val, json!([
295 "call", "api.post", "/url",
296 {
297 "status": "ok",
298 "val": ["+", ["var", "x"], 1.0]
299 }
300 ]));
301 }
302}