1pub use crate::compiler::ast::{BinOp, Expr, UnOp};
4
5use crate::compiler::lexer::{Token, tokenise};
6use crate::error::{DialogueError, Result};
7
8pub fn parse_expr_at(source: &str, file: &str, line: usize) -> Result<Expr> {
17 let tokens: Vec<Token> = tokenise(source, file, line)?
18 .into_iter()
19 .map(|(t, _)| t)
20 .collect();
21 let mut p = ExprParser {
22 tokens: &tokens,
23 pos: 0,
24 file,
25 line,
26 };
27 let expr = p.parse_or()?;
28 if p.pos < p.tokens.len() {
29 return Err(DialogueError::Parse {
30 file: file.to_owned(),
31 line,
32 message: format!("unexpected token after expression: {:?}", p.tokens[p.pos]),
33 });
34 }
35 Ok(expr)
36}
37
38struct ExprParser<'a> {
41 tokens: &'a [Token],
42 pos: usize,
43 file: &'a str,
44 line: usize,
45}
46
47impl ExprParser<'_> {
48 fn peek(&self) -> Option<&Token> {
49 self.tokens.get(self.pos)
50 }
51
52 fn advance(&mut self) -> Option<&Token> {
53 let t = self.tokens.get(self.pos);
54 self.pos += 1;
55 t
56 }
57
58 fn err(&self, msg: &str) -> DialogueError {
59 DialogueError::Parse {
60 file: self.file.to_owned(),
61 line: self.line,
62 message: msg.into(),
63 }
64 }
65
66 fn parse_or(&mut self) -> Result<Expr> {
70 let mut left = self.parse_and()?;
71 while self.peek() == Some(&Token::OrOr) {
72 self.advance();
73 let right = self.parse_and()?;
74 left = Expr::Binary {
75 left: Box::new(left),
76 op: BinOp::Or,
77 right: Box::new(right),
78 };
79 }
80 Ok(left)
81 }
82
83 fn parse_and(&mut self) -> Result<Expr> {
84 let mut left = self.parse_equality()?;
85 while self.peek() == Some(&Token::AndAnd) {
86 self.advance();
87 let right = self.parse_equality()?;
88 left = Expr::Binary {
89 left: Box::new(left),
90 op: BinOp::And,
91 right: Box::new(right),
92 };
93 }
94 Ok(left)
95 }
96
97 fn parse_equality(&mut self) -> Result<Expr> {
98 let mut left = self.parse_comparison()?;
99 loop {
100 let op = match self.peek() {
101 Some(Token::EqEq) => BinOp::Eq,
102 Some(Token::Neq) => BinOp::Neq,
103 _ => break,
104 };
105 self.advance();
106 let right = self.parse_comparison()?;
107 left = Expr::Binary {
108 left: Box::new(left),
109 op,
110 right: Box::new(right),
111 };
112 }
113 Ok(left)
114 }
115
116 fn parse_comparison(&mut self) -> Result<Expr> {
117 let mut left = self.parse_additive()?;
118 loop {
119 let op = match self.peek() {
120 Some(Token::Lt) => BinOp::Lt,
121 Some(Token::Lte) => BinOp::Lte,
122 Some(Token::Gt) => BinOp::Gt,
123 Some(Token::Gte) => BinOp::Gte,
124 _ => break,
125 };
126 self.advance();
127 let right = self.parse_additive()?;
128 left = Expr::Binary {
129 left: Box::new(left),
130 op,
131 right: Box::new(right),
132 };
133 }
134 Ok(left)
135 }
136
137 fn parse_additive(&mut self) -> Result<Expr> {
138 let mut left = self.parse_multiplicative()?;
139 loop {
140 let op = match self.peek() {
141 Some(Token::Plus) => BinOp::Add,
142 Some(Token::Minus) => BinOp::Sub,
143 _ => break,
144 };
145 self.advance();
146 let right = self.parse_multiplicative()?;
147 left = Expr::Binary {
148 left: Box::new(left),
149 op,
150 right: Box::new(right),
151 };
152 }
153 Ok(left)
154 }
155
156 fn parse_multiplicative(&mut self) -> Result<Expr> {
157 let mut left = self.parse_unary()?;
158 loop {
159 let op = match self.peek() {
160 Some(Token::Star) => BinOp::Mul,
161 Some(Token::Slash) => BinOp::Div,
162 Some(Token::Percent) => BinOp::Rem,
163 _ => break,
164 };
165 self.advance();
166 let right = self.parse_unary()?;
167 left = Expr::Binary {
168 left: Box::new(left),
169 op,
170 right: Box::new(right),
171 };
172 }
173 Ok(left)
174 }
175
176 fn parse_unary(&mut self) -> Result<Expr> {
177 match self.peek() {
178 Some(Token::Minus) => {
179 self.advance();
180 let expr = self.parse_unary()?;
181 Ok(Expr::Unary {
182 op: UnOp::Neg,
183 expr: Box::new(expr),
184 })
185 }
186 Some(Token::Bang) => {
187 self.advance();
188 let expr = self.parse_unary()?;
189 Ok(Expr::Unary {
190 op: UnOp::Not,
191 expr: Box::new(expr),
192 })
193 }
194 _ => self.parse_primary(),
195 }
196 }
197
198 fn parse_primary(&mut self) -> Result<Expr> {
199 match self.advance().cloned() {
200 Some(Token::Number(n)) => Ok(Expr::Number(n)),
201 Some(Token::Str(s)) => Ok(Expr::Text(s)),
202 Some(Token::Var(v)) => Ok(Expr::Var(v)),
203 Some(Token::Ident(ref s)) if s == "true" => Ok(Expr::Bool(true)),
204 Some(Token::Ident(ref s)) if s == "false" => Ok(Expr::Bool(false)),
205 Some(Token::Ident(name)) => {
206 if self.peek() == Some(&Token::LParen) {
207 self.advance();
208 let mut args = Vec::new();
209 if self.peek() != Some(&Token::RParen) {
210 args.push(self.parse_or()?);
211 while self.peek() == Some(&Token::Comma) {
212 self.advance();
213 args.push(self.parse_or()?);
214 }
215 }
216 if self.advance() != Some(&Token::RParen) {
217 return Err(self.err("expected `)` after function arguments"));
218 }
219 Ok(Expr::Call { name, args })
220 } else {
221 Err(self.err(&format!(
222 "unknown identifier `{name}`; variables need a `$` prefix"
223 )))
224 }
225 }
226 Some(Token::LParen) => {
227 let expr = self.parse_or()?;
228 if self.advance() != Some(&Token::RParen) {
229 return Err(self.err("expected closing `)`"));
230 }
231 Ok(expr)
232 }
233 Some(t) => Err(self.err(&format!("unexpected token `{t:?}`"))),
234 None => Err(self.err("unexpected end of expression")),
235 }
236 }
237}
238
239#[cfg(test)]
240#[path = "expr_tests.rs"]
241mod tests;