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                        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            // Lookahead: is this `Name(field = value, ...)` (record creation)?
193            // Detect by checking if token after `(` is `Ident` followed by `=`.
194            // Use peek_skip_formatting to handle multiline constructor syntax.
195            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(); // consume (
207                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    /// Parse named-field arguments for record creation: `name = expr, name2 = expr2`
235    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(); // =>
297            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                        // Parse the interpolation expression; empty `{}` → empty literal.
332                        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(); // consume [
382                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}