1use super::*;
2
3impl Parser {
4 fn is_upper_camel_segment(name: &str) -> bool {
5 name.chars().next().is_some_and(|c| c.is_uppercase())
6 }
7
8 fn is_constructor_path(path: &str) -> bool {
9 path.rsplit('.')
10 .next()
11 .is_some_and(Self::is_upper_camel_segment)
12 }
13
14 fn reject_zero_arg_constructor_call(&self, path: &str) -> Result<(), ParseError> {
15 if Self::is_constructor_path(path) && self.peek(1).kind == TokenKind::RParen {
16 return Err(self.error(format!(
17 "Zero-argument constructor call '{}()' is not allowed. Use '{}' (no parentheses).",
18 path, path
19 )));
20 }
21 Ok(())
22 }
23
24 pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
25 self.parse_comparison()
26 }
27
28 pub(super) fn parse_comparison(&mut self) -> Result<Expr, ParseError> {
29 let mut left = self.parse_additive()?;
30
31 loop {
32 let op = match &self.current().kind {
33 TokenKind::Eq => BinOp::Eq,
34 TokenKind::Neq => BinOp::Neq,
35 TokenKind::Lt => BinOp::Lt,
36 TokenKind::Gt => BinOp::Gt,
37 TokenKind::Lte => BinOp::Lte,
38 TokenKind::Gte => BinOp::Gte,
39 _ => break,
40 };
41 self.advance();
42 let right = self.parse_additive()?;
43 left = Expr::BinOp(op, Box::new(left), Box::new(right));
44 }
45
46 Ok(left)
47 }
48
49 pub(super) fn parse_additive(&mut self) -> Result<Expr, ParseError> {
50 let mut left = self.parse_multiplicative()?;
51
52 loop {
53 let op = match &self.current().kind {
54 TokenKind::Plus => BinOp::Add,
55 TokenKind::Minus => BinOp::Sub,
56 _ => break,
57 };
58 self.advance();
59 let right = self.parse_multiplicative()?;
60 left = Expr::BinOp(op, Box::new(left), Box::new(right));
61 }
62
63 Ok(left)
64 }
65
66 pub(super) fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
67 let mut left = self.parse_unary()?;
68
69 loop {
70 let op = match &self.current().kind {
71 TokenKind::Star => BinOp::Mul,
72 TokenKind::Slash => BinOp::Div,
73 _ => break,
74 };
75 self.advance();
76 let right = self.parse_unary()?;
77 left = Expr::BinOp(op, Box::new(left), Box::new(right));
78 }
79
80 Ok(left)
81 }
82
83 pub(super) fn parse_unary(&mut self) -> Result<Expr, ParseError> {
84 if self.check_exact(&TokenKind::Minus) {
85 self.advance();
86 let operand = self.parse_postfix()?;
87 return Ok(Expr::BinOp(
88 BinOp::Sub,
89 Box::new(Expr::Literal(Literal::Int(0))),
90 Box::new(operand),
91 ));
92 }
93 self.parse_postfix()
94 }
95
96 pub(super) fn parse_postfix(&mut self) -> Result<Expr, ParseError> {
97 let mut expr = self.parse_call_or_atom()?;
98
99 loop {
100 if self.check_exact(&TokenKind::Question) {
101 self.advance();
102 expr = Expr::ErrorProp(Box::new(expr));
103 } else if self.check_exact(&TokenKind::Dot) {
104 self.advance();
105 let field_tok = self.expect_kind(
106 &TokenKind::Ident(String::new()),
107 "Expected field name after '.'",
108 )?;
109 let field = match field_tok.kind {
110 TokenKind::Ident(s) => s,
111 _ => unreachable!(),
112 };
113 expr = Expr::Attr(Box::new(expr), field);
114 if self.check_exact(&TokenKind::LParen) {
115 if let Some(path) = Self::dotted_name(&expr)
117 && path.ends_with(".update")
118 {
119 let prefix = &path[..path.len() - ".update".len()];
120 if !prefix.is_empty()
121 && prefix.chars().next().is_some_and(|c| c.is_uppercase())
122 {
123 self.advance(); let base = self.parse_expr()?;
125 let updates = if self.check_exact(&TokenKind::Comma) {
126 self.advance();
127 self.skip_formatting();
128 self.parse_record_create_fields()?
129 } else {
130 Vec::new()
131 };
132 self.expect_exact(&TokenKind::RParen)?;
133 expr = Expr::RecordUpdate {
134 type_name: prefix.to_string(),
135 base: Box::new(base),
136 updates,
137 };
138 continue;
139 }
140 }
141 if let Some(path) = Self::dotted_name(&expr) {
142 self.reject_zero_arg_constructor_call(&path)?;
143 }
144 let named_arg_start = matches!(&self.peek(1).kind, TokenKind::Ident(_))
145 && self.peek(2).kind == TokenKind::Assign;
146 if named_arg_start && let Some(path) = Self::dotted_name(&expr) {
147 if path == "Tcp.Connection" {
148 return Err(self.error(
149 "Cannot construct 'Tcp.Connection' directly. Use Tcp.connect(host, port)."
150 .to_string(),
151 ));
152 }
153 return Err(self.error(format!(
154 "Named-field call syntax is only valid for direct record constructors like User(...), not '{}(...)'",
155 path
156 )));
157 }
158 self.advance();
159 let args = self.parse_args()?;
160 self.expect_exact(&TokenKind::RParen)?;
161 expr = Expr::FnCall(Box::new(expr), args);
162 }
163 } else {
164 break;
165 }
166 }
167
168 Ok(expr)
169 }
170
171 pub(super) fn dotted_name(expr: &Expr) -> Option<String> {
172 match expr {
173 Expr::Ident(name) => Some(name.clone()),
174 Expr::Attr(inner, field) => {
175 let mut base = Self::dotted_name(inner)?;
176 base.push('.');
177 base.push_str(field);
178 Some(base)
179 }
180 _ => None,
181 }
182 }
183
184 pub(super) fn parse_call_or_atom(&mut self) -> Result<Expr, ParseError> {
185 let atom = self.parse_atom()?;
186
187 if self.check_exact(&TokenKind::LParen) {
188 if let Some(path) = Self::dotted_name(&atom) {
189 self.reject_zero_arg_constructor_call(&path)?;
190 }
191
192 let is_record_create = if let Expr::Ident(ref name) = atom {
196 name.chars().next().is_some_and(|c| c.is_uppercase())
197 && matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
198 && self.peek_skip_formatting(2).kind == TokenKind::Assign
199 } else {
200 false
201 };
202 let named_arg_start = matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
203 && self.peek_skip_formatting(2).kind == TokenKind::Assign;
204
205 if is_record_create && let Expr::Ident(type_name) = atom {
206 self.advance(); let fields = self.parse_record_create_fields()?;
208 self.expect_exact(&TokenKind::RParen)?;
209 return Ok(Expr::RecordCreate { type_name, fields });
210 }
211
212 if named_arg_start && let Some(path) = Self::dotted_name(&atom) {
213 if path == "Tcp.Connection" {
214 return Err(self.error(
215 "Cannot construct 'Tcp.Connection' directly. Use Tcp.connect(host, port)."
216 .to_string(),
217 ));
218 }
219 return Err(self.error(format!(
220 "Named-field call syntax is only valid for direct record constructors like User(...), not '{}(...)'",
221 path
222 )));
223 }
224
225 self.advance();
226 let args = self.parse_args()?;
227 self.expect_exact(&TokenKind::RParen)?;
228 return Ok(Expr::FnCall(Box::new(atom), args));
229 }
230
231 Ok(atom)
232 }
233
234 pub(super) fn parse_record_create_fields(&mut self) -> Result<Vec<(String, Expr)>, ParseError> {
236 let mut fields = Vec::new();
237 self.skip_formatting();
238
239 while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
240 if self.check_exact(&TokenKind::Comma) {
241 self.advance();
242 self.skip_formatting();
243 continue;
244 }
245 let name_tok =
246 self.expect_kind(&TokenKind::Ident(String::new()), "Expected field name")?;
247 let field_name = match name_tok.kind {
248 TokenKind::Ident(s) => s,
249 _ => unreachable!(),
250 };
251 self.expect_exact(&TokenKind::Assign)?;
252 let value = self.parse_expr()?;
253 fields.push((field_name, value));
254 self.skip_formatting();
255 }
256
257 Ok(fields)
258 }
259
260 pub(super) fn parse_args(&mut self) -> Result<Vec<Expr>, ParseError> {
261 let mut args = Vec::new();
262 self.skip_formatting();
263
264 while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
265 if self.check_exact(&TokenKind::Comma) {
266 self.advance();
267 self.skip_formatting();
268 continue;
269 }
270 args.push(self.parse_expr()?);
271 self.skip_formatting();
272 }
273
274 Ok(args)
275 }
276
277 pub(super) fn parse_map_literal(&mut self) -> Result<Expr, ParseError> {
278 self.expect_exact(&TokenKind::LBrace)?;
279 let mut entries = Vec::new();
280 self.skip_formatting();
281
282 while !self.check_exact(&TokenKind::RBrace) && !self.is_eof() {
283 if self.check_exact(&TokenKind::Comma) {
284 self.advance();
285 self.skip_formatting();
286 continue;
287 }
288
289 let key = self.parse_expr()?;
290 self.skip_formatting();
291 if !self.check_exact(&TokenKind::FatArrow) {
292 return Err(
293 self.error("Expected '=>' between key and value in map literal".to_string())
294 );
295 }
296 self.advance(); self.skip_formatting();
298 let value = self.parse_expr()?;
299 entries.push((key, value));
300 self.skip_formatting();
301
302 if self.check_exact(&TokenKind::Comma) {
303 self.advance();
304 self.skip_formatting();
305 }
306 }
307
308 self.expect_exact(&TokenKind::RBrace)?;
309 Ok(Expr::MapLiteral(entries))
310 }
311
312 pub(super) fn parse_atom(&mut self) -> Result<Expr, ParseError> {
313 match self.current().kind.clone() {
314 TokenKind::Int(i) => {
315 self.advance();
316 Ok(Expr::Literal(Literal::Int(i)))
317 }
318 TokenKind::Float(f) => {
319 self.advance();
320 Ok(Expr::Literal(Literal::Float(f)))
321 }
322 TokenKind::Str(s) => {
323 self.advance();
324 Ok(Expr::Literal(Literal::Str(s)))
325 }
326 TokenKind::InterpStr(parts) => {
327 self.advance();
328 let mut str_parts = Vec::new();
329 for (is_expr, s) in parts {
330 if is_expr {
331 if s.trim().is_empty() {
333 str_parts.push(StrPart::Literal(String::new()));
334 } else {
335 let mut lexer = crate::lexer::Lexer::new(&s);
336 let tokens = lexer.tokenize().map_err(|e| ParseError::Error {
337 msg: format!("Error in interpolation: {}", e),
338 line: self.current().line,
339 col: self.current().col,
340 })?;
341 let mut sub_parser = Parser::new(tokens);
342 let expr = sub_parser.parse_expr().map_err(|e| ParseError::Error {
343 msg: format!("Error in interpolation: {}", e),
344 line: self.current().line,
345 col: self.current().col,
346 })?;
347 str_parts.push(StrPart::Parsed(Box::new(expr)));
348 }
349 } else {
350 str_parts.push(StrPart::Literal(s));
351 }
352 }
353 Ok(Expr::InterpolatedStr(str_parts))
354 }
355 TokenKind::Bool(b) => {
356 self.advance();
357 Ok(Expr::Literal(Literal::Bool(b)))
358 }
359 TokenKind::Match => self.parse_match(),
360 TokenKind::LParen => {
361 self.advance();
362 let first = self.parse_expr()?;
363 if self.check_exact(&TokenKind::Comma) {
364 let mut items = vec![first];
365 while self.check_exact(&TokenKind::Comma) {
366 self.advance();
367 items.push(self.parse_expr()?);
368 }
369 self.expect_exact(&TokenKind::RParen)?;
370 Ok(Expr::Tuple(items))
371 } else {
372 self.expect_exact(&TokenKind::RParen)?;
373 Ok(first)
374 }
375 }
376 TokenKind::Ident(s) => {
377 self.advance();
378 Ok(Expr::Ident(s))
379 }
380 TokenKind::LBracket => {
381 self.advance(); let mut elements = Vec::new();
383 self.skip_formatting();
384 while !self.check_exact(&TokenKind::RBracket) && !self.is_eof() {
385 if self.check_exact(&TokenKind::Comma) {
386 self.advance();
387 self.skip_formatting();
388 continue;
389 }
390 elements.push(self.parse_expr()?);
391 self.skip_formatting();
392 }
393 self.expect_exact(&TokenKind::RBracket)?;
394 Ok(Expr::List(elements))
395 }
396 TokenKind::LBrace => self.parse_map_literal(),
397 TokenKind::Fn => Err(self.error(
398 "Anonymous functions are not supported in Aver. Define a top-level function and pass its name."
399 .to_string(),
400 )),
401 _ => Err(self.error(format!(
402 "Expected expression (identifier, literal, '[', or '{{'), found {}",
403 self.current().kind
404 ))),
405 }
406 }
407}