Skip to main content

pflow_dsl/
parser.rs

1//! S-expression parser producing AST nodes.
2
3use crate::ast::*;
4use crate::lexer::{Lexer, Token, TokenType};
5
6/// Parser for the S-expression DSL.
7pub struct Parser {
8    lexer: Lexer,
9    cur: Token,
10    peek: Token,
11}
12
13impl Parser {
14    pub fn new(input: &str) -> Self {
15        let mut lexer = Lexer::new(input);
16        let cur = lexer.next_token();
17        let peek = lexer.next_token();
18        Self { lexer, cur, peek }
19    }
20
21    fn next_token(&mut self) {
22        self.cur = self.peek.clone();
23        self.peek = self.lexer.next_token();
24    }
25
26    fn expect(&self, t: TokenType) -> Result<(), String> {
27        if self.cur.typ != t {
28            Err(format!(
29                "expected {}, got {} at position {}",
30                t, self.cur.typ, self.cur.pos
31            ))
32        } else {
33            Ok(())
34        }
35    }
36
37    fn expect_symbol(&self, s: &str) -> Result<(), String> {
38        if self.cur.typ != TokenType::Symbol || self.cur.literal != s {
39            Err(format!(
40                "expected symbol {:?}, got {} {:?} at position {}",
41                s, self.cur.typ, self.cur.literal, self.cur.pos
42            ))
43        } else {
44            Ok(())
45        }
46    }
47
48    fn expect_identifier(&mut self) -> Result<String, String> {
49        if self.cur.typ == TokenType::Symbol {
50            let lit = self.cur.literal.clone();
51            self.next_token();
52            Ok(lit)
53        } else {
54            Err(format!(
55                "expected identifier, got {} at position {}",
56                self.cur.typ, self.cur.pos
57            ))
58        }
59    }
60
61    fn expect_guard_expr(&mut self) -> Result<String, String> {
62        if self.cur.typ == TokenType::Guard {
63            let lit = self.cur.literal.clone();
64            self.next_token();
65            Ok(lit)
66        } else {
67            Err(format!(
68                "expected guard expression, got {} at position {}",
69                self.cur.typ, self.cur.pos
70            ))
71        }
72    }
73
74    fn parse_schema(&mut self) -> Result<SchemaNode, String> {
75        self.expect(TokenType::LParen)?;
76        self.next_token();
77
78        self.expect_symbol("schema")?;
79        self.next_token();
80
81        let name = self.expect_identifier()?;
82        let mut node = SchemaNode {
83            name,
84            version: String::new(),
85            states: Vec::new(),
86            actions: Vec::new(),
87            arcs: Vec::new(),
88            constraints: Vec::new(),
89        };
90
91        while self.cur.typ != TokenType::RParen && self.cur.typ != TokenType::Eof {
92            if self.cur.typ != TokenType::LParen {
93                return Err(format!(
94                    "expected '(' for clause, got {} at position {}",
95                    self.cur.typ, self.cur.pos
96                ));
97            }
98            self.next_token();
99
100            if self.cur.typ != TokenType::Symbol {
101                return Err(format!(
102                    "expected clause type, got {} at position {}",
103                    self.cur.typ, self.cur.pos
104                ));
105            }
106
107            match self.cur.literal.as_str() {
108                "version" => {
109                    self.next_token();
110                    node.version = self.expect_identifier()?;
111                    self.expect(TokenType::RParen)?;
112                    self.next_token();
113                }
114                "states" => {
115                    self.next_token();
116                    node.states = self.parse_states()?;
117                    self.expect(TokenType::RParen)?;
118                    self.next_token();
119                }
120                "actions" => {
121                    self.next_token();
122                    node.actions = self.parse_actions()?;
123                    self.expect(TokenType::RParen)?;
124                    self.next_token();
125                }
126                "arcs" => {
127                    self.next_token();
128                    node.arcs = self.parse_arcs()?;
129                    self.expect(TokenType::RParen)?;
130                    self.next_token();
131                }
132                "constraints" => {
133                    self.next_token();
134                    node.constraints = self.parse_constraints()?;
135                    self.expect(TokenType::RParen)?;
136                    self.next_token();
137                }
138                other => {
139                    return Err(format!(
140                        "unknown clause type {:?} at position {}",
141                        other, self.cur.pos
142                    ));
143                }
144            }
145        }
146
147        Ok(node)
148    }
149
150    fn parse_states(&mut self) -> Result<Vec<StateNode>, String> {
151        let mut states = Vec::new();
152
153        while self.cur.typ == TokenType::LParen {
154            self.next_token();
155            self.expect_symbol("state")?;
156            self.next_token();
157
158            let id = self.expect_identifier()?;
159            let mut state = StateNode {
160                id,
161                typ: String::new(),
162                kind: "data".into(),
163                initial: None,
164                exported: false,
165            };
166
167            while self.cur.typ == TokenType::Keyword {
168                let keyword = self.cur.literal.clone();
169                self.next_token();
170
171                match keyword.as_str() {
172                    ":type" => {
173                        state.typ = self.expect_identifier()?;
174                    }
175                    ":kind" => {
176                        self.expect(TokenType::Symbol)?;
177                        state.kind = self.cur.literal.clone();
178                        self.next_token();
179                    }
180                    ":initial" => {
181                        state.initial = Some(self.parse_value()?);
182                    }
183                    ":exported" => {
184                        state.exported = true;
185                    }
186                    other => {
187                        return Err(format!(
188                            "unknown state keyword {:?} at position {}",
189                            other, self.cur.pos
190                        ));
191                    }
192                }
193            }
194
195            self.expect(TokenType::RParen)?;
196            self.next_token();
197            states.push(state);
198        }
199
200        Ok(states)
201    }
202
203    fn parse_actions(&mut self) -> Result<Vec<ActionNode>, String> {
204        let mut actions = Vec::new();
205
206        while self.cur.typ == TokenType::LParen {
207            self.next_token();
208            self.expect_symbol("action")?;
209            self.next_token();
210
211            let id = self.expect_identifier()?;
212            let mut action = ActionNode {
213                id,
214                guard: String::new(),
215            };
216
217            while self.cur.typ == TokenType::Keyword {
218                let keyword = self.cur.literal.clone();
219                self.next_token();
220
221                match keyword.as_str() {
222                    ":guard" => {
223                        action.guard = self.expect_guard_expr()?;
224                    }
225                    other => {
226                        return Err(format!(
227                            "unknown action keyword {:?} at position {}",
228                            other, self.cur.pos
229                        ));
230                    }
231                }
232            }
233
234            self.expect(TokenType::RParen)?;
235            self.next_token();
236            actions.push(action);
237        }
238
239        Ok(actions)
240    }
241
242    fn parse_arcs(&mut self) -> Result<Vec<ArcNode>, String> {
243        let mut arcs = Vec::new();
244
245        while self.cur.typ == TokenType::LParen {
246            self.next_token();
247            self.expect_symbol("arc")?;
248            self.next_token();
249
250            let source = self.expect_identifier()?;
251
252            self.expect(TokenType::Arrow)?;
253            self.next_token();
254
255            let target = self.expect_identifier()?;
256
257            let mut arc = ArcNode {
258                source,
259                target,
260                keys: Vec::new(),
261                value: String::new(),
262            };
263
264            while self.cur.typ == TokenType::Keyword {
265                let keyword = self.cur.literal.clone();
266                self.next_token();
267
268                match keyword.as_str() {
269                    ":keys" => {
270                        arc.keys = self.parse_identifier_list()?;
271                    }
272                    ":value" => {
273                        arc.value = self.expect_identifier()?;
274                    }
275                    other => {
276                        return Err(format!(
277                            "unknown arc keyword {:?} at position {}",
278                            other, self.cur.pos
279                        ));
280                    }
281                }
282            }
283
284            self.expect(TokenType::RParen)?;
285            self.next_token();
286            arcs.push(arc);
287        }
288
289        Ok(arcs)
290    }
291
292    fn parse_constraints(&mut self) -> Result<Vec<ConstraintNode>, String> {
293        let mut constraints = Vec::new();
294
295        while self.cur.typ == TokenType::LParen {
296            self.next_token();
297            self.expect_symbol("constraint")?;
298            self.next_token();
299
300            let id = self.expect_identifier()?;
301            let expr = self.expect_guard_expr()?;
302
303            self.expect(TokenType::RParen)?;
304            self.next_token();
305            constraints.push(ConstraintNode { id, expr });
306        }
307
308        Ok(constraints)
309    }
310
311    fn parse_identifier_list(&mut self) -> Result<Vec<String>, String> {
312        self.expect(TokenType::LParen)?;
313        self.next_token();
314
315        let mut ids = Vec::new();
316        while self.cur.typ == TokenType::Symbol {
317            ids.push(self.cur.literal.clone());
318            self.next_token();
319        }
320
321        self.expect(TokenType::RParen)?;
322        self.next_token();
323
324        Ok(ids)
325    }
326
327    fn parse_value(&mut self) -> Result<InitialValue, String> {
328        match self.cur.typ {
329            TokenType::Number => {
330                let n: i64 = self
331                    .cur
332                    .literal
333                    .parse()
334                    .map_err(|e| format!("invalid number: {}", e))?;
335                self.next_token();
336                Ok(InitialValue::Int(n))
337            }
338            TokenType::Str => {
339                let s = self.cur.literal.clone();
340                self.next_token();
341                Ok(InitialValue::Str(s))
342            }
343            TokenType::Symbol if self.cur.literal == "nil" => {
344                self.next_token();
345                Ok(InitialValue::Nil)
346            }
347            _ => Err(format!(
348                "unexpected token {} in value position at {}",
349                self.cur.typ, self.cur.pos
350            )),
351        }
352    }
353}
354
355/// Parse S-expression DSL input into a SchemaNode.
356pub fn parse(input: &str) -> Result<SchemaNode, String> {
357    let mut p = Parser::new(input);
358    p.parse_schema()
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364
365    #[test]
366    fn test_parse_simple_schema() {
367        let input = r#"(schema ERC-020
368  (version v1.0.0)
369
370  (states
371    (state totalSupply :type uint256)
372    (state balances :type map[address]uint256 :exported)
373  )
374
375  (actions
376    (action transfer :guard {balances[from] >= amount})
377  )
378
379  (arcs
380    (arc balances -> transfer :keys (from))
381    (arc transfer -> balances :keys (to))
382  )
383
384  (constraints
385    (constraint conservation {sum(balances) == totalSupply})
386  )
387)"#;
388
389        let node = parse(input).unwrap();
390        assert_eq!(node.name, "ERC-020");
391        assert_eq!(node.version, "v1.0.0");
392        assert_eq!(node.states.len(), 2);
393        assert_eq!(node.actions.len(), 1);
394        assert_eq!(node.arcs.len(), 2);
395        assert_eq!(node.constraints.len(), 1);
396
397        assert_eq!(node.states[0].id, "totalSupply");
398        assert_eq!(node.states[1].id, "balances");
399        assert!(node.states[1].exported);
400
401        assert_eq!(node.actions[0].id, "transfer");
402        assert_eq!(node.actions[0].guard, "balances[from] >= amount");
403
404        assert_eq!(node.arcs[0].source, "balances");
405        assert_eq!(node.arcs[0].target, "transfer");
406        assert_eq!(node.arcs[0].keys, vec!["from"]);
407
408        assert_eq!(node.constraints[0].id, "conservation");
409        assert_eq!(node.constraints[0].expr, "sum(balances) == totalSupply");
410    }
411
412    #[test]
413    fn test_parse_token_state() {
414        let input = r#"(schema counter
415  (states
416    (state count :kind token :initial 5)
417  )
418  (actions
419    (action inc)
420  )
421  (arcs
422    (arc inc -> count)
423  )
424)"#;
425
426        let node = parse(input).unwrap();
427        assert_eq!(node.states[0].kind, "token");
428        match &node.states[0].initial {
429            Some(InitialValue::Int(5)) => {}
430            other => panic!("expected Int(5), got {:?}", other),
431        }
432    }
433}