Skip to main content

harn_parser/parser/
precedence.rs

1use crate::ast::SNode;
2use harn_lexer::{Span, TokenKind};
3
4use super::error::ParserError;
5use super::expression_nodes::{self as nodes, Expression};
6use super::state::Parser;
7
8impl Parser {
9    /// Parse a single expression for a string interpolation hole.
10    pub fn parse_single_expression(&mut self) -> Result<SNode, ParserError> {
11        self.check_token_nesting_limit()?;
12        self.skip_newlines();
13        let expr = self.parse_expression()?;
14        self.skip_newlines();
15        if !self.is_at_end() {
16            return Err(self.error("end of interpolated expression"));
17        }
18        Ok(expr)
19    }
20
21    pub(super) fn parse_nested_expression(
22        &mut self,
23        context: &'static str,
24    ) -> Result<SNode, ParserError> {
25        self.with_nesting(context, |parser| parser.parse_expression())
26    }
27
28    pub(super) fn parse_expression(&mut self) -> Result<SNode, ParserError> {
29        self.skip_newlines();
30        self.parse_pipe().map(|expr| *expr)
31    }
32
33    fn parse_pipe(&mut self) -> Result<Expression, ParserError> {
34        self.parse_binary_level(Self::parse_range, &[(TokenKind::Pipe, "|>")])
35    }
36
37    fn parse_range(&mut self) -> Result<Expression, ParserError> {
38        let left = self.parse_ternary()?;
39        if !self.check(&TokenKind::To) {
40            return Ok(left);
41        }
42        let start = left.span;
43        self.advance();
44        let right = self.parse_ternary()?;
45        let inclusive = if self.check(&TokenKind::Exclusive) {
46            self.advance();
47            false
48        } else {
49            true
50        };
51        Ok(nodes::range(
52            left,
53            right,
54            inclusive,
55            Span::merge(start, self.prev_span()),
56        ))
57    }
58
59    fn parse_ternary(&mut self) -> Result<Expression, ParserError> {
60        let condition = self.parse_logical_or()?;
61        // Postfix try has already been consumed. A remaining `?`, including
62        // one across a newline, starts a ternary.
63        if !self.check_skip_newlines(&TokenKind::Question) {
64            return Ok(condition);
65        }
66        let start = condition.span;
67        self.advance();
68        self.skip_newlines();
69        let true_expr = self.with_nesting("ternary expression", |parser| parser.parse_ternary())?;
70        self.consume(&TokenKind::Colon, ":")?;
71        self.skip_newlines();
72        let false_expr =
73            self.with_nesting("ternary expression", |parser| parser.parse_ternary())?;
74        Ok(nodes::ternary(
75            condition,
76            true_expr,
77            false_expr,
78            Span::merge(start, self.prev_span()),
79        ))
80    }
81
82    fn parse_logical_or(&mut self) -> Result<Expression, ParserError> {
83        self.parse_binary_level(Self::parse_logical_and, &[(TokenKind::Or, "||")])
84    }
85
86    fn parse_logical_and(&mut self) -> Result<Expression, ParserError> {
87        self.parse_binary_level(Self::parse_equality, &[(TokenKind::And, "&&")])
88    }
89
90    fn parse_equality(&mut self) -> Result<Expression, ParserError> {
91        self.parse_binary_level(
92            Self::parse_comparison,
93            &[(TokenKind::Eq, "=="), (TokenKind::Neq, "!=")],
94        )
95    }
96
97    fn parse_comparison(&mut self) -> Result<Expression, ParserError> {
98        let mut left = self.parse_additive()?;
99        loop {
100            let op = if self.check_skip_newlines(&TokenKind::Lt) {
101                "<"
102            } else if self.check_skip_newlines(&TokenKind::Gt) {
103                ">"
104            } else if self.check_skip_newlines(&TokenKind::Lte) {
105                "<="
106            } else if self.check_skip_newlines(&TokenKind::Gte) {
107                ">="
108            } else if self.check(&TokenKind::In) {
109                "in"
110            } else if self.check_identifier("not") {
111                let saved = self.pos;
112                self.advance();
113                if !self.check(&TokenKind::In) {
114                    self.pos = saved;
115                    break;
116                }
117                "not_in"
118            } else {
119                break;
120            };
121            let start = left.span;
122            self.advance();
123            self.skip_newlines();
124            let right = self.parse_additive()?;
125            left = nodes::binary(op, left, right, Span::merge(start, self.prev_span()));
126        }
127        Ok(left)
128    }
129
130    fn parse_additive(&mut self) -> Result<Expression, ParserError> {
131        let mut left = self.parse_nil_coalescing()?;
132        // A leading minus on a new line starts a separate unary expression.
133        while self.check_skip_newlines(&TokenKind::Plus) || self.check(&TokenKind::Minus) {
134            let op = if self.check(&TokenKind::Plus) {
135                "+"
136            } else {
137                "-"
138            };
139            let start = left.span;
140            self.advance();
141            self.skip_newlines();
142            let right = self.parse_nil_coalescing()?;
143            left = nodes::binary(op, left, right, Span::merge(start, self.prev_span()));
144        }
145        Ok(left)
146    }
147
148    // `??` binds tighter than addition/comparison and looser than multiplication.
149    fn parse_nil_coalescing(&mut self) -> Result<Expression, ParserError> {
150        self.parse_binary_level(Self::parse_multiplicative, &[(TokenKind::NilCoal, "??")])
151    }
152
153    fn parse_multiplicative(&mut self) -> Result<Expression, ParserError> {
154        self.parse_binary_level(
155            Self::parse_unary_expression,
156            &[
157                (TokenKind::Star, "*"),
158                (TokenKind::Slash, "/"),
159                (TokenKind::Percent, "%"),
160            ],
161        )
162    }
163
164    fn parse_binary_level(
165        &mut self,
166        operand: fn(&mut Self) -> Result<Expression, ParserError>,
167        operators: &[(TokenKind, &str)],
168    ) -> Result<Expression, ParserError> {
169        let mut left = operand(self)?;
170        while let Some((_, op)) = operators
171            .iter()
172            .find(|(kind, _)| self.check_skip_newlines(kind))
173        {
174            let start = left.span;
175            self.advance();
176            self.skip_newlines();
177            let right = operand(self)?;
178            left = nodes::binary(op, left, right, Span::merge(start, self.prev_span()));
179        }
180        Ok(left)
181    }
182
183    // The base binds tighter than a unary prefix (`-2 ** 2` is `-(2 ** 2)`).
184    // The exponent accepts unary prefixes and remains right-associative.
185    fn parse_exponent(&mut self) -> Result<Expression, ParserError> {
186        let left = self.parse_postfix()?;
187        if !self.check_skip_newlines(&TokenKind::Pow) {
188            return Ok(left);
189        }
190        let start = left.span;
191        self.advance();
192        self.skip_newlines();
193        let right = self.with_nesting("exponent expression", |parser| {
194            parser.parse_unary_expression()
195        })?;
196        Ok(nodes::binary(
197            "**",
198            left,
199            right,
200            Span::merge(start, self.prev_span()),
201        ))
202    }
203
204    pub(super) fn parse_unary(&mut self) -> Result<SNode, ParserError> {
205        self.parse_unary_expression().map(|expr| *expr)
206    }
207
208    fn parse_unary_expression(&mut self) -> Result<Expression, ParserError> {
209        let op = if self.check(&TokenKind::Not) {
210            "!"
211        } else if self.check(&TokenKind::Minus) {
212            "-"
213        } else {
214            return self.parse_exponent();
215        };
216        let start = self.current_span();
217        self.advance();
218        let operand =
219            self.with_nesting("unary expression", |parser| parser.parse_unary_expression())?;
220        Ok(nodes::unary(
221            op,
222            operand,
223            Span::merge(start, self.prev_span()),
224        ))
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231    use crate::ast::Node;
232    use harn_lexer::Lexer;
233
234    #[test]
235    fn additive_newlines_preserve_statement_boundaries() {
236        let tokens = Lexer::new("1\n-2").tokenize().unwrap();
237        let program = Parser::new(tokens).parse().unwrap();
238        assert_eq!(program.len(), 2);
239        assert!(matches!(program[0].node, Node::IntLiteral(1)));
240        assert!(matches!(&program[1].node, Node::UnaryOp { op, operand }
241            if op == "-" && matches!(operand.node, Node::IntLiteral(2))));
242
243        for (source, expected_op) in [("1\n+2", "+"), ("1-\n2", "-")] {
244            let tokens = Lexer::new(source).tokenize().unwrap();
245            let expression = Parser::new(tokens).parse_single_expression().unwrap();
246            assert!(
247                matches!(&expression.node, Node::BinaryOp { op, left, right }
248                if op == expected_op
249                    && matches!(left.node, Node::IntLiteral(1))
250                    && matches!(right.node, Node::IntLiteral(2)))
251            );
252        }
253    }
254}