diff options
author | Greg Brown <gmb60@cam.ac.uk> | 2021-04-18 11:50:06 +0100 |
---|---|---|
committer | Greg Brown <gmb60@cam.ac.uk> | 2021-04-22 09:45:51 +0100 |
commit | 081560e2080426bc61e75624f3af96c97aa6c011 (patch) | |
tree | 21f2f97d31a8b15519908337179d006d392dd4aa /src/nibble/convert.rs | |
parent | 449695dcf87d26b0d06a51ca27bbc8214338f954 (diff) |
Update AST and parser for lambda expressions.
Diffstat (limited to 'src/nibble/convert.rs')
-rw-r--r-- | src/nibble/convert.rs | 210 |
1 files changed, 106 insertions, 104 deletions
diff --git a/src/nibble/convert.rs b/src/nibble/convert.rs index cef3159..f79e1be 100644 --- a/src/nibble/convert.rs +++ b/src/nibble/convert.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt, mem}; +use std::{fmt, mem}; use proc_macro2::Span; use syn::{punctuated::Pair, Token}; @@ -8,62 +8,42 @@ use crate::chomp::{ Name, }; -use super::cst::{Alt, Call, Cat, Fix, Ident, Labelled, ParenExpression, Term}; - -#[derive(Clone, Copy, Debug)] -pub enum Binding { - Variable(usize), - Parameter(usize), - Global, -} +use super::cst::{Alt, Call, Cat, Expression, Fix, Ident, Labelled, Lambda, ParenExpression, Term}; #[derive(Debug, Default)] pub struct Context { - names: HashMap<String, Binding>, - vars: usize, + bindings: Vec<Name>, } impl Context { - pub fn new<I: IntoIterator<Item = Name>>(globals: &[Name], 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 new() -> Self { + Self::default() } - pub fn lookup(&self, name: &Name) -> 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)), - Some(Binding::Parameter(index)) => Some(Binding::Parameter(index)), - Some(Binding::Global) => Some(Binding::Global), - None => None, - } + pub fn lookup(&self, name: &Name) -> Option<usize> { + self.bindings + .iter() + .enumerate() + .rfind(|(_, n)| *n == name) + .map(|(idx, _)| idx) } - pub fn with_variable<F: FnOnce(&mut Self) -> R, R>(&mut self, name: &Name, f: F) -> R { - let old = self - .names - .insert(name.to_string(), Binding::Variable(self.vars)); + pub fn push_variable(&mut self, name: Name) { + self.bindings.push(name); + } - // 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; + pub fn with_variable<F: FnOnce(&mut Self) -> R, R>(&mut self, name: Name, f: F) -> R { + self.bindings.push(name); 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()); - } + self.bindings.pop(); + res + } + pub fn with_variables<I: IntoIterator<Item = Name>, F: FnOnce(&mut Self) -> R, R>(&mut self, names: I, f: F) -> R { + let len = self.bindings.len(); + self.bindings.extend(names); + let res = f(self); + self.bindings.resize_with(len, || unreachable!()); res } } @@ -73,6 +53,8 @@ pub enum ConvertError { UndeclaredName(Box<Name>), EmptyCat(Option<Span>), EmptyAlt(Option<Span>), + EmptyCall(Option<Span>), + MissingArgs(Option<Span>), } impl From<ConvertError> for syn::Error { @@ -80,7 +62,10 @@ impl From<ConvertError> for syn::Error { let msg = e.to_string(); let span = match e { ConvertError::UndeclaredName(name) => name.span(), - ConvertError::EmptyCat(span) | ConvertError::EmptyAlt(span) => span, + ConvertError::EmptyCat(span) + | ConvertError::EmptyAlt(span) + | ConvertError::EmptyCall(span) + | ConvertError::MissingArgs(span) => span, }; Self::new(span.unwrap_or_else(Span::call_site), msg) @@ -99,6 +84,12 @@ impl fmt::Display for ConvertError { Self::EmptyAlt(_) => { write!(f, "alternation has no elements") } + Self::EmptyCall(_) => { + write!(f, "call has no function") + } + Self::MissingArgs(_) => { + write!(f, "call has no arguments") + } } } } @@ -114,49 +105,13 @@ impl Convert for Ident { let span = Some(self.span()); let name = self.into(); - let binding = context + let index = context .lookup(&name) .ok_or_else(|| ConvertError::UndeclaredName(Box::new(name.clone())))?; - Ok(match binding { - Binding::Variable(index) => NamedExpression { - name: Some(name), - expr: ast::Variable { index }.into(), - span, - }, - Binding::Parameter(index) => NamedExpression { - name: Some(name), - expr: ast::Parameter { index }.into(), - span, - }, - Binding::Global => NamedExpression { - name: None, - expr: ast::Call { - name, - args: Vec::new(), - } - .into(), - span, - }, - }) - } -} - -impl Convert for Call { - fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { - let span = self.span(); - let args = self - .args - .into_iter() - .map(|arg| arg.convert(context)) - .collect::<Result<_, _>>()?; Ok(NamedExpression { - name: None, - expr: ast::Call { - name: self.name.into(), - args, - } - .into(), + name: Some(name), + expr: ast::Variable { index }.into(), span, }) } @@ -165,13 +120,10 @@ impl Convert for Call { impl Convert for Fix { fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { let span = self.span(); - let expr = self.expr; - let arg = self.arg.into(); - let inner = context.with_variable(&arg, |context| expr.convert(context))?; + let inner = self.expr.convert(context)?; Ok(NamedExpression { name: None, expr: ast::Fix { - arg: Some(arg), inner: Box::new(inner), } .into(), @@ -200,17 +152,43 @@ impl Convert for Term { expr: l.value().into(), span: Some(l.span()), }), - Self::Call(c) => c.convert(context), Self::Fix(f) => f.convert(context), Self::Parens(p) => p.convert(context), } } } +impl Convert for Call { + fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { + let span = self.span(); + let mut iter = self.0.into_iter(); + let on = iter + .next() + .ok_or_else(|| ConvertError::EmptyCall(span))? + .convert(context)?; + let args = iter + .map(|arg| arg.convert(context)) + .collect::<Result<Vec<_>, _>>()?; + if args.is_empty() { + Err(ConvertError::MissingArgs(span)) + } else { + Ok(NamedExpression { + name: None, + expr: ast::Call { + on: Box::new(on), + args, + } + .into(), + span, + }) + } + } +} + impl Convert for Cat { fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { fn convert_pair( - pair: Pair<Term, Token![.]>, + pair: Pair<Call, Token![.]>, context: &mut Context, ) -> Result<(NamedExpression, Option<Span>), ConvertError> { match pair { @@ -227,18 +205,17 @@ impl Convert for Cat { .ok_or(ConvertError::EmptyCat(span)) .and_then(|pair| convert_pair(pair, context))?; - let mut rest = iter.map(|pair| { - convert_pair(pair, context).map(|(snd, p)| (mem::replace(&mut punct, p), snd)) - }); + let mut rest = iter + .map(|pair| { + convert_pair(pair, context).map(|(snd, p)| (mem::replace(&mut punct, p), snd)) + }) + .peekable(); - if let Some(res) = rest.next() { - let (punct, second) = res?; + if let Some(_) = rest.peek() { Ok(NamedExpression { name: None, expr: ast::Cat { first: Box::new(first), - punct, - second: Box::new(second), rest: rest.collect::<Result<_, _>>()?, } .into(), @@ -284,18 +261,17 @@ impl Convert for Alt { .ok_or(ConvertError::EmptyAlt(span)) .and_then(|pair| convert_pair(pair, context))?; - let mut rest = iter.map(|pair| { - convert_pair(pair, context).map(|(snd, p)| (mem::replace(&mut punct, p), snd)) - }); + let mut rest = iter + .map(|pair| { + convert_pair(pair, context).map(|(snd, p)| (mem::replace(&mut punct, p), snd)) + }) + .peekable(); - if let Some(res) = rest.next() { - let (punct, second) = res?; + if let Some(_) = rest.peek() { Ok(NamedExpression { name: None, expr: ast::Alt { first: Box::new(first), - punct, - second: Box::new(second), rest: rest.collect::<Result<_, _>>()?, } .into(), @@ -306,3 +282,29 @@ impl Convert for Alt { } } } + +impl Convert for Lambda { + fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { + let span = self.span(); + let mut names = self.args.into_iter().map(Name::from); + let expr = self.expr; + let inner = context.with_variables(names.clone(), |ctx| expr.convert(ctx))?; + let first = names.next().unwrap(); + let rest = names.collect(); + Ok(NamedExpression { + name: None, + expr: ast::Lambda { first, rest, inner: Box::new(inner)}.into(), + span, + }) + + } +} + +impl Convert for Expression { + fn convert(self, context: &mut Context) -> Result<NamedExpression, ConvertError> { + match self { + Expression::Alt(a) => a.convert(context), + Expression::Lambda(l) => l.convert(context), + } + } +} |