Skip to main content

harn_parser/parser/
precedence.rs

1use crate::ast::SNode;
2use crate::operators::{Associativity, BinaryOperator, Precedence, UnaryOperator};
3use harn_lexer::{Span, TokenKind};
4
5use super::error::ParserError;
6use super::expression_nodes::{self as nodes, Expression};
7use super::state::Parser;
8
9enum RangeSpelling {
10    /// `a..b`
11    Exclusive,
12    /// `a..=b`
13    Inclusive,
14    /// `a to b [exclusive]`, accepted only by [`crate::operators::OperatorGrammar::LEGACY`].
15    LegacyTo,
16}
17
18/// The token that spells a single-token binary operator.
19fn binary_operator_token(op: BinaryOperator) -> TokenKind {
20    match op {
21        BinaryOperator::Pipe => TokenKind::Pipe,
22        BinaryOperator::Or => TokenKind::Or,
23        BinaryOperator::And => TokenKind::And,
24        BinaryOperator::Eq => TokenKind::Eq,
25        BinaryOperator::Neq => TokenKind::Neq,
26        BinaryOperator::Lt => TokenKind::Lt,
27        BinaryOperator::Gt => TokenKind::Gt,
28        BinaryOperator::Lte => TokenKind::Lte,
29        BinaryOperator::Gte => TokenKind::Gte,
30        BinaryOperator::In | BinaryOperator::NotIn => TokenKind::In,
31        BinaryOperator::NilCoalesce => TokenKind::NilCoal,
32        BinaryOperator::Add => TokenKind::Plus,
33        BinaryOperator::Sub => TokenKind::Minus,
34        BinaryOperator::Mul => TokenKind::Star,
35        BinaryOperator::Div => TokenKind::Slash,
36        BinaryOperator::Mod => TokenKind::Percent,
37        BinaryOperator::Pow => TokenKind::Pow,
38    }
39}
40
41impl Parser {
42    /// Parse a single expression for a string interpolation hole.
43    pub fn parse_single_expression(&mut self) -> Result<SNode, ParserError> {
44        self.check_token_nesting_limit()?;
45        self.skip_newlines();
46        let expr = self.parse_expression()?;
47        self.skip_newlines();
48        if !self.is_at_end() {
49            return Err(self.error("end of interpolated expression"));
50        }
51        Ok(expr)
52    }
53
54    pub(super) fn parse_nested_expression(
55        &mut self,
56        context: &'static str,
57    ) -> Result<SNode, ParserError> {
58        self.with_nesting(context, |parser| parser.parse_expression())
59    }
60
61    pub(super) fn parse_expression(&mut self) -> Result<SNode, ParserError> {
62        self.skip_newlines();
63        self.parse_precedence(self.grammar.loosest())
64            .map(|expr| *expr)
65    }
66
67    /// Parse an expression whose loosest operator belongs to `group` or a
68    /// tighter group on the active [`crate::operators::OperatorGrammar`] ladder.
69    fn parse_precedence(&mut self, group: Precedence) -> Result<Expression, ParserError> {
70        match group {
71            Precedence::Ternary => self.parse_ternary(),
72            Precedence::Range => self.parse_range(),
73            Precedence::Prefix => self.parse_unary_expression(),
74            Precedence::Exponent => self.parse_exponent(),
75            Precedence::Postfix => self.parse_postfix(),
76            binary => self.parse_binary_group(binary),
77        }
78    }
79
80    /// A left-associative group of binary operators.
81    fn parse_binary_group(&mut self, group: Precedence) -> Result<Expression, ParserError> {
82        debug_assert_eq!(group.associativity(), Associativity::Left);
83        let operand = self.grammar.tighter(group);
84        let mut left = self.parse_precedence(operand)?;
85        while let Some(op) = self.check_binary_operator(group) {
86            let start = left.span;
87            self.advance();
88            if op == BinaryOperator::NotIn {
89                self.advance();
90            }
91            self.skip_newlines();
92            let right = self.parse_precedence(operand)?;
93            left = nodes::binary(op, left, right, Span::merge(start, self.prev_span()));
94        }
95        Ok(left)
96    }
97
98    /// The operator of `group` at the current position, if any. Operators that
99    /// continue across a line break consume the newlines before them.
100    fn check_binary_operator(&mut self, group: Precedence) -> Option<BinaryOperator> {
101        for op in BinaryOperator::ALL {
102            if op.precedence() != group {
103                continue;
104            }
105            let found = match op {
106                BinaryOperator::In => self.check(&TokenKind::In),
107                BinaryOperator::NotIn => {
108                    self.check_identifier("not") && matches!(self.peek_kind(), Some(TokenKind::In))
109                }
110                _ => {
111                    let kind = binary_operator_token(op);
112                    if op.continues_across_newline() {
113                        self.check_skip_newlines(&kind)
114                    } else {
115                        self.check(&kind)
116                    }
117                }
118            };
119            if found {
120                return Some(op);
121            }
122        }
123        None
124    }
125
126    /// Range formation does not associate: `a..b..c` needs parentheses.
127    fn parse_range(&mut self) -> Result<Expression, ParserError> {
128        let operand = self.grammar.tighter(Precedence::Range);
129        let left = self.parse_precedence(operand)?;
130        let Some(spelling) = self.check_range_operator()? else {
131            return Ok(left);
132        };
133        let start = left.span;
134        self.advance();
135        self.skip_newlines();
136        let right = self.parse_precedence(operand)?;
137        let inclusive = match spelling {
138            RangeSpelling::Exclusive => false,
139            RangeSpelling::Inclusive => true,
140            RangeSpelling::LegacyTo => {
141                let exclusive = self.check_identifier("exclusive");
142                if exclusive {
143                    self.advance();
144                }
145                !exclusive
146            }
147        };
148        if self.check(&TokenKind::DotDot) || self.check(&TokenKind::DotDotEq) {
149            return Err(self.error("end of range; parenthesize a range used as a range bound"));
150        }
151        Ok(nodes::range(
152            left,
153            right,
154            inclusive,
155            Span::merge(start, self.prev_span()),
156        ))
157    }
158
159    /// The range operator at the current position, if any. Under the
160    /// current grammar the retired `a to b` spelling is a targeted error.
161    fn check_range_operator(&mut self) -> Result<Option<RangeSpelling>, ParserError> {
162        if self.check_skip_newlines(&TokenKind::DotDot) {
163            return Ok(Some(RangeSpelling::Exclusive));
164        }
165        if self.check_skip_newlines(&TokenKind::DotDotEq) {
166            return Ok(Some(RangeSpelling::Inclusive));
167        }
168        if self.check_identifier("to") {
169            if self.grammar.legacy_ranges() {
170                return Ok(Some(RangeSpelling::LegacyTo));
171            }
172            return Err(ParserError::RemovedRangeSyntax {
173                span: self.current_span(),
174            });
175        }
176        Ok(None)
177    }
178
179    fn parse_ternary(&mut self) -> Result<Expression, ParserError> {
180        let condition = self.parse_precedence(self.grammar.tighter(Precedence::Ternary))?;
181        // Postfix try has already been consumed. A remaining `?`, including
182        // one across a newline, starts a ternary.
183        if !self.check_skip_newlines(&TokenKind::Question) {
184            return Ok(condition);
185        }
186        let start = condition.span;
187        self.advance();
188        self.skip_newlines();
189        let true_expr = self.with_nesting("ternary expression", |parser| parser.parse_ternary())?;
190        self.consume(&TokenKind::Colon, ":")?;
191        self.skip_newlines();
192        let false_expr =
193            self.with_nesting("ternary expression", |parser| parser.parse_ternary())?;
194        Ok(nodes::ternary(
195            condition,
196            true_expr,
197            false_expr,
198            Span::merge(start, self.prev_span()),
199        ))
200    }
201
202    // The base binds tighter than a unary prefix (`-2 ** 2` is `-(2 ** 2)`).
203    // The exponent accepts unary prefixes and remains right-associative.
204    fn parse_exponent(&mut self) -> Result<Expression, ParserError> {
205        let left = self.parse_postfix()?;
206        if !self.check_skip_newlines(&TokenKind::Pow) {
207            return Ok(left);
208        }
209        let start = left.span;
210        self.advance();
211        self.skip_newlines();
212        let right = self.with_nesting("exponent expression", |parser| {
213            parser.parse_unary_expression()
214        })?;
215        Ok(nodes::binary(
216            BinaryOperator::Pow,
217            left,
218            right,
219            Span::merge(start, self.prev_span()),
220        ))
221    }
222
223    pub(super) fn parse_unary(&mut self) -> Result<SNode, ParserError> {
224        self.parse_unary_expression().map(|expr| *expr)
225    }
226
227    fn parse_unary_expression(&mut self) -> Result<Expression, ParserError> {
228        let op = if self.check(&TokenKind::Not) {
229            UnaryOperator::Not
230        } else if self.check(&TokenKind::Minus) {
231            UnaryOperator::Neg
232        } else {
233            return self.parse_exponent();
234        };
235        let start = self.current_span();
236        self.advance();
237        let operand =
238            self.with_nesting("unary expression", |parser| parser.parse_unary_expression())?;
239        Ok(nodes::unary(
240            op,
241            operand,
242            Span::merge(start, self.prev_span()),
243        ))
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250    use crate::ast::Node;
251    use harn_lexer::Lexer;
252
253    #[test]
254    fn additive_newlines_preserve_statement_boundaries() {
255        let tokens = Lexer::new("1\n-2").tokenize().unwrap();
256        let program = Parser::new(tokens).parse().unwrap();
257        assert_eq!(program.len(), 2);
258        assert!(matches!(program[0].node, Node::IntLiteral(1)));
259        assert!(matches!(&program[1].node, Node::UnaryOp { op, operand }
260            if *op == UnaryOperator::Neg && matches!(operand.node, Node::IntLiteral(2))));
261
262        for (source, expected_op) in [
263            ("1\n+2", BinaryOperator::Add),
264            ("1-\n2", BinaryOperator::Sub),
265        ] {
266            let tokens = Lexer::new(source).tokenize().unwrap();
267            let expression = Parser::new(tokens).parse_single_expression().unwrap();
268            assert!(
269                matches!(&expression.node, Node::BinaryOp { op, left, right }
270                if *op == expected_op
271                    && matches!(left.node, Node::IntLiteral(1))
272                    && matches!(right.node, Node::IntLiteral(2)))
273            );
274        }
275    }
276}