atmos/os_lib/aura/
parser.rs1use super::lexer::Token;
2use super::number::{BigInt, Ratio};
3use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::vec;
6use alloc::vec::Vec;
7
8#[derive(Debug, Clone)]
9pub enum AST {
10 Symbol(String),
11 Num(BigInt),
12 Ratio(Ratio),
13 Str(String),
14 List(Vec<AST>),
15 Dict(Vec<(String, AST)>),
16 FuncCall(Box<AST>, Vec<AST>),
17}
18
19pub fn parse(tokens: &[Token]) -> Result<Vec<AST>, String> {
20 let mut asts = Vec::new();
21 let mut pos = 0;
22 while pos < tokens.len() {
23 let (ast, next_pos) = parse_expr(tokens, pos, false)?;
24 asts.push(ast);
25 pos = next_pos;
26 }
27 Ok(asts)
28}
29
30fn parse_expr(tokens: &[Token], mut pos: usize, in_list: bool) -> Result<(AST, usize), String> {
31 if pos >= tokens.len() {
32 return Err(String::from("Unexpected end of input"));
33 }
34
35 match &tokens[pos] {
36 Token::Ident(name) => {
37 let has_dot = name.contains('.');
39
40 if pos + 1 < tokens.len() && tokens[pos + 1] == Token::Colon {
42 if pos + 2 < tokens.len() {
43 let key_str = match &tokens[pos + 2] {
44 Token::Ident(k) => k.clone(),
45 Token::Str(s) => s.clone(),
46 _ => return Err(String::from("Expected identifier or string after colon")),
47 };
48 pos += 3; Ok((
50 AST::FuncCall(
51 Box::new(AST::Symbol(String::from("get"))),
52 alloc::vec![AST::Symbol(name.clone()), AST::Str(key_str)],
53 ),
54 pos,
55 ))
56 } else {
57 Err(String::from("Missing key after colon"))
58 }
59 }
60 else if !in_list
62 && pos + 1 < tokens.len()
63 && matches!(
64 tokens[pos + 1],
65 Token::LParen {
66 leading_space: false
67 }
68 )
69 {
70 let func_expr = if has_dot {
71 if let Some((obj_name, key_name)) = name.rsplit_once('.') {
72 AST::FuncCall(
73 Box::new(AST::Symbol(String::from("get"))),
74 alloc::vec![
75 AST::Symbol(String::from(obj_name)),
76 AST::Str(String::from(key_name))
77 ],
78 )
79 } else {
80 AST::Symbol(name.clone())
81 }
82 } else {
83 AST::Symbol(name.clone())
84 };
85
86 pos += 2; let mut args = Vec::new();
89 while pos < tokens.len() && tokens[pos] != Token::RParen {
90 let (expr, next_pos) = parse_expr(tokens, pos, false)?;
91 args.push(expr);
92 pos = next_pos;
93 }
94
95 if pos >= tokens.len() {
96 return Err(String::from("Missing closing parenthesis ')'"));
97 }
98 pos += 1; Ok((AST::FuncCall(Box::new(func_expr), args), pos))
100 } else if has_dot {
101 if let Some((obj_name, key_name)) = name.rsplit_once('.') {
103 Ok((
104 AST::FuncCall(
105 Box::new(AST::Symbol(String::from("get"))),
106 alloc::vec![
107 AST::Symbol(String::from(obj_name)),
108 AST::Str(String::from(key_name))
109 ],
110 ),
111 pos + 1,
112 ))
113 } else {
114 Ok((AST::Symbol(name.clone()), pos + 1))
115 }
116 } else {
117 Ok((AST::Symbol(name.clone()), pos + 1))
118 }
119 }
120 Token::Num(num) => Ok((AST::Num(num.clone()), pos + 1)),
121 Token::Ratio(ratio) => Ok((AST::Ratio(ratio.clone()), pos + 1)),
122 Token::Str(s) => Ok((AST::Str(s.clone()), pos + 1)),
123 Token::TemplateStr(s) => {
124 let mut template_parts = String::new();
125 let mut expr_asts = Vec::new();
126 let mut chars = s.chars().peekable();
127
128 while let Some(c) = chars.next() {
129 if c == '$' {
130 if chars.peek() == Some(&'$') {
131 chars.next(); template_parts.push('$');
133 } else if chars.peek() == Some(&'{') {
134 chars.next(); template_parts.push_str("${}");
136
137 let mut expr_str = String::new();
138 let mut format_spec = String::new();
139 let mut brace_depth = 1;
140 let mut in_format = false;
141
142 for ec in chars.by_ref() {
143 if ec == '{' {
144 brace_depth += 1;
145 } else if ec == '}' {
146 brace_depth -= 1;
147 if brace_depth == 0 {
148 break;
149 }
150 }
151
152 if ec == ':' && brace_depth == 1 {
153 in_format = true;
154 continue;
155 }
156
157 if in_format {
158 format_spec.push(ec);
159 } else {
160 expr_str.push(ec);
161 }
162 }
163
164 let sub_tokens = super::lexer::tokenize(&expr_str);
165 let sub_asts = super::parser::parse(&sub_tokens)?;
166 let expr_ast = if sub_asts.is_empty() {
167 AST::Symbol(String::from("nil"))
168 } else {
169 sub_asts[0].clone()
170 };
171
172 expr_asts.push((expr_ast, format_spec));
173 } else {
174 template_parts.push('$');
175 }
176 } else {
177 template_parts.push(c);
178 }
179 }
180
181 let mut list_exprs = vec![AST::Symbol(String::from("list"))];
182 let mut list_formats = vec![AST::Symbol(String::from("list"))];
183 for (ast, fmt) in expr_asts {
184 list_exprs.push(ast);
185 list_formats.push(AST::Str(fmt));
186 }
187
188 Ok((
189 AST::FuncCall(
190 Box::new(AST::Symbol(String::from("os.template-eval"))),
191 vec![
192 AST::Str(template_parts),
193 AST::List(list_exprs),
194 AST::List(list_formats),
195 ],
196 ),
197 pos + 1,
198 ))
199 }
200 Token::LParen { .. } => {
201 let mut list = Vec::new();
203 pos += 1; while pos < tokens.len() && tokens[pos] != Token::RParen {
206 let (expr, next_pos) = parse_expr(tokens, pos, true)?;
207 list.push(expr);
208 pos = next_pos;
209 }
210
211 if pos >= tokens.len() {
212 return Err(String::from("Missing closing parenthesis ')'"));
213 }
214 pos += 1; Ok((AST::List(list), pos))
216 }
217 Token::RParen => Err(String::from("Unexpected closing parenthesis ')'")),
218 Token::LBrace => {
219 let mut dict = Vec::new();
220 pos += 1; while pos < tokens.len() && tokens[pos] != Token::RBrace {
223 let key_str = match &tokens[pos] {
225 Token::Str(s) => s.clone(),
226 _ => return Err(String::from("Expected string key in dictionary")),
227 };
228 pos += 1;
229
230 if pos >= tokens.len() || tokens[pos] != Token::Colon {
232 return Err(String::from("Expected ':' after dictionary key"));
233 }
234 pos += 1; let (val_expr, next_pos) = parse_expr(tokens, pos, false)?;
238 dict.push((key_str, val_expr));
239 pos = next_pos;
240 }
241
242 if pos >= tokens.len() {
243 return Err(String::from("Missing closing brace '}'"));
244 }
245 pos += 1; Ok((AST::Dict(dict), pos))
247 }
248 Token::RBrace => Err(String::from("Unexpected closing brace '}'")),
249 Token::Colon => Err(String::from("Unexpected colon ':'")),
250 }
251}