diff options
author | Greg Brown <gmb60@cam.ac.uk> | 2021-04-27 13:07:27 +0100 |
---|---|---|
committer | Greg Brown <gmb60@cam.ac.uk> | 2021-04-27 13:07:27 +0100 |
commit | 51dcf19eb6f8d2127f3dedfc1d7d5326d7a8017b (patch) | |
tree | 9d66a63fa1d017cad1b4483399950bf216d58bf8 /src/chomp/ast.rs | |
parent | 61eb62dde6a62cf19d33d5c689b0c3a4f36d93c3 (diff) |
Start rewrite using query system.queries
Diffstat (limited to 'src/chomp/ast.rs')
-rw-r--r-- | src/chomp/ast.rs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/chomp/ast.rs b/src/chomp/ast.rs new file mode 100644 index 0000000..dbbcb5d --- /dev/null +++ b/src/chomp/ast.rs @@ -0,0 +1,146 @@ +use std::rc::Rc; + +use super::Context; + +#[derive(Debug)] +pub struct Lambda(pub Rc<Tree>, pub Rc<Tree>); + +#[derive(Debug)] +pub enum Tree { + Epsilon, + Bottom, + Identity, + Literal(String), + Variable, + Cat(Rc<Tree>, Rc<Tree>), + Alt(Rc<Tree>, Rc<Tree>), + Fix(Rc<Tree>), + Call(Rc<Tree>, Rc<Tree>), + Lambda(Lambda), + Let(Rc<Tree>, Rc<Tree>, Rc<Tree>), +} + +impl Tree { + pub fn try_as_lambda(&self) -> Option<&Lambda> { + if let Self::Lambda(v) = self { + Some(v) + } else { + None + } + } +} + +#[derive(Clone, Debug)] +pub enum TranslateError { + CallFunNotLambda(Rc<Tree>), +} + +pub(crate) fn substitute( + ctx: &mut Context, + tree: &Rc<Tree>, + from: &Rc<Tree>, + into: &Rc<Tree>, +) -> Option<Rc<Tree>> { + if Rc::ptr_eq(tree, from) { + return Some(into.clone()); + } + + match tree.as_ref() { + Tree::Epsilon | Tree::Bottom | Tree::Identity | Tree::Literal(_) | Tree::Variable => None, + Tree::Cat(front, back) => { + let new_front = ctx.substitute(front, from, into); + let new_back = ctx.substitute(back, from, into); + let (front, back) = match (new_front, new_back) { + (None, None) => return None, + (None, Some(back)) => (front.clone(), back), + (Some(front), None) => (front, back.clone()), + (Some(front), Some(back)) => (front, back), + }; + Some(ctx.tree_interner.cat(front, back)) + } + Tree::Alt(left, right) => { + let new_left = ctx.substitute(left, from, into); + let new_right = ctx.substitute(right, from, into); + let (left, right) = match (new_left, new_right) { + (None, None) => return None, + (None, Some(right)) => (left.clone(), right), + (Some(left), None) => (left, right.clone()), + (Some(left), Some(right)) => (left, right), + }; + Some(ctx.tree_interner.alt(left, right)) + } + Tree::Fix(inner) => { + let inner = ctx.substitute(inner, from, into)?; + Some(ctx.tree_interner.fix(inner)) + } + Tree::Call(fun, arg) => { + let new_fun = ctx.substitute(fun, from, into); + let new_arg = ctx.substitute(arg, from, into); + let (fun, arg) = match (new_fun, new_arg) { + (None, None) => return None, + (None, Some(arg)) => (fun.clone(), arg), + (Some(fun), None) => (fun, arg.clone()), + (Some(fun), Some(arg)) => (fun, arg), + }; + Some(ctx.tree_interner.call(fun, arg)) + } + Tree::Lambda(Lambda(arg, inner)) => { + let inner = ctx.substitute(inner, from, into)?; + Some(Rc::new(Tree::Lambda(Lambda(arg.clone(), inner)))) + } + Tree::Let(bind, arg, inner) => { + let new_bind = ctx.substitute(bind, from, into); + let new_inner = ctx.substitute(inner, from, into); + let (bind, inner) = match (new_bind, new_inner) { + (None, None) => return None, + (None, Some(inner)) => (bind.clone(), inner), + (Some(bind), None) => (bind, inner.clone()), + (Some(bind), Some(inner)) => (bind, inner), + }; + Some(Rc::new(Tree::Let(bind, arg.clone(), inner))) + } + } +} + +pub(crate) fn translate( + ctx: &mut Context, + tree: &Rc<Tree>, +) -> Result<Option<Rc<Tree>>, TranslateError> { + match tree.as_ref() { + Tree::Epsilon + | Tree::Bottom + | Tree::Identity + | Tree::Literal(_) + | Tree::Variable + | Tree::Cat(_, _) + | Tree::Alt(_, _) + | Tree::Fix(_) + | Tree::Lambda(_) => Ok(None), + Tree::Call(fun, arg) => { + let fun = ctx.translate(fun)?.unwrap_or_else(|| fun.clone()); + let lambda = fun + .try_as_lambda() + .ok_or_else(|| TranslateError::CallFunNotLambda(fun.clone()))?; + let mut out = ctx + .substitute(&lambda.1, &lambda.0, arg) + .unwrap_or_else(|| lambda.1.clone()); + loop { + match ctx.translate(&out)? { + Some(tree) => out = tree, + None => return Ok(Some(out)), + } + } + } + Tree::Let(bind, arg, inner) => { + let mut out = ctx + .substitute(inner, arg, bind) + .unwrap_or_else(|| inner.clone()); + loop { + match ctx.translate(&out)? { + Some(tree) => out = tree, + None => return Ok(Some(out)), + } + } + } + } +} |