diff --git a/src/ast/desugar.rs b/src/ast/desugar.rs index 67aa786a..d73c5c44 100644 --- a/src/ast/desugar.rs +++ b/src/ast/desugar.rs @@ -112,7 +112,7 @@ pub(crate) fn desugar_command( let s = std::fs::read_to_string(&file) .unwrap_or_else(|_| panic!("{span} Failed to read file {file}")); return desugar_program( - parse_program(Some(file), &s, parser)?, + parser.get_program_from_string(Some(file), &s)?, parser, seminaive_transform, ); diff --git a/src/ast/parse.rs b/src/ast/parse.rs index a95a72c2..d619897b 100644 --- a/src/ast/parse.rs +++ b/src/ast/parse.rs @@ -3,47 +3,6 @@ use crate::*; use ordered_float::OrderedFloat; -pub type Macro = fn(&[Sexp], Span, &Parser) -> Result; - -#[derive(Clone)] -pub struct Parser { - pub commands: HashMap>>, - pub actions: HashMap>>, - pub exprs: HashMap>, - pub symbol_gen: SymbolGen, -} - -impl Default for Parser { - fn default() -> Self { - Self { - commands: Default::default(), - actions: Default::default(), - exprs: Default::default(), - symbol_gen: SymbolGen::new("$".to_string()), - } - } -} - -pub fn parse_program( - filename: Option, - input: &str, - parser: &Parser, -) -> Result, ParseError> { - let sexps = all_sexps(Context::new(filename, input))?; - let nested = map_fallible(&sexps, parser, commands)?; - Ok(nested.into_iter().flatten().collect()) -} - -// currently only used for testing, but no reason it couldn't be used elsewhere later -pub fn parse_expr( - filename: Option, - input: &str, - parser: &Parser, -) -> Result { - let sexp = sexp(&mut Context::new(filename, input))?; - expr(&sexp, parser) -} - /// A [`Span`] contains the file name and a pair of offsets representing the start and the end. #[derive(Clone, PartialEq, Eq, Hash)] pub enum Span { @@ -172,7 +131,7 @@ impl Display for Span { // error messages are defined in the same place that they are created, // making it easier to improve errors over time. #[derive(Debug, Error)] -pub struct ParseError(Span, String); +pub struct ParseError(pub Span, pub String); impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -195,7 +154,7 @@ pub enum Sexp { } impl Sexp { - fn span(&self) -> Span { + pub fn span(&self) -> Span { match self { Sexp::Literal(_, span) => span.clone(), Sexp::Atom(_, span) => span.clone(), @@ -203,7 +162,7 @@ impl Sexp { } } - fn expect_uint(&self, e: &'static str) -> Result { + pub fn expect_uint(&self, e: &'static str) -> Result { if let Sexp::Literal(Literal::Int(x), _) = self { if *x >= 0 { return Ok(*x as usize); @@ -215,28 +174,28 @@ impl Sexp { ) } - fn expect_string(&self, e: &'static str) -> Result { + pub fn expect_string(&self, e: &'static str) -> Result { if let Sexp::Literal(Literal::String(x), _) = self { return Ok(x.to_string()); } error!(self.span(), "expected {e} to be a string literal") } - fn expect_atom(&self, e: &'static str) -> Result { + pub fn expect_atom(&self, e: &'static str) -> Result { if let Sexp::Atom(symbol, _) = self { return Ok(*symbol); } error!(self.span(), "expected {e}") } - fn expect_list(&self, e: &'static str) -> Result<&[Sexp], ParseError> { + pub fn expect_list(&self, e: &'static str) -> Result<&[Sexp], ParseError> { if let Sexp::List(sexps, _) = self { return Ok(sexps); } error!(self.span(), "expected {e}") } - fn expect_call(&self, e: &'static str) -> Result<(Symbol, &[Sexp], Span), ParseError> { + pub fn expect_call(&self, e: &'static str) -> Result<(Symbol, &[Sexp], Span), ParseError> { if let Sexp::List(sexps, span) = self { if let [Sexp::Atom(func, _), args @ ..] = sexps.as_slice() { return Ok((*func, args, span.clone())); @@ -249,586 +208,700 @@ impl Sexp { // helper for mapping a function that returns `Result` fn map_fallible( slice: &[Sexp], - parser: &Parser, - func: impl Fn(&Sexp, &Parser) -> Result, + parser: &mut Parser, + func: impl Fn(&mut Parser, &Sexp) -> Result, ) -> Result, ParseError> { slice .iter() - .map(|sexp| func(sexp, parser)) + .map(|sexp| func(parser, sexp)) .collect::>() } -// helper for parsing a list of options -fn options(sexps: &[Sexp]) -> Result, ParseError> { - fn option_name(sexp: &Sexp) -> Option<&str> { - if let Ok(symbol) = sexp.expect_atom("") { - let s: &str = symbol.into(); - if let Some(':') = s.chars().next() { - return Some(s); - } - } - None +pub trait Macro { + fn name(&self) -> Symbol; + fn parse(&self, args: &[Sexp], span: Span, parser: &mut Parser) -> Result; +} + +pub struct SimpleMacro Result>(Symbol, F); + +impl SimpleMacro +where + F: Fn(&[Sexp], Span, &mut Parser) -> Result, +{ + pub fn new(head: &str, f: F) -> Self { + Self(head.into(), f) } +} - let mut out = Vec::new(); - let mut i = 0; - while i < sexps.len() { - let Some(key) = option_name(&sexps[i]) else { - return error!(sexps[i].span(), "option key must start with ':'"); - }; - i += 1; +impl Macro for SimpleMacro +where + F: Fn(&[Sexp], Span, &mut Parser) -> Result, +{ + fn name(&self) -> Symbol { + self.0 + } - let start = i; - while i < sexps.len() && option_name(&sexps[i]).is_none() { - i += 1; + fn parse(&self, args: &[Sexp], span: Span, parser: &mut Parser) -> Result { + self.1(args, span, parser) + } +} + +#[derive(Clone)] +pub struct Parser { + commands: HashMap>>>, + actions: HashMap>>>, + exprs: HashMap>>, + pub symbol_gen: SymbolGen, +} + +impl Default for Parser { + fn default() -> Self { + Self { + commands: Default::default(), + actions: Default::default(), + exprs: Default::default(), + symbol_gen: SymbolGen::new("$".to_string()), } - out.push((key, &sexps[start..i])); } - Ok(out) } -fn commands(sexp: &Sexp, parser: &Parser) -> Result, ParseError> { - let (head, tail, span) = sexp.expect_call("command")?; +impl Parser { + pub fn get_program_from_string( + &mut self, + filename: Option, + input: &str, + ) -> Result, ParseError> { + let sexps = all_sexps(Context::new(filename, input))?; + let nested = map_fallible(&sexps, self, Self::parse_command)?; + Ok(nested.into_iter().flatten().collect()) + } - if let Some(func) = parser.commands.get(&head) { - return func(tail, span, parser); + // currently only used for testing, but no reason it couldn't be used elsewhere later + pub fn get_expr_from_string( + &mut self, + filename: Option, + input: &str, + ) -> Result { + let sexp = sexp(&mut Context::new(filename, input))?; + self.parse_expr(&sexp) } - Ok(match head.into() { - "set-option" => match tail { - [name, value] => vec![Command::SetOption { - name: name.expect_atom("option name")?, - value: expr(value, parser)?, - }], - _ => return error!(span, "usage: (set-option )"), - }, - "sort" => match tail { - [name] => vec![Command::Sort(span, name.expect_atom("sort name")?, None)], - [name, call] => { - let (func, args, _) = call.expect_call("container sort declaration")?; - vec![Command::Sort( - span, - name.expect_atom("sort name")?, - Some((func, map_fallible(args, parser, expr)?)), - )] - } - _ => { - return error!( - span, - "usages:\n(sort )\n(sort ( *))" - ) - } - }, - "datatype" => match tail { - [name, variants @ ..] => vec![Command::Datatype { - span, - name: name.expect_atom("sort name")?, - variants: map_fallible(variants, parser, variant)?, - }], - _ => return error!(span, "usage: (datatype *)"), - }, - "datatype*" => vec![Command::Datatypes { - span, - datatypes: map_fallible(tail, parser, rec_datatype)?, - }], - "function" => match tail { - [name, inputs, output, rest @ ..] => vec![Command::Function { - name: name.expect_atom("function name")?, - schema: schema(inputs, output)?, - merge: match options(rest)?.as_slice() { - [(":no-merge", [])] => None, - [(":merge", [e])] => Some(expr(e, parser)?), - [] => return error!(span, "functions are required to specify merge behaviour"), - _ => return error!(span, "could not parse function options"), - }, - span, - }], - _ => { - let a = "(function (*) :merge )"; - let b = "(function (*) :no-merge)"; - return error!(span, "usages:\n{a}\n{b}"); - } - }, - "constructor" => match tail { - [name, inputs, output, rest @ ..] => { - let mut cost = None; - let mut unextractable = false; - match options(rest)?.as_slice() { - [] => {} - [(":unextractable", [])] => unextractable = true, - [(":cost", [c])] => cost = Some(c.expect_uint("cost")?), - _ => return error!(span, "could not parse constructor options"), - } + pub fn add_command_macro(&mut self, ma: Arc>>) { + self.commands.insert(ma.name(), ma); + } + + pub fn add_action_macro(&mut self, ma: Arc>>) { + self.actions.insert(ma.name(), ma); + } + + pub fn add_expr_macro(&mut self, ma: Arc>) { + self.exprs.insert(ma.name(), ma); + } + + pub fn parse_command(&mut self, sexp: &Sexp) -> Result, ParseError> { + let (head, tail, span) = sexp.expect_call("command")?; + + if let Some(macr0) = self.commands.get(&head).cloned() { + return macr0.parse(tail, span, self); + } - vec![Command::Constructor { + Ok(match head.into() { + "set-option" => match tail { + [name, value] => vec![Command::SetOption { + name: name.expect_atom("option name")?, + value: self.parse_expr(value)?, + }], + _ => return error!(span, "usage: (set-option )"), + }, + "sort" => match tail { + [name] => vec![Command::Sort(span, name.expect_atom("sort name")?, None)], + [name, call] => { + let (func, args, _) = call.expect_call("container sort declaration")?; + vec![Command::Sort( + span, + name.expect_atom("sort name")?, + Some((func, map_fallible(args, self, Self::parse_expr)?)), + )] + } + _ => { + return error!( + span, + "usages:\n(sort )\n(sort ( *))" + ) + } + }, + "datatype" => match tail { + [name, variants @ ..] => vec![Command::Datatype { span, - name: name.expect_atom("constructor name")?, - schema: schema(inputs, output)?, - cost, - unextractable, - }] - } - _ => { - let a = "(constructor (*) )"; - let b = "(constructor (*) :cost )"; - let c = "(constructor (*) :unextractable)"; - return error!(span, "usages:\n{a}\n{b}\n{c}"); - } - }, - "relation" => match tail { - [name, inputs] => vec![Command::Relation { + name: name.expect_atom("sort name")?, + variants: map_fallible(variants, self, Self::variant)?, + }], + _ => return error!(span, "usage: (datatype *)"), + }, + "datatype*" => vec![Command::Datatypes { span, - name: name.expect_atom("relation name")?, - inputs: map_fallible(inputs.expect_list("input sorts")?, parser, |sexp, _| { - sexp.expect_atom("input sort") - })?, + datatypes: map_fallible(tail, self, Self::rec_datatype)?, }], - _ => return error!(span, "usage: (relation (*))"), - }, - "ruleset" => match tail { - [name] => vec![Command::AddRuleset(name.expect_atom("ruleset name")?)], - _ => return error!(span, "usage: (ruleset )"), - }, - "unstable-combined-ruleset" => match tail { - [name, subrulesets @ ..] => vec![Command::UnstableCombinedRuleset( - name.expect_atom("combined ruleset name")?, - map_fallible(subrulesets, parser, |sexp, _| { - sexp.expect_atom("subruleset name") - })?, - )], - _ => { - return error!( + "function" => match tail { + [name, inputs, output, rest @ ..] => vec![Command::Function { + name: name.expect_atom("function name")?, + schema: self.parse_schema(inputs, output)?, + merge: match self.parse_options(rest)?.as_slice() { + [(":no-merge", [])] => None, + [(":merge", [e])] => Some(self.parse_expr(e)?), + [] => { + return error!( + span, + "functions are required to specify merge behaviour" + ) + } + _ => return error!(span, "could not parse function options"), + }, span, - "usage: (unstable-combined-ruleset *)" - ) - } - }, - "rule" => match tail { - [lhs, rhs, rest @ ..] => { - let body = map_fallible(lhs.expect_list("rule query")?, parser, fact)?; - let head = map_fallible(rhs.expect_list("rule actions")?, parser, actions)?; - let head = GenericActions(head.into_iter().flatten().collect()); - - let mut ruleset = "".into(); - let mut name = "".into(); - for option in options(rest)? { - match option { - (":ruleset", [r]) => ruleset = r.expect_atom("ruleset name")?, - (":name", [s]) => name = s.expect_string("rule name")?.into(), - _ => return error!(span, "could not parse rule option"), - } + }], + _ => { + let a = "(function (*) :merge )"; + let b = "(function (*) :no-merge)"; + return error!(span, "usages:\n{a}\n{b}"); } - - vec![Command::Rule { - ruleset, - name, - rule: Rule { span, head, body }, - }] - } - _ => return error!(span, "usage: (rule (*) (*)