use crate::obj::prelude::{Float, Int, Str}; use crate::syn::ast::*; use crate::syn::span::*; use pest::{error::Error, iterators::Pair, Parser}; use std::rc::Rc; #[derive(pest_derive::Parser)] #[grammar = "syn/parser.pest"] pub struct SybilParser; pub type Result> = std::result::Result; fn unescape_string(text: &str) -> Str { let mut string = String::with_capacity(text.len() - 2); let mut chars = text.chars().skip(1).take(text.len() - 2); while let Some(c) = chars.next() { if c == '\\' { let c = match chars .next() .expect("reached end of string literal before escape") { '"' => '"', '\'' => '\'', '\\' => '\\', 'n' => '\n', 'r' => '\r', 't' => '\t', u => panic!( "unexpected character escape that made it through the lexer: {:?}", u ), }; string.push(c); } else { string.push(c); } } string } fn parse_atom(source: &Rc, pair: Pair) -> Result { let pair_span = pair.as_span(); let atom = match pair.as_rule() { Rule::float => { let float = pair.as_str().parse::().unwrap(); Atom::Float(float) } Rule::int => { let int = pair.as_str().parse::().unwrap(); Atom::Int(int) } Rule::assign => { let word = pair.into_inner().next().unwrap().as_str().to_string(); Atom::Assign(word) } Rule::word => { let word = pair.as_str().to_string(); Atom::Word(word) } Rule::str => { let string = pair.as_str(); Atom::Str(unescape_string(string)) } Rule::apply => Atom::Apply, rule => unreachable!("{:?}", rule), }; let span = Span { source: Rc::clone(source), start: pair_span.start(), end: pair_span.end(), }; Ok(SpAtom::new(span, atom)) } fn parse_expr(source: &Rc, pair: Pair) -> Result { let pair_span = pair.as_span(); let expr = match pair.as_rule() { Rule::atom => Expr::Atom(parse_atom(source, pair.into_inner().next().unwrap())?), Rule::quote => Expr::Quote( pair.into_inner() .map(|pair| parse_stmt(source, pair.into_inner().next().unwrap())) .collect::>>()?, ), rule => unreachable!("{:?}", rule), }; let span = Span { source: Rc::clone(source), start: pair_span.start(), end: pair_span.end(), }; Ok(SpExpr::new(span, expr)) } fn parse_stmt(source: &Rc, pair: Pair) -> Result { let pair_span = pair.as_span(); let stmt = match pair.as_rule() { Rule::expr => Stmt::Expr(parse_expr(source, pair.into_inner().next().unwrap())?), Rule::include => Stmt::Include(unescape_string(pair.into_inner().next().unwrap().as_str())), rule => unreachable!("{:?}", rule), }; let span = Span { source: Rc::clone(&source), start: pair_span.start(), end: pair_span.end(), }; Ok(SpStmt::new(span, stmt)) } pub fn parse_file(source: impl ToString, text: &str) -> Result> { let input = SybilParser::parse(Rule::file, text)?.next().unwrap(); let source = Rc::new(source.to_string()); let mut stmts = Vec::new(); for pair in input.into_inner() { match pair.as_rule() { Rule::EOI => {} Rule::stmt => { let pair = pair.into_inner().next().unwrap(); stmts.push(parse_stmt(&source, pair)?); } rule => unreachable!("{:?}", rule), } } Ok(stmts) }