summaryrefslogtreecommitdiff
path: root/src/nibble/convert.rs
diff options
context:
space:
mode:
authorGreg Brown <gmb60@cam.ac.uk>2021-01-06 14:56:11 +0000
committerGreg Brown <gmb60@cam.ac.uk>2021-01-06 14:56:11 +0000
commitdc10a278cca74d737e4af0fe034a1caa8abb291d (patch)
tree3fbb9efc0bdc9fd3bc24d2ad743585071b7006a4 /src/nibble/convert.rs
parent4fb6b740e79c1942fd0bfde9b167ea273c7d0b4b (diff)
Restructure code base to separate compilation phases.
Diffstat (limited to 'src/nibble/convert.rs')
-rw-r--r--src/nibble/convert.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/nibble/convert.rs b/src/nibble/convert.rs
new file mode 100644
index 0000000..3e47208
--- /dev/null
+++ b/src/nibble/convert.rs
@@ -0,0 +1,165 @@
+use std::collections::HashMap;
+
+use syn::punctuated::Pair;
+
+use crate::chomp::ast;
+
+use super::cst::{Alt, Call, Cat, Fix, Ident, ParenExpression, Term};
+
+#[derive(Clone, Copy, Debug)]
+pub enum Binding {
+ Variable(usize),
+ Parameter(usize),
+ Global,
+}
+
+#[derive(Debug)]
+pub struct Context {
+ names: HashMap<String, Binding>,
+ vars: usize,
+}
+
+impl Context {
+ pub fn new<I: IntoIterator<Item = Ident>>(globals: &[Ident], params: I) -> Self {
+ let mut names = HashMap::new();
+ for global in globals {
+ names.insert(global.to_string(), Binding::Global);
+ }
+
+ for (index, param) in params.into_iter().enumerate() {
+ names.insert(param.to_string(), Binding::Parameter(index));
+ }
+
+ Self { names, vars: 0 }
+ }
+
+ pub fn lookup(&self, name: &Ident) -> Option<Binding> {
+ // we make variable binding cheaper by inserting wrong and pulling right.
+ match self.names.get(&name.to_string()).copied() {
+ Some(Binding::Variable(index)) => Some(Binding::Variable(self.vars - index - 1)),
+ x => x,
+ }
+ }
+
+ pub fn with_variable<F: FnOnce(&mut Self) -> R, R>(&mut self, name: &Ident, f: F) -> R {
+ let old = self
+ .names
+ .insert(name.to_string(), Binding::Variable(self.vars));
+
+ // we make variable binding cheaper by inserting wrong and pulling right.
+ // we should increment all values in names instead, but that's slow
+ self.vars += 1;
+ let res = f(self);
+ self.vars -= 1;
+
+ if let Some(val) = old {
+ self.names.insert(name.to_string(), val);
+ } else {
+ self.names.remove(&name.to_string());
+ }
+
+ res
+ }
+}
+
+pub trait Convert {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression>;
+}
+
+impl Convert for Ident {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ let span = self.span();
+
+ match context.lookup(&self)? {
+ Binding::Variable(index) => Some(ast::Variable::new(self, index).into()),
+ Binding::Parameter(index) => Some(ast::Parameter::new(self, index).into()),
+ Binding::Global => Some(ast::Call::new(self, Vec::new(), Some(span)).into()),
+ }
+ }
+}
+
+impl Convert for Call {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ let span = self.span();
+ let args = self
+ .args
+ .into_iter()
+ .map(|arg| arg.convert(context))
+ .collect::<Option<_>>()?;
+ Some(ast::Call::new(self.name, args, span).into())
+ }
+}
+
+impl Convert for Fix {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ let span = self.span();
+ let expr = self.expr;
+ let inner = context.with_variable(&self.arg, |context| expr.convert(context))?;
+ Some(ast::Fix::new(self.arg, inner, span).into())
+ }
+}
+
+impl Convert for ParenExpression {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ self.expr.convert(context)
+ }
+}
+
+impl Convert for Term {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ match self {
+ Self::Epsilon(e) => Some(e.into()),
+ Self::Ident(i) => i.convert(context),
+ Self::Literal(l) => Some(l.into()),
+ Self::Call(c) => c.convert(context),
+ Self::Fix(f) => f.convert(context),
+ Self::Parens(p) => p.convert(context),
+ }
+ }
+}
+
+impl Convert for Cat {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ let mut iter = self.0.into_pairs();
+ let mut out = match iter.next().unwrap() {
+ Pair::Punctuated(t, p) => (t.convert(context)?, Some(p)),
+ Pair::End(t) => (t.convert(context)?, None),
+ };
+
+ for pair in iter {
+ let (fst, punct) = out;
+ out = match pair {
+ Pair::Punctuated(t, p) => (
+ ast::Cat::new(fst, punct, t.convert(context)?).into(),
+ Some(p),
+ ),
+ Pair::End(t) => (ast::Cat::new(fst, punct, t.convert(context)?).into(), None),
+ };
+ }
+
+ Some(out.0)
+ }
+}
+
+impl Convert for Alt {
+ fn convert(self, context: &mut Context) -> Option<ast::Expression> {
+ let mut iter = self.0.into_pairs();
+ let mut out = match iter.next().unwrap() {
+ Pair::Punctuated(t, p) => (t.convert(context)?, Some(p)),
+ Pair::End(t) => (t.convert(context)?, None),
+ };
+
+ for pair in iter {
+ let (fst, punct) = out;
+ out = match pair {
+ Pair::Punctuated(t, p) => (
+ ast::Alt::new(fst, punct, t.convert(context)?).into(),
+ Some(p),
+ ),
+ Pair::End(t) => (ast::Alt::new(fst, punct, t.convert(context)?).into(), None),
+ };
+ }
+
+ Some(out.0)
+ }
+}