From eba0e9d2239d22a4fd13cb33c345567f2f49d2df Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Sun, 5 Jan 2025 00:02:31 -0800 Subject: [PATCH 1/6] Polish the macro interface --- src/ast/desugar.rs | 2 +- src/ast/parse.rs | 1139 +++++++++++++++++++++++--------------------- src/lib.rs | 2 +- src/termdag.rs | 4 +- 4 files changed, 601 insertions(+), 546 deletions(-) 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..3c4db857 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 { @@ -250,11 +209,11 @@ impl Sexp { fn map_fallible( slice: &[Sexp], parser: &Parser, - func: impl Fn(&Sexp, &Parser) -> Result, + func: impl Fn(&Parser, &Sexp) -> Result, ) -> Result, ParseError> { slice .iter() - .map(|sexp| func(sexp, parser)) + .map(|sexp| func(parser, sexp)) .collect::>() } @@ -287,548 +246,642 @@ fn options(sexps: &[Sexp]) -> Result, ParseError> { Ok(out) } -fn commands(sexp: &Sexp, parser: &Parser) -> Result, ParseError> { - let (head, tail, span) = sexp.expect_call("command")?; +fn schema(input: &Sexp, output: &Sexp) -> Result { + Ok(Schema { + input: input + .expect_list("input sorts")? + .iter() + .map(|sexp| sexp.expect_atom("input sort")) + .collect::>()?, + output: output.expect_atom("output sort")?, + }) +} + +pub trait Macro { + fn get_head(&self) -> String; + fn parse(&self, args: &[Sexp], span: Span, parser: &Parser) -> Result; +} + +pub struct SimpleMacro Result>(String, F); - if let Some(func) = parser.commands.get(&head) { - return func(tail, span, parser); +impl SimpleMacro +where + F: Fn(&[Sexp], Span, &Parser) -> Result, +{ + pub fn new(head: &str, f: F) -> Self { + Self(head.into(), f) } +} - 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"), - } +impl Macro for SimpleMacro +where + F: Fn(&[Sexp], Span, &Parser) -> Result, +{ + fn get_head(&self) -> String { + self.0.clone() + } + + fn parse(&self, args: &[Sexp], span: Span, parser: &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()), + } + } +} + +impl Parser { + pub fn get_program_from_string( + &self, + filename: Option, + input: &str, + ) -> Result, ParseError> { + let sexps = all_sexps(Context::new(filename, input))?; + let nested = map_fallible(&sexps, self, Self::commands)?; + Ok(nested.into_iter().flatten().collect()) + } + + // currently only used for testing, but no reason it couldn't be used elsewhere later + pub fn get_expr_from_string( + &self, + filename: Option, + input: &str, + ) -> Result { + let sexp = sexp(&mut Context::new(filename, input))?; + self.expr(&sexp) + } + + pub fn add_command_macro(&mut self, ma: Arc>>) { + self.commands.insert(ma.get_head().into(), ma); + } + + pub fn add_action_macro(&mut self, ma: Arc>>) { + self.actions.insert(ma.get_head().into(), ma); + } + + pub fn add_expr_macro(&mut self, ma: Arc>) { + self.exprs.insert(ma.get_head().into(), ma); + } + + pub fn commands(&self, sexp: &Sexp) -> Result, ParseError> { + let (head, tail, span) = sexp.expect_call("command")?; - vec![Command::Constructor { + if let Some(macr0) = self.commands.get(&head) { + return macr0.parse(tail, span, self); + } + + Ok(match head.into() { + "set-option" => match tail { + [name, value] => vec![Command::SetOption { + name: name.expect_atom("option name")?, + value: self.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::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: schema(inputs, output)?, + merge: match options(rest)?.as_slice() { + [(":no-merge", [])] => None, + [(":merge", [e])] => Some(self.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}"); } + }, + "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"), + } - vec![Command::Rule { - ruleset, - name, - rule: Rule { span, head, body }, - }] - } - _ => return error!(span, "usage: (rule (*) (*)