Download src/parser.rs from RDS777/smt: direct link, hf CLI and curl.
- Browser
- Download file 4.95 kB
-
https://huggingface.co/spaces/RDS777/smt/resolve/main/src/parser.rs
- Command line
-
hf download hf://spaces/RDS777/smt/src/parser.rs
-
curl -L -o parser.rs https://huggingface.co/spaces/RDS777/smt/resolve/main/src/parser.rs
4.95 kB
| //! Parser domain: bridges the zero-copy lexer to the canonical | |
| //! e-graph. This is the piece that finishes Phase I — until now the | |
| //! FFI entry point only interned flat leaf tokens; this walks actual | |
| //! s-expression structure, applies AC-normalization and the depth | |
| //! budget through `EGraph::intern_app`, and resolves quantifier-bound | |
| //! variables to De Bruijn indices via `BinderStack` instead of names. | |
| use crate::ast::{BinderStack, Lexer, TokenKind}; | |
| use crate::canon::{CanonId, DepthExceeded, EGraph}; | |
| pub enum ParseError { | |
| UnexpectedEof, | |
| UnexpectedToken { found: String, at: usize }, | |
| DepthBudgetExceeded, | |
| } | |
| impl From<DepthExceeded> for ParseError { | |
| fn from(_: DepthExceeded) -> Self { | |
| ParseError::DepthBudgetExceeded | |
| } | |
| } | |
| const QUANTIFIERS: &[&str] = &["forall", "exists"]; | |
| pub struct Parser<'src> { | |
| lexer: Lexer<'src>, | |
| peeked: Option<crate::ast::Token<'src>>, | |
| binders: BinderStack<'src>, | |
| } | |
| impl<'src> Parser<'src> { | |
| pub fn new(input: &'src str) -> Self { | |
| Self { lexer: Lexer::new(input), peeked: None, binders: BinderStack::new() } | |
| } | |
| fn peek(&mut self) -> crate::ast::Token<'src> { | |
| if self.peeked.is_none() { | |
| self.peeked = Some(self.lexer.next_token()); | |
| } | |
| self.peeked.unwrap() | |
| } | |
| fn advance(&mut self) -> crate::ast::Token<'src> { | |
| match self.peeked.take() { | |
| Some(t) => t, | |
| None => self.lexer.next_token(), | |
| } | |
| } | |
| /// Parses every top-level term in the input, interning each into | |
| /// `egraph`, and returns their root ids. A malformed or | |
| /// depth-exceeding formula aborts with `ParseError` — callers | |
| /// surface this as an `unknown`/`error` verdict rather than | |
| /// panicking, same treatment as the OOM guard. | |
| pub fn parse_all(&mut self, egraph: &mut EGraph) -> Result<Vec<CanonId>, ParseError> { | |
| let mut roots = Vec::new(); | |
| loop { | |
| if self.peek().kind == TokenKind::Eof { | |
| break; | |
| } | |
| roots.push(self.parse_term(egraph)?); | |
| } | |
| Ok(roots) | |
| } | |
| fn parse_term(&mut self, egraph: &mut EGraph) -> Result<CanonId, ParseError> { | |
| let tok = self.advance(); | |
| match tok.kind { | |
| TokenKind::Eof => Err(ParseError::UnexpectedEof), | |
| TokenKind::RParen => Err(ParseError::UnexpectedToken { found: ")".into(), at: tok.pos }), | |
| TokenKind::IntLit | TokenKind::Symbol => { | |
| // A bare symbol may refer to a De Bruijn-bound variable; | |
| // resolve it against the active binder scopes first so | |
| // alpha-equivalent formulas intern identically (Q1 sol.1). | |
| if let Some(idx) = self.binders.resolve(tok.text) { | |
| return Ok(egraph.intern_leaf(&format!("#{idx}"))); | |
| } | |
| Ok(egraph.intern_leaf(tok.text)) | |
| } | |
| TokenKind::LParen => self.parse_app(egraph), | |
| } | |
| } | |
| fn parse_app(&mut self, egraph: &mut EGraph) -> Result<CanonId, ParseError> { | |
| let head = self.advance(); | |
| if head.kind != TokenKind::Symbol { | |
| return Err(ParseError::UnexpectedToken { found: head.text.into(), at: head.pos }); | |
| } | |
| let is_quantifier = QUANTIFIERS.contains(&head.text); | |
| let mut bound_here = 0u32; | |
| if is_quantifier { | |
| // Expect a binder list: (forall (x y) body). Each bound | |
| // name is pushed onto the binder stack so occurrences in | |
| // `body` resolve to De Bruijn indices rather than names. | |
| self.expect(TokenKind::LParen)?; | |
| loop { | |
| let t = self.peek(); | |
| if t.kind == TokenKind::RParen { | |
| self.advance(); | |
| break; | |
| } | |
| if t.kind != TokenKind::Symbol { | |
| return Err(ParseError::UnexpectedToken { found: t.text.into(), at: t.pos }); | |
| } | |
| self.advance(); | |
| self.binders.push(t.text); | |
| bound_here += 1; | |
| } | |
| } | |
| let mut children = Vec::new(); | |
| loop { | |
| let t = self.peek(); | |
| match t.kind { | |
| TokenKind::RParen => { | |
| self.advance(); | |
| break; | |
| } | |
| TokenKind::Eof => return Err(ParseError::UnexpectedEof), | |
| _ => children.push(self.parse_term(egraph)?), | |
| } | |
| } | |
| for _ in 0..bound_here { | |
| self.binders.pop(); | |
| } | |
| Ok(egraph.intern_app(head.text, &children)?) | |
| } | |
| fn expect(&mut self, kind: TokenKind) -> Result<(), ParseError> { | |
| let t = self.advance(); | |
| if t.kind == kind { | |
| Ok(()) | |
| } else { | |
| Err(ParseError::UnexpectedToken { found: t.text.into(), at: t.pos }) | |
| } | |
| } | |
| } | |