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    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                    // Detect `Type.update(base, field = val, ...)` for record update
116                    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(); // consume (
124                            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                        // Dotted record constructor: Tcp.Connection(id = ..., host = ...)
148                        self.advance();
149                        let fields = self.parse_record_create_fields()?;
150                        self.expect_exact(&TokenKind::RParen)?;
151                        expr = Expr::RecordCreate {
152                            type_name: path,
153                            fields,
154                        };
155                    } else {
156                        self.advance();
157                        let args = self.parse_args()?;
158                        self.expect_exact(&TokenKind::RParen)?;
159                        expr = Expr::FnCall(Box::new(expr), args);
160                    }
161                }
162            } else {
163                break;
164            }
165        }
166
167        Ok(expr)
168    }
169
170    pub(super) fn dotted_name(expr: &Expr) -> Option<String> {
171        match expr {
172            Expr::Ident(name) => Some(name.clone()),
173            Expr::Attr(inner, field) => {
174                let mut base = Self::dotted_name(inner)?;
175                base.push('.');
176                base.push_str(field);
177                Some(base)
178            }
179            _ => None,
180        }
181    }
182
183    pub(super) fn parse_call_or_atom(&mut self) -> Result<Expr, ParseError> {
184        let atom = self.parse_atom()?;
185
186        if self.check_exact(&TokenKind::LParen) {
187            if let Some(path) = Self::dotted_name(&atom) {
188                self.reject_zero_arg_constructor_call(&path)?;
189            }
190
191            // Lookahead: is this `Name(field = value, ...)` (record creation)?
192            // Detect by checking if token after `(` is `Ident` followed by `=`.
193            // Use peek_skip_formatting to handle multiline constructor syntax.
194            let is_record_create = if let Expr::Ident(ref name) = atom {
195                name.chars().next().is_some_and(|c| c.is_uppercase())
196                    && matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
197                    && self.peek_skip_formatting(2).kind == TokenKind::Assign
198            } else {
199                false
200            };
201            let named_arg_start = matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
202                && self.peek_skip_formatting(2).kind == TokenKind::Assign;
203
204            if is_record_create && let Expr::Ident(type_name) = atom {
205                self.advance(); // consume (
206                let fields = self.parse_record_create_fields()?;
207                self.expect_exact(&TokenKind::RParen)?;
208                return Ok(Expr::RecordCreate { type_name, fields });
209            }
210
211            // Dotted record constructor: Tcp.Connection(id = ..., host = ...)
212            if named_arg_start && let Some(path) = Self::dotted_name(&atom) {
213                self.advance();
214                let fields = self.parse_record_create_fields()?;
215                self.expect_exact(&TokenKind::RParen)?;
216                return Ok(Expr::RecordCreate {
217                    type_name: path,
218                    fields,
219                });
220            }
221
222            self.advance();
223            let args = self.parse_args()?;
224            self.expect_exact(&TokenKind::RParen)?;
225            return Ok(Expr::FnCall(Box::new(atom), args));
226        }
227
228        Ok(atom)
229    }
230
231    /// Parse named-field arguments for record creation: `name = expr, name2 = expr2`
232    pub(super) fn parse_record_create_fields(&mut self) -> Result<Vec<(String, Expr)>, ParseError> {
233        let mut fields = Vec::new();
234        self.skip_formatting();
235
236        while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
237            if self.check_exact(&TokenKind::Comma) {
238                self.advance();
239                self.skip_formatting();
240                continue;
241            }
242            let name_tok =
243                self.expect_kind(&TokenKind::Ident(String::new()), "Expected field name")?;
244            let field_name = match name_tok.kind {
245                TokenKind::Ident(s) => s,
246                _ => unreachable!(),
247            };
248            self.expect_exact(&TokenKind::Assign)?;
249            let value = self.parse_expr()?;
250            fields.push((field_name, value));
251            self.skip_formatting();
252        }
253
254        Ok(fields)
255    }
256
257    pub(super) fn parse_args(&mut self) -> Result<Vec<Expr>, ParseError> {
258        let mut args = Vec::new();
259        self.skip_formatting();
260
261        while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
262            if self.check_exact(&TokenKind::Comma) {
263                self.advance();
264                self.skip_formatting();
265                continue;
266            }
267            args.push(self.parse_expr()?);
268            self.skip_formatting();
269        }
270
271        Ok(args)
272    }
273
274    pub(super) fn parse_map_literal(&mut self) -> Result<Expr, ParseError> {
275        self.expect_exact(&TokenKind::LBrace)?;
276        let mut entries = Vec::new();
277        self.skip_formatting();
278
279        while !self.check_exact(&TokenKind::RBrace) && !self.is_eof() {
280            if self.check_exact(&TokenKind::Comma) {
281                self.advance();
282                self.skip_formatting();
283                continue;
284            }
285
286            let key = self.parse_expr()?;
287            self.skip_formatting();
288            if !self.check_exact(&TokenKind::FatArrow) {
289                return Err(
290                    self.error("Expected '=>' between key and value in map literal".to_string())
291                );
292            }
293            self.advance(); // =>
294            self.skip_formatting();
295            let value = self.parse_expr()?;
296            entries.push((key, value));
297            self.skip_formatting();
298
299            if self.check_exact(&TokenKind::Comma) {
300                self.advance();
301                self.skip_formatting();
302            }
303        }
304
305        self.expect_exact(&TokenKind::RBrace)?;
306        Ok(Expr::MapLiteral(entries))
307    }
308
309    pub(super) fn parse_atom(&mut self) -> Result<Expr, ParseError> {
310        match self.current().kind.clone() {
311            TokenKind::Int(i) => {
312                self.advance();
313                Ok(Expr::Literal(Literal::Int(i)))
314            }
315            TokenKind::Float(f) => {
316                self.advance();
317                Ok(Expr::Literal(Literal::Float(f)))
318            }
319            TokenKind::Str(s) => {
320                self.advance();
321                Ok(Expr::Literal(Literal::Str(s)))
322            }
323            TokenKind::InterpStr(parts) => {
324                self.advance();
325                let mut str_parts = Vec::new();
326                for (is_expr, s) in parts {
327                    if is_expr {
328                        // Parse the interpolation expression; empty `{}` → empty literal.
329                        if s.trim().is_empty() {
330                            str_parts.push(StrPart::Literal(String::new()));
331                        } else {
332                            let mut lexer = crate::lexer::Lexer::new(&s);
333                            let tokens = lexer.tokenize().map_err(|e| ParseError::Error {
334                                msg: format!("Error in interpolation: {}", e),
335                                line: self.current().line,
336                                col: self.current().col,
337                            })?;
338                            let mut sub_parser = Parser::new(tokens);
339                            let expr = sub_parser.parse_expr().map_err(|e| ParseError::Error {
340                                msg: format!("Error in interpolation: {}", e),
341                                line: self.current().line,
342                                col: self.current().col,
343                            })?;
344                            str_parts.push(StrPart::Parsed(Box::new(expr)));
345                        }
346                    } else {
347                        str_parts.push(StrPart::Literal(s));
348                    }
349                }
350                Ok(Expr::InterpolatedStr(str_parts))
351            }
352            TokenKind::Bool(b) => {
353                self.advance();
354                Ok(Expr::Literal(Literal::Bool(b)))
355            }
356            TokenKind::Match => self.parse_match(),
357            TokenKind::LParen => {
358                self.advance();
359                let first = self.parse_expr()?;
360                if self.check_exact(&TokenKind::Comma) {
361                    let mut items = vec![first];
362                    while self.check_exact(&TokenKind::Comma) {
363                        self.advance();
364                        items.push(self.parse_expr()?);
365                    }
366                    self.expect_exact(&TokenKind::RParen)?;
367                    Ok(Expr::Tuple(items))
368                } else {
369                    self.expect_exact(&TokenKind::RParen)?;
370                    Ok(first)
371                }
372            }
373            TokenKind::Ident(s) => {
374                self.advance();
375                if s == "Unit" {
376                    Ok(Expr::Literal(Literal::Unit))
377                } else {
378                    Ok(Expr::Ident(s))
379                }
380            }
381            TokenKind::LBracket => {
382                self.advance(); // consume [
383                let mut elements = Vec::new();
384                self.skip_formatting();
385                while !self.check_exact(&TokenKind::RBracket) && !self.is_eof() {
386                    if self.check_exact(&TokenKind::Comma) {
387                        self.advance();
388                        self.skip_formatting();
389                        continue;
390                    }
391                    elements.push(self.parse_expr()?);
392                    self.skip_formatting();
393                }
394                self.expect_exact(&TokenKind::RBracket)?;
395                Ok(Expr::List(elements))
396            }
397            TokenKind::LBrace => self.parse_map_literal(),
398            TokenKind::Fn => Err(self.error(
399                "Anonymous functions are not supported in Aver. Define a top-level function and pass its name."
400                    .to_string(),
401            )),
402            _ => Err(self.error(format!(
403                "Expected expression (identifier, literal, '[', or '{{'), found {}",
404                self.current().kind
405            ))),
406        }
407    }
408}