1use crate::ast::Spanned;
18use crate::parser::{ParseError, Parser};
19use crate::template::{self, StringTemplate};
20use crate::token::TokenKind;
21
22#[derive(Debug, Clone, PartialEq)]
25pub enum Expr {
26 Str(StringTemplate),
27 Bool(bool),
28 Var(Spanned<String>),
29 Call {
30 name: Spanned<String>,
31 args: Vec<Expr>,
32 },
33 Binary {
34 op: BinOp,
35 lhs: Box<Expr>,
36 rhs: Box<Expr>,
37 },
38 If {
39 cond: Box<Expr>,
40 then: Box<Expr>,
41 otherwise: Box<Expr>,
42 },
43 Field {
46 object: Spanned<String>,
47 field: Spanned<String>,
48 },
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum BinOp {
54 Eq,
55 NotEq,
56 And,
57 Or,
58 Concat,
60}
61
62impl<'a> Parser<'a> {
63 pub(crate) fn parse_expr(&mut self) -> Result<Expr, ParseError> {
66 self.parse_or()
67 }
68
69 fn parse_or(&mut self) -> Result<Expr, ParseError> {
71 let mut lhs = self.parse_and()?;
72 while self.check(&TokenKind::OrOr) {
73 self.advance();
74 let rhs = self.parse_and()?;
75 lhs = Expr::Binary {
76 op: BinOp::Or,
77 lhs: Box::new(lhs),
78 rhs: Box::new(rhs),
79 };
80 }
81 Ok(lhs)
82 }
83
84 fn parse_and(&mut self) -> Result<Expr, ParseError> {
86 let mut lhs = self.parse_equality()?;
87 while self.check(&TokenKind::AndAnd) {
88 self.advance();
89 let rhs = self.parse_equality()?;
90 lhs = Expr::Binary {
91 op: BinOp::And,
92 lhs: Box::new(lhs),
93 rhs: Box::new(rhs),
94 };
95 }
96 Ok(lhs)
97 }
98
99 fn parse_equality(&mut self) -> Result<Expr, ParseError> {
101 let mut lhs = self.parse_additive()?;
102 loop {
103 let op = if self.check(&TokenKind::EqEq) {
104 BinOp::Eq
105 } else if self.check(&TokenKind::NotEq) {
106 BinOp::NotEq
107 } else {
108 break;
109 };
110 self.advance();
111 let rhs = self.parse_additive()?;
112 lhs = Expr::Binary {
113 op,
114 lhs: Box::new(lhs),
115 rhs: Box::new(rhs),
116 };
117 }
118 Ok(lhs)
119 }
120
121 fn parse_additive(&mut self) -> Result<Expr, ParseError> {
124 let mut lhs = self.parse_atom()?;
125 while self.check(&TokenKind::Plus) {
126 self.advance();
127 let rhs = self.parse_atom()?;
128 lhs = Expr::Binary {
129 op: BinOp::Concat,
130 lhs: Box::new(lhs),
131 rhs: Box::new(rhs),
132 };
133 }
134 Ok(lhs)
135 }
136
137 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
140 match self.peek().kind.clone() {
141 TokenKind::KwIf => self.parse_if(),
142 TokenKind::Str(_) => {
143 let tok = self.advance();
144 let template = template::parse_template_at(self.source(), tok.span)?;
145 Ok(Expr::Str(template))
146 }
147 TokenKind::KwTrue => {
148 self.advance();
149 Ok(Expr::Bool(true))
150 }
151 TokenKind::KwFalse => {
152 self.advance();
153 Ok(Expr::Bool(false))
154 }
155 TokenKind::Ident(name) => {
156 let name_span = self.advance().span;
157 if self.check(&TokenKind::Dot) {
158 self.advance();
159 let field = self.eat_ident()?;
160 return Ok(Expr::Field {
161 object: Spanned::new(name, name_span),
162 field,
163 });
164 }
165 if self.check(&TokenKind::LParen) {
166 self.advance();
167 let mut args = Vec::new();
168 if !self.check(&TokenKind::RParen) {
169 loop {
170 args.push(self.parse_expr()?);
171 if self.check(&TokenKind::Comma) {
172 self.advance();
173 } else {
174 break;
175 }
176 }
177 }
178 self.expect(TokenKind::RParen, "`)`")?;
179 Ok(Expr::Call {
180 name: Spanned::new(name, name_span),
181 args,
182 })
183 } else {
184 Ok(Expr::Var(Spanned::new(name, name_span)))
185 }
186 }
187 _ => Err(self.unexpected("an expression")),
188 }
189 }
190
191 fn parse_if(&mut self) -> Result<Expr, ParseError> {
194 self.expect(TokenKind::KwIf, "`if`")?;
195 let cond = self.parse_expr()?;
196 self.expect(TokenKind::KwThen, "`then`")?;
197 let then = self.parse_expr()?;
198 self.expect(TokenKind::KwElse, "`else`")?;
199 let otherwise = self.parse_expr()?;
200 Ok(Expr::If {
201 cond: Box::new(cond),
202 then: Box::new(then),
203 otherwise: Box::new(otherwise),
204 })
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::parser::tokenize;
212
213 fn parse_expr_str(source: &str) -> Expr {
216 let tokens = tokenize(source).expect("tokenize failed");
217 let mut parser = Parser::new(tokens, source);
218 let expr = parser.parse_expr().expect("parse failed");
219 assert!(parser.is_eof(), "trailing tokens after expression");
220 expr
221 }
222
223 #[test]
224 fn parses_if_then_else_and_precedence() {
225 let e = parse_expr_str(r#"if a == "x" && b then "yes" else "no" + suffix"#);
227 insta::assert_debug_snapshot!(e);
228 }
229}