Skip to main content

rl_lang/parser/expressions/
postfix.rs

1use crate::{
2    ast::{ExprId, nodes::ExpressionKind},
3    lexer::tokentypes::TokenType,
4    parser::parser_logic::Parser,
5    utils::{errors::Error, span::Span},
6};
7
8impl Parser {
9    /// Parses zero or more postfix method-call chains on `expr`.
10    ///
11    /// After any primary or sub-expression is built, this function consumes
12    /// repeated `.method(args)` suffixes, producing a left-associative chain of
13    /// [`ExpressionKind::MethodCall`] nodes. Method names may themselves be
14    /// namespaced (`obj.module::method()`).
15    ///
16    /// Returns `expr` unchanged when the next token is not `.`.
17    ///
18    /// # Errors
19    /// Returns an error if `.` is not followed by a valid identifier, or if the
20    /// argument list is not opened with `(`.
21    pub fn parse_postfix(&mut self, mut expr: ExprId, start: Span) -> Result<ExprId, Error> {
22        loop {
23            // --- method call / field access ---
24            if self.match_type(&[TokenType::Dot]) {
25                if !self.match_type(&[TokenType::Identifier(String::new())]) {
26                    return Err(self.err("expected name after '.'", self.peek_span()));
27                }
28                let first = if let TokenType::Identifier(name) = self.previous() {
29                    name
30                } else {
31                    return Err(self.err("expected name after '.'", self.peek_span()));
32                };
33
34                // a bare `.name` not followed by `::` or `(` is a field access,
35                // not a method call.
36                if self.peek() != TokenType::ColonColon && self.peek() != TokenType::LeftParen {
37                    let span = start.join(self.previous_span());
38                    #[cfg(feature = "debug")]
39                    log::trace!(
40                        "alloc FieldAccess expr: target={:?} field={:?} @ {:?}",
41                        expr,
42                        first,
43                        span
44                    );
45                    expr = self.ast_arena.alloc_expr(
46                        ExpressionKind::FieldAccess {
47                            target: expr,
48                            field: first,
49                        },
50                        span,
51                    );
52
53                    // --- field assignment: target.field = value ---
54                    if self.match_type(&[TokenType::Assign]) {
55                        let value = self.parse_expression()?;
56                        let value_id = self.ast_arena.exprs.get(value);
57                        let assign_span = start.join(value_id.span);
58                        let expr_kind = self.ast_arena.exprs.get(expr).kind.clone();
59                        if let ExpressionKind::FieldAccess { target, field } = expr_kind {
60                            #[cfg(feature = "debug")]
61                            log::trace!(
62                                "alloc FieldAssign expr: target={:?} field={:?} value={:?} @ {:?}",
63                                target,
64                                field,
65                                value,
66                                assign_span
67                            );
68                            return Ok(self.ast_arena.alloc_expr(
69                                ExpressionKind::FieldAssign {
70                                    target,
71                                    field,
72                                    value,
73                                },
74                                assign_span,
75                            ));
76                        }
77                    }
78
79                    continue;
80                }
81
82                let mut method = vec![first];
83                while self.match_type(&[TokenType::ColonColon]) {
84                    if !self.match_type(&[TokenType::Identifier(String::new())]) {
85                        return Err(
86                            self.err("expected identifier name after '::'", self.peek_span())
87                        );
88                    }
89                    if let TokenType::Identifier(seg) = self.previous() {
90                        method.push(seg);
91                    }
92                }
93
94                if !self.match_type(&[TokenType::LeftParen]) {
95                    return Err(self.err("expected '(' after method name", self.peek_span()));
96                }
97                while self.match_type(&[TokenType::Newline]) {}
98                let mut args = Vec::new();
99                if self.peek() != TokenType::RightParen {
100                    loop {
101                        args.push(self.parse_expression()?);
102                        while self.match_type(&[TokenType::Newline]) {}
103                        if !self.match_type(&[TokenType::Comma]) {
104                            break;
105                        }
106                        while self.match_type(&[TokenType::Newline]) {}
107                    }
108                }
109                while self.match_type(&[TokenType::Newline]) {}
110                self.match_type(&[TokenType::RightParen]);
111                let span = start.join(self.previous_span());
112
113                #[cfg(feature = "debug")]
114                log::trace!(
115                    "alloc MethodCall expr: caller={:?} method={:?} args={} @ {:?}",
116                    expr,
117                    method,
118                    args.len(),
119                    span
120                );
121
122                expr = self.ast_arena.alloc_expr(
123                    ExpressionKind::MethodCall {
124                        caller: expr,
125                        method,
126                        args,
127                    },
128                    span,
129                );
130            }
131            // --- cast ---
132            else if self.match_type(&[TokenType::As]) {
133                let span = self.previous_span();
134                let target_type = self
135                    .parse_type(true)
136                    .map_err(|_| self.err("expected type after `as`", span))?;
137                let span = start.join(self.previous_span());
138
139                #[cfg(feature = "debug")]
140                log::trace!(
141                    "alloc Cast expr: value={:?} target_type={:?} @ {:?}",
142                    expr,
143                    target_type,
144                    span
145                );
146
147                expr = self.ast_arena.alloc_expr(
148                    ExpressionKind::Cast {
149                        value: expr,
150                        target_type,
151                    },
152                    span,
153                )
154            }
155            // --- propagate ---
156            else if self.match_type(&[TokenType::Question]) {
157                let span = start.join(self.previous_span());
158
159                #[cfg(feature = "debug")]
160                log::trace!("alloc Propagate expr: inner={:?} @ {:?}", expr, span);
161
162                expr = self
163                    .ast_arena
164                    .alloc_expr(ExpressionKind::Propagate(expr), span)
165            } else {
166                break;
167            }
168        }
169        Ok(expr)
170    }
171}