1use std::str::FromStr;
4
5use super::{ParseResult, Parser};
6use crate::ast::{Arg, BinaryOp, Expr, ExprKind, StringPart, UnaryOp};
7use crate::lexer::unescape;
8use crate::span::Span;
9use crate::token::TokenKind;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub(super) enum TrailingBlocks {
15 Allowed,
16 Forbidden,
17}
18
19fn binding_power(op: BinaryOp) -> u8 {
21 match op {
22 BinaryOp::Or => 1,
23 BinaryOp::And => 2,
24 BinaryOp::Eq
25 | BinaryOp::NotEq
26 | BinaryOp::Lt
27 | BinaryOp::LtEq
28 | BinaryOp::Gt
29 | BinaryOp::GtEq => 3,
30 BinaryOp::Add | BinaryOp::Sub => 4,
31 BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => 5,
32 }
33}
34
35fn binary_op(kind: TokenKind) -> Option<BinaryOp> {
36 Some(match kind {
37 TokenKind::OrOr => BinaryOp::Or,
38 TokenKind::AndAnd => BinaryOp::And,
39 TokenKind::EqEq => BinaryOp::Eq,
40 TokenKind::BangEq => BinaryOp::NotEq,
41 TokenKind::Lt => BinaryOp::Lt,
42 TokenKind::LtEq => BinaryOp::LtEq,
43 TokenKind::Gt => BinaryOp::Gt,
44 TokenKind::GtEq => BinaryOp::GtEq,
45 TokenKind::Plus => BinaryOp::Add,
46 TokenKind::Minus => BinaryOp::Sub,
47 TokenKind::Star => BinaryOp::Mul,
48 TokenKind::Slash => BinaryOp::Div,
49 TokenKind::Percent => BinaryOp::Rem,
50 _ => return None,
51 })
52}
53
54impl Parser<'_> {
55 pub(super) fn parse_expr(&mut self, trailing: TrailingBlocks) -> ParseResult<Expr> {
56 self.parse_binary(0, trailing)
57 }
58
59 fn parse_binary(&mut self, min_power: u8, trailing: TrailingBlocks) -> ParseResult<Expr> {
60 let mut lhs = self.parse_prefix(trailing)?;
61 while let Some(op) = binary_op(self.peek()) {
62 let power = binding_power(op);
63 if power <= min_power {
64 break;
65 }
66 self.bump();
67 self.skip_newlines();
68 let rhs = self.parse_binary(power, trailing)?;
69 if op.is_comparison() && binary_op(self.peek()).is_some_and(BinaryOp::is_comparison) {
70 let chained = self.current().span;
71 self.report(
72 "comparisons can't be chained; combine them with `&&`",
73 chained,
74 );
75 }
76 lhs = Expr {
77 span: lhs.span.to(rhs.span),
78 kind: ExprKind::Binary {
79 op,
80 lhs: Box::new(lhs),
81 rhs: Box::new(rhs),
82 },
83 };
84 }
85 Ok(lhs)
86 }
87
88 fn parse_prefix(&mut self, trailing: TrailingBlocks) -> ParseResult<Expr> {
89 let op = match self.peek() {
90 TokenKind::Bang => UnaryOp::Not,
91 TokenKind::Minus => UnaryOp::Neg,
92 TokenKind::Dollar => UnaryOp::Bind,
93 _ => return self.parse_postfix(trailing),
94 };
95 let start = self.bump().span;
96 let operand = self.parse_prefix(trailing)?;
97 Ok(Expr {
98 span: start.to(operand.span),
99 kind: ExprKind::Unary {
100 op,
101 operand: Box::new(operand),
102 },
103 })
104 }
105
106 fn parse_postfix(&mut self, trailing: TrailingBlocks) -> ParseResult<Expr> {
107 let mut expr = self.parse_primary()?;
108 loop {
109 expr = match self.peek() {
110 TokenKind::LParen => self.parse_call(expr, trailing)?,
111 TokenKind::Dot => {
112 self.bump();
113 let name = self.expect_ident()?;
114 Expr {
115 span: expr.span.to(name.span),
116 kind: ExprKind::Field {
117 base: Box::new(expr),
118 name,
119 },
120 }
121 }
122 TokenKind::LBracket => {
123 self.bump();
124 self.skip_newlines();
125 let index = self.parse_expr(TrailingBlocks::Allowed)?;
126 self.skip_newlines();
127 let close = self.expect(TokenKind::RBracket)?;
128 Expr {
129 span: expr.span.to(close.span),
130 kind: ExprKind::Index {
131 base: Box::new(expr),
132 index: Box::new(index),
133 },
134 }
135 }
136 _ => return Ok(expr),
137 };
138 }
139 }
140
141 pub(super) fn parse_call(
142 &mut self,
143 callee: Expr,
144 trailing: TrailingBlocks,
145 ) -> ParseResult<Expr> {
146 self.bump();
147 let args = self.comma_list(TokenKind::RParen, Self::parse_arg)?;
148 self.check_argument_order(&args);
149 let trailing_block = if trailing == TrailingBlocks::Allowed && self.at(TokenKind::LBrace) {
150 Some(self.parse_block()?)
151 } else {
152 None
153 };
154 Ok(Expr {
155 span: self.span_from(callee.span.start),
156 kind: ExprKind::Call {
157 callee: Box::new(callee),
158 args,
159 trailing_block,
160 },
161 })
162 }
163
164 fn parse_arg(&mut self) -> ParseResult<Arg> {
165 let label = if self.at(TokenKind::Ident) && self.peek_nth(1) == TokenKind::Colon {
166 let label = self.expect_ident()?;
167 self.bump();
168 self.skip_newlines();
169 Some(label)
170 } else {
171 None
172 };
173 let value = self.parse_expr(TrailingBlocks::Allowed)?;
174 Ok(Arg { label, value })
175 }
176
177 fn check_argument_order(&mut self, args: &[Arg]) {
178 let mut named_seen = false;
179 for arg in args {
180 match arg.label {
181 Some(_) => named_seen = true,
182 None if named_seen => {
183 self.report(
184 "positional arguments must come before named ones",
185 arg.value.span,
186 );
187 }
188 None => {}
189 }
190 }
191 }
192
193 fn parse_primary(&mut self) -> ParseResult<Expr> {
194 let token = self.current();
195 let kind = match token.kind {
196 TokenKind::Int => {
197 self.bump();
198 self.number_literal(
199 token.span,
200 ExprKind::Int,
201 "this number is too large for an `int`",
202 )
203 }
204 TokenKind::Float => {
205 self.bump();
206 self.number_literal(token.span, ExprKind::Float, "this is not a valid `float`")
207 }
208 TokenKind::True | TokenKind::False => {
209 self.bump();
210 ExprKind::Bool(token.kind == TokenKind::True)
211 }
212 TokenKind::Ident => {
213 self.bump();
214 ExprKind::Name(self.text(token.span).to_owned())
215 }
216 TokenKind::StringStart => return self.parse_string(),
217 TokenKind::Dot => {
218 self.bump();
219 ExprKind::ImplicitMember(self.expect_ident()?)
220 }
221 TokenKind::LBracket => {
222 self.bump();
223 let items = self.comma_list(TokenKind::RBracket, |parser| {
224 parser.parse_expr(TrailingBlocks::Allowed)
225 })?;
226 ExprKind::List(items)
227 }
228 TokenKind::LParen => {
229 self.bump();
230 self.skip_newlines();
231 let inner = self.parse_expr(TrailingBlocks::Allowed)?;
232 self.skip_newlines();
233 self.expect(TokenKind::RParen)?;
234 return Ok(inner);
235 }
236 _ => return self.unexpected("an expression"),
237 };
238 Ok(Expr {
239 kind,
240 span: self.span_from(token.span.start),
241 })
242 }
243
244 fn parse_string(&mut self) -> ParseResult<Expr> {
245 let start = self.bump().span.start;
246 let mut parts = Vec::new();
247 loop {
248 match self.peek() {
249 TokenKind::StringText => {
250 let text = self.bump();
251 parts.push(StringPart::Text(unescape(self.text(text.span))));
252 }
253 TokenKind::InterpolationStart => {
254 self.bump();
255 self.skip_newlines();
256 parts.push(StringPart::Expr(self.parse_expr(TrailingBlocks::Allowed)?));
257 self.skip_newlines();
258 self.expect(TokenKind::InterpolationEnd)?;
259 }
260 TokenKind::StringEnd => {
261 self.bump();
262 return Ok(Expr {
263 kind: ExprKind::String(parts),
264 span: self.span_from(start),
265 });
266 }
267 _ => return self.unexpected("the end of the string"),
268 }
269 }
270 }
271
272 fn number_literal<T: FromStr>(
274 &mut self,
275 span: Span,
276 wrap: fn(T) -> ExprKind,
277 problem: &str,
278 ) -> ExprKind {
279 if let Ok(value) = self.text(span).replace('_', "").parse() {
280 wrap(value)
281 } else {
282 self.report(problem, span);
283 ExprKind::Error
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::binary_op;
291 use crate::token::SYMBOLS;
292
293 #[test]
294 fn binary_operators_round_trip_through_their_tokens() {
295 for &kind in SYMBOLS {
296 if let Some(op) = binary_op(kind) {
297 assert_eq!(op.token(), kind);
298 }
299 }
300 }
301}