Skip to main content

aver/parser/
expr.rs

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    /// Wrap an Expr with the current token's line.
25    fn spanned(&self, expr: Expr, line: usize) -> Spanned<Expr> {
26        Spanned::new(expr, line)
27    }
28
29    pub fn parse_expr(&mut self) -> Result<Spanned<Expr>, ParseError> {
30        self.parse_comparison()
31    }
32
33    pub(super) fn parse_comparison(&mut self) -> Result<Spanned<Expr>, ParseError> {
34        let mut left = self.parse_additive()?;
35
36        loop {
37            let line = self.current().line;
38            let op = match &self.current().kind {
39                TokenKind::Eq => BinOp::Eq,
40                TokenKind::Neq => BinOp::Neq,
41                TokenKind::Lt => BinOp::Lt,
42                TokenKind::Gt => BinOp::Gt,
43                TokenKind::Lte => BinOp::Lte,
44                TokenKind::Gte => BinOp::Gte,
45                _ => break,
46            };
47            self.advance();
48            let right = self.parse_additive()?;
49            left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
50        }
51
52        Ok(left)
53    }
54
55    pub(super) fn parse_additive(&mut self) -> Result<Spanned<Expr>, ParseError> {
56        let mut left = self.parse_multiplicative()?;
57
58        loop {
59            let line = self.current().line;
60            let op = match &self.current().kind {
61                TokenKind::Plus => BinOp::Add,
62                TokenKind::Minus => BinOp::Sub,
63                _ => break,
64            };
65            self.advance();
66            let right = self.parse_multiplicative()?;
67            left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
68        }
69
70        Ok(left)
71    }
72
73    pub(super) fn parse_multiplicative(&mut self) -> Result<Spanned<Expr>, ParseError> {
74        let mut left = self.parse_unary()?;
75
76        loop {
77            let line = self.current().line;
78            let op = match &self.current().kind {
79                TokenKind::Star => BinOp::Mul,
80                TokenKind::Slash => BinOp::Div,
81                _ => break,
82            };
83            self.advance();
84            let right = self.parse_unary()?;
85            left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
86        }
87
88        Ok(left)
89    }
90
91    pub(super) fn parse_unary(&mut self) -> Result<Spanned<Expr>, ParseError> {
92        if self.check_exact(&TokenKind::Minus) {
93            let line = self.current().line;
94            self.advance();
95            let operand = self.parse_postfix()?;
96            return Ok(self.spanned(
97                Expr::BinOp(
98                    BinOp::Sub,
99                    Box::new(Spanned::bare(Expr::Literal(Literal::Int(0)))),
100                    Box::new(operand),
101                ),
102                line,
103            ));
104        }
105        self.parse_postfix()
106    }
107
108    pub(super) fn parse_postfix(&mut self) -> Result<Spanned<Expr>, ParseError> {
109        let mut expr = self.parse_call_or_atom()?;
110
111        loop {
112            if self.check_exact(&TokenKind::Question) && self.peek(1).kind == TokenKind::Bang {
113                // ?! on tuple: independent product with Result unwrapping
114                let line = self.current().line;
115                if let Expr::Tuple(elements) = expr.node {
116                    self.advance(); // consume ?
117                    self.advance(); // consume !
118                    expr = self.spanned(Expr::IndependentProduct(elements, true), line);
119                } else {
120                    return Err(self.error(
121                        "Operator '?!' can only be applied to a tuple expression, e.g. (a, b)?!"
122                            .to_string(),
123                    ));
124                }
125            } else if self.check_exact(&TokenKind::Bang)
126                && !matches!(self.peek(1).kind, TokenKind::LBracket)
127            {
128                // ! on tuple: independent product without unwrapping
129                // (but not `! [Effects]` which is an effect declaration)
130                let line = self.current().line;
131                if let Expr::Tuple(elements) = expr.node {
132                    self.advance(); // consume !
133                    expr = self.spanned(Expr::IndependentProduct(elements, false), line);
134                } else {
135                    break; // bare ! on non-tuple — not ours, leave for caller
136                }
137            } else if self.check_exact(&TokenKind::Question) {
138                let line = self.current().line;
139                self.advance();
140                expr = self.spanned(Expr::ErrorProp(Box::new(expr)), line);
141            } else if self.check_exact(&TokenKind::Dot) {
142                let dot_line = self.current().line;
143                self.advance();
144                let field_tok = self.expect_kind(
145                    &TokenKind::Ident(String::new()),
146                    "Expected field name after '.'",
147                )?;
148                let field = match field_tok.kind {
149                    TokenKind::Ident(s) => s,
150                    _ => unreachable!(),
151                };
152                expr = self.spanned(Expr::Attr(Box::new(expr), field), dot_line);
153                if self.check_exact(&TokenKind::LParen) {
154                    // Detect `Type.update(base, field = val, ...)` for record update
155                    if let Some(path) = Self::dotted_name(&expr)
156                        && path.ends_with(".update")
157                    {
158                        let prefix = &path[..path.len() - ".update".len()];
159                        if !prefix.is_empty()
160                            && prefix.chars().next().is_some_and(|c| c.is_uppercase())
161                        {
162                            let update_line = self.current().line;
163                            self.advance(); // consume (
164                            let base = self.parse_expr()?;
165                            let updates = if self.check_exact(&TokenKind::Comma) {
166                                self.advance();
167                                self.skip_formatting();
168                                self.parse_record_create_fields()?
169                            } else {
170                                Vec::new()
171                            };
172                            self.expect_exact(&TokenKind::RParen)?;
173                            expr = self.spanned(
174                                Expr::RecordUpdate {
175                                    type_name: prefix.to_string(),
176                                    base: Box::new(base),
177                                    updates,
178                                },
179                                update_line,
180                            );
181                            continue;
182                        }
183                    }
184                    if let Some(path) = Self::dotted_name(&expr) {
185                        self.reject_zero_arg_constructor_call(&path)?;
186                    }
187                    let named_arg_start = matches!(&self.peek(1).kind, TokenKind::Ident(_))
188                        && self.peek(2).kind == TokenKind::Assign;
189                    if named_arg_start && let Some(path) = Self::dotted_name(&expr) {
190                        // Dotted record constructor: Tcp.Connection(id = ..., host = ...)
191                        let ctor_line = self.current().line;
192                        self.advance();
193                        let fields = self.parse_record_create_fields()?;
194                        self.expect_exact(&TokenKind::RParen)?;
195                        expr = self.spanned(
196                            Expr::RecordCreate {
197                                type_name: path,
198                                fields,
199                            },
200                            ctor_line,
201                        );
202                    } else {
203                        let call_line = self.current().line;
204                        self.advance();
205                        let args = self.parse_args()?;
206                        self.expect_exact(&TokenKind::RParen)?;
207                        expr = self.spanned(Expr::FnCall(Box::new(expr), args), call_line);
208                    }
209                }
210            } else {
211                break;
212            }
213        }
214
215        Ok(expr)
216    }
217
218    pub(super) fn dotted_name(expr: &Spanned<Expr>) -> Option<String> {
219        match &expr.node {
220            Expr::Ident(name) => Some(name.clone()),
221            Expr::Attr(inner, field) => {
222                let mut base = Self::dotted_name(inner)?;
223                base.push('.');
224                base.push_str(field);
225                Some(base)
226            }
227            _ => None,
228        }
229    }
230
231    pub(super) fn parse_call_or_atom(&mut self) -> Result<Spanned<Expr>, ParseError> {
232        let atom = self.parse_atom()?;
233
234        if self.check_exact(&TokenKind::LParen) {
235            if let Some(path) = Self::dotted_name(&atom) {
236                self.reject_zero_arg_constructor_call(&path)?;
237            }
238
239            // Lookahead: is this `Name(field = value, ...)` (record creation)?
240            // Detect by checking if token after `(` is `Ident` followed by `=`.
241            // Use peek_skip_formatting to handle multiline constructor syntax.
242            let is_record_create = if let Expr::Ident(ref name) = atom.node {
243                name.chars().next().is_some_and(|c| c.is_uppercase())
244                    && matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
245                    && self.peek_skip_formatting(2).kind == TokenKind::Assign
246            } else {
247                false
248            };
249            let named_arg_start = matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
250                && self.peek_skip_formatting(2).kind == TokenKind::Assign;
251
252            if is_record_create && let Expr::Ident(type_name) = atom.node {
253                let line = atom.line;
254                self.advance(); // consume (
255                let fields = self.parse_record_create_fields()?;
256                self.expect_exact(&TokenKind::RParen)?;
257                return Ok(self.spanned(Expr::RecordCreate { type_name, fields }, line));
258            }
259
260            // Dotted record constructor: Tcp.Connection(id = ..., host = ...)
261            if named_arg_start && let Some(path) = Self::dotted_name(&atom) {
262                let line = atom.line;
263                self.advance();
264                let fields = self.parse_record_create_fields()?;
265                self.expect_exact(&TokenKind::RParen)?;
266                return Ok(self.spanned(
267                    Expr::RecordCreate {
268                        type_name: path,
269                        fields,
270                    },
271                    line,
272                ));
273            }
274
275            let call_line = self.current().line;
276            self.advance();
277            let args = self.parse_args()?;
278            self.expect_exact(&TokenKind::RParen)?;
279            return Ok(self.spanned(Expr::FnCall(Box::new(atom), args), call_line));
280        }
281
282        Ok(atom)
283    }
284
285    /// Parse named-field arguments for record creation: `name = expr, name2 = expr2`
286    pub(super) fn parse_record_create_fields(
287        &mut self,
288    ) -> Result<Vec<(String, Spanned<Expr>)>, ParseError> {
289        let mut fields = Vec::new();
290        self.skip_formatting();
291
292        while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
293            if self.check_exact(&TokenKind::Comma) {
294                self.advance();
295                self.skip_formatting();
296                continue;
297            }
298            let name_tok =
299                self.expect_kind(&TokenKind::Ident(String::new()), "Expected field name")?;
300            let field_name = match name_tok.kind {
301                TokenKind::Ident(s) => s,
302                _ => unreachable!(),
303            };
304            self.expect_exact(&TokenKind::Assign)?;
305            let value = self.parse_expr()?;
306            fields.push((field_name, value));
307            self.skip_formatting();
308        }
309
310        Ok(fields)
311    }
312
313    pub(super) fn parse_args(&mut self) -> Result<Vec<Spanned<Expr>>, ParseError> {
314        let mut args = Vec::new();
315        self.skip_formatting();
316
317        while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
318            if self.check_exact(&TokenKind::Comma) {
319                self.advance();
320                self.skip_formatting();
321                continue;
322            }
323            args.push(self.parse_expr()?);
324            self.skip_formatting();
325        }
326
327        Ok(args)
328    }
329
330    pub(super) fn parse_map_literal(&mut self) -> Result<Expr, ParseError> {
331        self.expect_exact(&TokenKind::LBrace)?;
332        let mut entries = Vec::new();
333        self.skip_formatting();
334
335        while !self.check_exact(&TokenKind::RBrace) && !self.is_eof() {
336            if self.check_exact(&TokenKind::Comma) {
337                self.advance();
338                self.skip_formatting();
339                continue;
340            }
341
342            let key = self.parse_expr()?;
343            self.skip_formatting();
344            if !self.check_exact(&TokenKind::FatArrow) {
345                return Err(
346                    self.error("Expected '=>' between key and value in map literal".to_string())
347                );
348            }
349            self.advance(); // =>
350            self.skip_formatting();
351            let value = self.parse_expr()?;
352            entries.push((key, value));
353            self.skip_formatting();
354
355            if self.check_exact(&TokenKind::Comma) {
356                self.advance();
357                self.skip_formatting();
358            }
359        }
360
361        self.expect_exact(&TokenKind::RBrace)?;
362        Ok(Expr::MapLiteral(entries))
363    }
364
365    pub(super) fn parse_atom(&mut self) -> Result<Spanned<Expr>, ParseError> {
366        let line = self.current().line;
367        match self.current().kind.clone() {
368            TokenKind::Int(i) => {
369                self.advance();
370                Ok(self.spanned(Expr::Literal(Literal::Int(i)), line))
371            }
372            TokenKind::Float(f) => {
373                self.advance();
374                Ok(self.spanned(Expr::Literal(Literal::Float(f)), line))
375            }
376            TokenKind::Str(s) => {
377                self.advance();
378                Ok(self.spanned(Expr::Literal(Literal::Str(s)), line))
379            }
380            TokenKind::InterpStr(parts) => {
381                self.advance();
382                let mut str_parts = Vec::new();
383                for (is_expr, s) in parts {
384                    if is_expr {
385                        // Parse the interpolation expression; empty `{}` → empty literal.
386                        if s.trim().is_empty() {
387                            str_parts.push(StrPart::Literal(String::new()));
388                        } else {
389                            let mut lexer = crate::lexer::Lexer::new(&s);
390                            let tokens = lexer.tokenize().map_err(|e| ParseError::Error {
391                                msg: format!("Error in interpolation: {}", e),
392                                line: self.current().line,
393                                col: self.current().col,
394                            })?;
395                            let mut sub_parser = Parser::new(tokens);
396                            let expr = sub_parser.parse_expr().map_err(|e| ParseError::Error {
397                                msg: format!("Error in interpolation: {}", e),
398                                line: self.current().line,
399                                col: self.current().col,
400                            })?;
401                            str_parts.push(StrPart::Parsed(Box::new(expr)));
402                        }
403                    } else {
404                        str_parts.push(StrPart::Literal(s));
405                    }
406                }
407                Ok(self.spanned(Expr::InterpolatedStr(str_parts), line))
408            }
409            TokenKind::Bool(b) => {
410                self.advance();
411                Ok(self.spanned(Expr::Literal(Literal::Bool(b)), line))
412            }
413            TokenKind::Match => {
414                let m = self.parse_match()?;
415                Ok(self.spanned(m, line))
416            }
417            TokenKind::LParen => {
418                self.advance();
419                let first = self.parse_expr()?;
420                if self.check_exact(&TokenKind::Comma) {
421                    let mut items = vec![first];
422                    while self.check_exact(&TokenKind::Comma) {
423                        self.advance();
424                        items.push(self.parse_expr()?);
425                    }
426                    self.expect_exact(&TokenKind::RParen)?;
427                    Ok(self.spanned(Expr::Tuple(items), line))
428                } else {
429                    self.expect_exact(&TokenKind::RParen)?;
430                    Ok(first)
431                }
432            }
433            TokenKind::Ident(s) => {
434                self.advance();
435                if s == "Unit" {
436                    Ok(self.spanned(Expr::Literal(Literal::Unit), line))
437                } else {
438                    Ok(self.spanned(Expr::Ident(s), line))
439                }
440            }
441            TokenKind::LBracket => {
442                self.advance(); // consume [
443                let mut elements = Vec::new();
444                self.skip_formatting();
445                while !self.check_exact(&TokenKind::RBracket) && !self.is_eof() {
446                    if self.check_exact(&TokenKind::Comma) {
447                        self.advance();
448                        self.skip_formatting();
449                        continue;
450                    }
451                    elements.push(self.parse_expr()?);
452                    self.skip_formatting();
453                }
454                self.expect_exact(&TokenKind::RBracket)?;
455                Ok(self.spanned(Expr::List(elements), line))
456            }
457            TokenKind::LBrace => {
458                let map = self.parse_map_literal()?;
459                Ok(self.spanned(map, line))
460            }
461            TokenKind::Fn => Err(self.error(
462                "Anonymous functions are not supported in Aver. Define a top-level function and pass its name."
463                    .to_string(),
464            )),
465            _ => Err(self.error(format!(
466                "Expected expression (identifier, literal, '[', or '{{'), found {}",
467                self.current().kind
468            ))),
469        }
470    }
471}