use std::{
convert::TryFrom,
collections::HashMap,
};
use crate::common::{
stamp::stamp,
span::{Span, Spanned},
};
use crate::compiler::{
ast::{AST, ASTPattern, ArgPattern},
syntax::Syntax
};
type Bindings = HashMap<String, Spanned<AST>>;
#[derive(Debug, Clone)]
pub struct Rule {
pub arg_pat: Spanned<ArgPattern>,
pub tree: Spanned<AST>,
}
impl Rule {
pub fn new(
arg_pat: Spanned<ArgPattern>,
tree: Spanned<AST>,
) -> Result<Rule, Syntax> {
if Rule::keywords(&arg_pat).len() == 0 {
return Err(Syntax::error(
"Syntactic macro must have at least one pseudokeyword",
&arg_pat.span,
));
}
Ok(Rule { arg_pat, tree })
}
pub fn keywords(arg_pat: &Spanned<ArgPattern>) -> Vec<String> {
match &arg_pat.item {
ArgPattern::Group(pats) => {
let mut keywords = vec![];
for pat in pats { keywords.append(&mut Rule::keywords(&pat)) }
keywords
},
ArgPattern::Keyword(name) => vec![name.clone()],
_ => vec![],
}
}
pub fn merge_safe(base: &mut Bindings, new: Bindings, def: Span) -> Result<(), Syntax> {
let collision = Syntax::error(
"Variable has already been declared in syntactic macro argument pattern", &def
);
for (n, t) in new {
if base.contains_key(&n) { return Err(collision); }
else { base.insert(n, t); }
}
Ok(())
}
pub fn bind(arg_pat: &Spanned<ArgPattern>, mut reversed_form: &mut Vec<Spanned<AST>>)
-> Option<Result<Bindings, Syntax>> {
match &arg_pat.item {
ArgPattern::Keyword(expected) => match reversed_form.pop()?.item {
AST::Symbol(name) if &Rule::remove_tag(&name) == expected => {
Some(Ok(HashMap::new()))
},
_ => None,
},
ArgPattern::Symbol(symbol) => Some(Ok(
vec![(symbol.clone(), reversed_form.pop()?)]
.into_iter().collect()
)),
ArgPattern::Group(pats) => {
let mut bindings = HashMap::new();
for pat in pats {
let span = pat.span.clone();
let new = match Rule::bind(&pat, &mut reversed_form)? {
Ok(matched) => matched,
mismatch @ Err(_) => return Some(mismatch),
};
if let Err(collision) = Rule::merge_safe(&mut bindings, new, span) {
return Some(Err(collision));
}
}
Some(Ok(bindings))
},
}
}
pub fn remove_tag(base: &str) -> String {
base.split("#").collect::<Vec<&str>>()[0].to_string()
}
pub fn unique_tag(base: String, bindings: &Bindings) -> String {
let mut tries = 0;
for _ in 0..1024 {
let stamp = stamp(tries);
let modified = format!("{}#{}", base, stamp);
if !bindings.contains_key(&modified) {
return modified;
}
tries += 1;
}
panic!("Generated 1024 new unique identifiers for macro expansion, but all were already in use!");
}
pub fn resolve_symbol(name: String, span: Span, bindings: &mut Bindings) -> Spanned<AST> {
if let Some(bound_tree) = bindings.get(&name) {
bound_tree.clone()
} else {
let unique = Rule::unique_tag(name.clone(), bindings);
let spanned = Spanned::new(AST::Symbol(unique.clone()), span.clone());
bindings.insert(name, spanned);
Spanned::new(AST::Symbol(unique), span)
}
}
pub fn expand_pattern(
pattern: Spanned<ASTPattern>,
bindings: &mut Bindings,
) -> Result<Spanned<ASTPattern>, Syntax> {
Ok(
match pattern.item {
ASTPattern::Symbol(name) => {
let span = pattern.span.clone();
Rule::resolve_symbol(name, pattern.span, bindings)
.map(ASTPattern::try_from)
.map_err(|s| Syntax::error(&s, &span))?
},
ASTPattern::Data(_) => pattern,
ASTPattern::Label(name, pattern) => {
let span = pattern.span.clone();
Spanned::new(
ASTPattern::label(name, Rule::expand_pattern(*pattern, bindings)?), span,
)
},
ASTPattern::Chain(chain) => {
let span = Spanned::build(&chain);
let expanded = chain.into_iter()
.map(|b| Rule::expand_pattern(b, bindings))
.collect::<Result<Vec<_>, _>>()?;
Spanned::new(ASTPattern::Chain(expanded), span)
},
ASTPattern::Tuple(tuple) => {
let span = Spanned::build(&tuple);
let expanded = tuple.into_iter()
.map(|b| Rule::expand_pattern(b, bindings))
.collect::<Result<Vec<_>, _>>()?;
Spanned::new(ASTPattern::Tuple(expanded), span)
}
}
)
}
pub fn expand_arg_pat(
arg_pat: Spanned<ArgPattern>,
bindings: &mut Bindings,
) -> Result<Spanned<ArgPattern>, Syntax> {
Ok(
match arg_pat.item {
ArgPattern::Keyword(_) => arg_pat,
ArgPattern::Symbol(name) => {
let span = arg_pat.span.clone();
Rule::resolve_symbol(name, arg_pat.span, bindings)
.map(ArgPattern::try_from)
.map_err(|s| Syntax::error(&s, &span))?
},
ArgPattern::Group(sub_pat) => {
let span = Spanned::build(&sub_pat);
let expanded = sub_pat.into_iter()
.map(|b| Rule::expand_arg_pat(b, bindings))
.collect::<Result<Vec<_>, _>>()?;
Spanned::new(ArgPattern::Group(expanded), span)
},
}
)
}
pub fn expand(tree: Spanned<AST>, mut bindings: &mut Bindings)
-> Result<Spanned<AST>, Syntax> {
let item: AST = match tree.item {
AST::Symbol(name) => return Ok(Rule::resolve_symbol(name, tree.span.clone(), &mut bindings)),
AST::Data(_) => return Ok(tree),
AST::Block(forms) => AST::Block(
forms.into_iter()
.map(|f| Rule::expand(f, bindings))
.collect::<Result<Vec<_>, _>>()?
),
AST::Form(branches) => AST::Form(
branches.into_iter()
.map(|b| Rule::expand(b, bindings))
.collect::<Result<Vec<_>, _>>()?
),
AST::Group(expression) => AST::group(Rule::expand(*expression, bindings)?),
AST::Composition { argument, function } => {
let a = Rule::expand(*argument, bindings)?;
let f = Rule::expand(*function, bindings)?;
AST::composition(a, f)
},
AST::CSTPattern(pattern) => {
let spanned = Spanned::new(pattern, tree.span.clone());
AST::CSTPattern(Rule::expand_pattern(spanned, bindings)?.item)
},
AST::ArgPattern(arg_pat) => {
let spanned = Spanned::new(arg_pat, tree.span.clone());
AST::ArgPattern(Rule::expand_arg_pat(spanned, bindings)?.item)
},
AST::Assign { pattern, expression } => {
let p = Rule::expand_pattern(*pattern, bindings)?;
let e = Rule::expand(*expression, bindings)?;
AST::assign(p, e)
},
AST::Lambda { pattern, expression } => {
let p = Rule::expand_pattern(*pattern, bindings)?;
let e = Rule::expand(*expression, bindings)?;
AST::lambda(p, e)
},
AST::Label(kind, expression) => AST::Label(
kind, Box::new(Rule::expand(*expression, bindings)?)
),
AST::Tuple(tuple) => AST::Tuple(
tuple.into_iter()
.map(|b| Rule::expand(b, bindings))
.collect::<Result<Vec<_>, _>>()?
),
AST::Syntax { arg_pat, expression } => {
let ap = Rule::expand_arg_pat(*arg_pat, bindings)?;
let e = Rule::expand(*expression, bindings)?;
AST::syntax(ap, e);
return Err(Syntax::error(
"Nested macros are not allowed",
&tree.span,
))?;
},
AST::FFI { name, expression } => AST::ffi(
&name,
Rule::expand(*expression, bindings)?
),
};
return Ok(Spanned::new(item, tree.span));
}
}