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