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