1use crate::ast::{BinOp, Def, Expr, Program};
4use crate::error::{CompileError, Span};
5use crate::lexer::{Tok, Token};
6
7struct Parser<'a> {
8 toks: &'a [Token],
9 pos: usize,
10}
11
12fn infix_binding_power(t: &Tok) -> Option<(BinOp, u8, u8)> {
15 Some(match t {
16 Tok::Tilde => (BinOp::Feedback, 1, 2),
17 Tok::Colon => (BinOp::Seq, 3, 4),
18 Tok::Merge => (BinOp::Merge, 5, 6),
19 Tok::Split => (BinOp::Split, 7, 8),
20 Tok::Comma => (BinOp::Par, 9, 10),
21 Tok::Plus => (BinOp::Add, 11, 12),
22 Tok::Minus => (BinOp::Sub, 11, 12),
23 Tok::Star => (BinOp::Mul, 13, 14),
24 Tok::Slash => (BinOp::Div, 13, 14),
25 Tok::Percent => (BinOp::Rem, 13, 14),
26 Tok::At => (BinOp::Delay, 15, 16),
27 _ => return None,
28 })
29}
30
31impl<'a> Parser<'a> {
32 fn new(toks: &'a [Token]) -> Self {
33 Self { toks, pos: 0 }
34 }
35 fn peek(&self) -> &Token {
36 &self.toks[self.pos]
37 }
38 fn bump(&mut self) -> Token {
39 let t = self.toks[self.pos].clone();
40 if self.pos + 1 < self.toks.len() {
41 self.pos += 1;
42 }
43 t
44 }
45 fn eat(&mut self, want: &Tok) -> Result<Token, CompileError> {
46 if &self.peek().tok == want {
47 Ok(self.bump())
48 } else {
49 let p = self.peek();
50 Err(CompileError::Parse {
51 msg: format!("expected {want:?}, found {:?}", p.tok),
52 span: p.span,
53 })
54 }
55 }
56
57 fn parse_program(&mut self) -> Result<Program, CompileError> {
58 let mut defs = Vec::new();
59 while self.peek().tok != Tok::Eof {
60 defs.push(self.parse_def()?);
61 }
62 if defs.is_empty() {
63 return Err(CompileError::Parse {
64 msg: "empty program (expected at least `process = ...;`)".into(),
65 span: Span::new(0, 0),
66 });
67 }
68 Ok(Program { defs })
69 }
70
71 fn parse_def(&mut self) -> Result<Def, CompileError> {
72 let name_tok = self.peek().clone();
73 let (name, start) = match &name_tok.tok {
74 Tok::Ident(n) => (n.clone(), name_tok.span.start),
75 _ => {
76 return Err(CompileError::Parse {
77 msg: format!("expected definition name, found {:?}", name_tok.tok),
78 span: name_tok.span,
79 })
80 }
81 };
82 self.bump();
83 let mut params = Vec::new();
84 if self.peek().tok == Tok::LParen {
85 self.bump();
86 loop {
87 match &self.peek().tok {
88 Tok::Ident(p) => {
89 params.push(p.clone());
90 self.bump();
91 }
92 other => {
93 return Err(CompileError::Parse {
94 msg: format!("expected parameter name, found {other:?}"),
95 span: self.peek().span,
96 })
97 }
98 }
99 match self.peek().tok {
100 Tok::Comma => {
101 self.bump();
102 }
103 Tok::RParen => break,
104 _ => {
105 return Err(CompileError::Parse {
106 msg: "expected `,` or `)` in parameter list".into(),
107 span: self.peek().span,
108 })
109 }
110 }
111 }
112 self.eat(&Tok::RParen)?;
113 }
114 self.eat(&Tok::Eq)?;
115 let body = self.parse_expr(0, false)?;
116 let semi = self.eat(&Tok::Semi)?;
117 Ok(Def {
118 name,
119 params,
120 body,
121 span: Span::new(start, semi.span.end),
122 })
123 }
124
125 fn parse_expr(&mut self, min_bp: u8, no_comma: bool) -> Result<Expr, CompileError> {
130 let mut lhs = self.parse_prefix(no_comma)?;
131 while let Some((op, l_bp, r_bp)) = infix_binding_power(&self.peek().tok) {
132 if no_comma && op == BinOp::Par {
133 break;
134 }
135 if l_bp < min_bp {
136 break;
137 }
138 self.bump();
139 let rhs = self.parse_expr(r_bp, no_comma)?;
140 let span = lhs.span().merge(rhs.span());
141 lhs = Expr::Bin {
142 op,
143 lhs: Box::new(lhs),
144 rhs: Box::new(rhs),
145 span,
146 };
147 }
148 Ok(lhs)
149 }
150
151 fn parse_prefix(&mut self, no_comma: bool) -> Result<Expr, CompileError> {
152 let t = self.peek().clone();
153 match t.tok {
154 Tok::Minus => {
155 self.bump();
156 let inner = self.parse_expr(15, no_comma)?;
157 let span = t.span.merge(inner.span());
158 Ok(Expr::Neg(Box::new(inner), span))
159 }
160 _ => self.parse_atom(),
161 }
162 }
163
164 fn parse_atom(&mut self) -> Result<Expr, CompileError> {
165 let t = self.bump();
166 match t.tok {
167 Tok::Int(v) => Ok(Expr::Int(v, t.span)),
168 Tok::Float(v) => Ok(Expr::Float(v, t.span)),
169 Tok::Wire => Ok(Expr::Wire(t.span)),
170 Tok::Cut => Ok(Expr::Cut(t.span)),
171 Tok::Str(s) => Ok(Expr::Str(s, t.span)),
172 Tok::Plus => Ok(Expr::Ref("+".into(), t.span)),
173 Tok::Minus => Ok(Expr::Ref("-".into(), t.span)),
174 Tok::Star => Ok(Expr::Ref("*".into(), t.span)),
175 Tok::Slash => Ok(Expr::Ref("/".into(), t.span)),
176 Tok::Percent => Ok(Expr::Ref("%".into(), t.span)),
177 Tok::Ident(name) => {
178 if self.peek().tok == Tok::LParen {
179 self.bump();
180 let mut args = Vec::new();
181 if self.peek().tok != Tok::RParen {
182 loop {
183 args.push(self.parse_expr(0, true)?);
184 match self.peek().tok {
185 Tok::Comma => {
186 self.bump();
187 }
188 _ => break,
189 }
190 }
191 }
192 let rp = self.eat(&Tok::RParen)?;
193 Ok(Expr::Apply {
194 name,
195 args,
196 span: t.span.merge(rp.span),
197 })
198 } else {
199 Ok(Expr::Ref(name, t.span))
200 }
201 }
202 Tok::LParen => {
203 let inner = self.parse_expr(0, false)?;
204 self.eat(&Tok::RParen)?;
205 Ok(inner)
206 }
207 other => Err(CompileError::Parse {
208 msg: format!("unexpected token {other:?}"),
209 span: t.span,
210 }),
211 }
212 }
213}
214
215pub fn parse(tokens: &[Token]) -> Result<Program, CompileError> {
217 Parser::new(tokens).parse_program()
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use crate::lexer::tokenize;
224
225 fn prog(src: &str) -> Program {
226 parse(&tokenize(src).unwrap()).unwrap()
227 }
228 fn body(src: &str) -> Expr {
229 let p = prog(src);
230 p.defs
231 .into_iter()
232 .find(|d| d.name == "process")
233 .unwrap()
234 .body
235 }
236
237 #[test]
238 fn parses_single_def() {
239 let p = prog("process = _;");
240 assert_eq!(p.defs.len(), 1);
241 assert_eq!(p.defs[0].name, "process");
242 assert!(matches!(p.defs[0].body, Expr::Wire(_)));
243 }
244
245 #[test]
246 fn arithmetic_binds_tighter_than_par() {
247 match body("process = _ * 2 , _;") {
248 Expr::Bin {
249 op: BinOp::Par,
250 lhs,
251 ..
252 } => {
253 assert!(matches!(*lhs, Expr::Bin { op: BinOp::Mul, .. }));
254 }
255 other => panic!("expected top Par, got {other:?}"),
256 }
257 }
258
259 #[test]
260 fn feedback_binds_loosest() {
261 match body("process = + ~ _;") {
262 Expr::Bin {
263 op: BinOp::Feedback,
264 lhs,
265 rhs,
266 ..
267 } => {
268 assert!(matches!(*lhs, Expr::Ref(_, _)) || matches!(*lhs, Expr::Bin { .. }));
269 assert!(matches!(*rhs, Expr::Wire(_)));
270 }
271 other => panic!("expected Feedback at top, got {other:?}"),
272 }
273 }
274
275 #[test]
276 fn seq_is_left_associative() {
277 match body("process = _ : _ : _;") {
278 Expr::Bin {
279 op: BinOp::Seq,
280 lhs,
281 ..
282 } => {
283 assert!(matches!(*lhs, Expr::Bin { op: BinOp::Seq, .. }));
284 }
285 other => panic!("expected Seq at top, got {other:?}"),
286 }
287 }
288
289 #[test]
290 fn application_uses_comma_as_arg_separator() {
291 let p = prog("gain(x, y) = x; process = gain(_, 2);");
292 let call = p.defs.iter().find(|d| d.name == "process").unwrap();
293 match &call.body {
294 Expr::Apply { name, args, .. } => {
295 assert_eq!(name, "gain");
296 assert_eq!(args.len(), 2);
297 }
298 other => panic!("expected Apply, got {other:?}"),
299 }
300 }
301
302 #[test]
303 fn grouping_paren_is_parallel_inside() {
304 match body("process = (_ , _) :> _;") {
305 Expr::Bin {
306 op: BinOp::Merge,
307 lhs,
308 ..
309 } => {
310 assert!(matches!(*lhs, Expr::Bin { op: BinOp::Par, .. }));
311 }
312 other => panic!("expected Merge, got {other:?}"),
313 }
314 }
315
316 #[test]
317 fn application_arg_may_be_a_composed_expression() {
318 let p = prog("f(a, b) = a; process = f(_ : _, 2);");
319 let call = p.defs.iter().find(|d| d.name == "process").unwrap();
320 match &call.body {
321 Expr::Apply { name, args, .. } => {
322 assert_eq!(name, "f");
323 assert_eq!(args.len(), 2);
324 assert!(matches!(args[0], Expr::Bin { op: BinOp::Seq, .. }));
325 }
326 other => panic!("expected Apply, got {other:?}"),
327 }
328 }
329
330 #[test]
331 fn rejects_missing_semicolon() {
332 assert!(parse(&tokenize("process = _").unwrap()).is_err());
333 }
334
335 #[test]
336 fn parses_string_arg() {
337 let p = parse(&tokenize(r#"process = f("x");"#).unwrap()).unwrap();
338 let call = p.defs.iter().find(|d| d.name == "process").unwrap();
339 match &call.body {
340 Expr::Apply { name, args, .. } => {
341 assert_eq!(name, "f");
342 assert_eq!(args.len(), 1);
343 assert!(matches!(&args[0], Expr::Str(s, _) if s == "x"));
344 }
345 other => panic!("expected Apply, got {other:?}"),
346 }
347 }
348}