Allow terms to have both extractors and constructors

Fixes #4
This commit is contained in:
Nick Fitzgerald
2021-10-07 16:38:47 -07:00
committed by Chris Fallin
parent fddff6ee2d
commit 7f8cb75e54
4 changed files with 420 additions and 116 deletions

View File

@@ -117,8 +117,12 @@ impl<'a> Codegen<'a> {
.unwrap(); .unwrap();
writeln!(code, "pub trait Context {{").unwrap(); writeln!(code, "pub trait Context {{").unwrap();
for term in &self.termenv.terms { for term in &self.termenv.terms {
if term.is_external() { if term.has_external_extractor() {
let ext_sig = term.to_sig(self.typeenv).unwrap(); let ext_sig = term.extractor_sig(self.typeenv).unwrap();
self.generate_trait_sig(code, " ", &ext_sig);
}
if term.has_external_constructor() {
let ext_sig = term.constructor_sig(self.typeenv).unwrap();
self.generate_trait_sig(code, " ", &ext_sig); self.generate_trait_sig(code, " ", &ext_sig);
} }
} }
@@ -241,11 +245,11 @@ impl<'a> Codegen<'a> {
// Skip terms that are enum variants or that have external // Skip terms that are enum variants or that have external
// constructors/extractors. // constructors/extractors.
if !termdata.is_constructor() || termdata.is_external() { if !termdata.has_constructor() || termdata.has_external_constructor() {
continue; continue;
} }
let sig = termdata.to_sig(self.typeenv).unwrap(); let sig = termdata.constructor_sig(self.typeenv).unwrap();
let args = sig let args = sig
.param_tys .param_tys
@@ -393,7 +397,7 @@ impl<'a> Codegen<'a> {
}; };
let outputname = self.value_name(&output); let outputname = self.value_name(&output);
let termdata = &self.termenv.terms[term.index()]; let termdata = &self.termenv.terms[term.index()];
let sig = termdata.to_sig(self.typeenv).unwrap(); let sig = termdata.constructor_sig(self.typeenv).unwrap();
assert_eq!(input_exprs.len(), sig.param_tys.len()); assert_eq!(input_exprs.len(), sig.param_tys.len());
let fallible_try = if infallible { "" } else { "?" }; let fallible_try = if infallible { "" } else { "?" };
writeln!( writeln!(
@@ -529,7 +533,7 @@ impl<'a> Codegen<'a> {
.. ..
} => { } => {
let termdata = &self.termenv.terms[term.index()]; let termdata = &self.termenv.terms[term.index()];
let sig = termdata.to_sig(self.typeenv).unwrap(); let sig = termdata.extractor_sig(self.typeenv).unwrap();
let input_values = inputs let input_values = inputs
.iter() .iter()
@@ -671,7 +675,9 @@ impl<'a> Codegen<'a> {
// variants in order to create a `match` rather than a // variants in order to create a `match` rather than a
// chain of if-lets. // chain of if-lets.
let mut edges = edges.clone(); let mut edges = edges.clone();
edges.sort_by(|e1, e2| (-e1.range.min, &e1.symbol).cmp(&(-e2.range.min, &e2.symbol))); edges.sort_by(|e1, e2| {
(-e1.range.min, &e1.symbol).cmp(&(-e2.range.min, &e2.symbol))
});
let mut i = 0; let mut i = 0;
while i < edges.len() { while i < edges.len() {

View File

@@ -400,12 +400,9 @@ impl PatternSequence {
let termdata = &termenv.terms[term.index()]; let termdata = &termenv.terms[term.index()];
let arg_tys = &termdata.arg_tys[..]; let arg_tys = &termdata.arg_tys[..];
match &termdata.kind { match &termdata.kind {
&TermKind::Declared => { TermKind::EnumVariant { variant } => {
panic!("Pattern invocation of undefined term body");
}
&TermKind::EnumVariant { variant } => {
let arg_values = let arg_values =
self.add_match_variant(input, ty, arg_tys, variant); self.add_match_variant(input, ty, arg_tys, *variant);
for (subpat, value) in args.iter().zip(arg_values.into_iter()) { for (subpat, value) in args.iter().zip(arg_values.into_iter()) {
let subpat = match subpat { let subpat = match subpat {
&TermArgPattern::Pattern(ref pat) => pat, &TermArgPattern::Pattern(ref pat) => pat,
@@ -420,16 +417,25 @@ impl PatternSequence {
); );
} }
} }
&TermKind::InternalConstructor TermKind::Decl {
| &TermKind::ExternalConstructor { .. } => { extractor_kind: None,
panic!("Should not invoke constructor in pattern"); ..
} => {
panic!("Pattern invocation of undefined term body")
} }
&TermKind::InternalExtractor { .. } => { TermKind::Decl {
panic!("Should have been expanded away"); extractor_kind: Some(ExtractorKind::InternalExtractor { .. }),
..
} => {
panic!("Should have been expanded away")
} }
&TermKind::ExternalExtractor { TermKind::Decl {
ref arg_polarity, extractor_kind:
infallible, Some(ExtractorKind::ExternalExtractor {
ref arg_polarity,
infallible,
..
}),
.. ..
} => { } => {
// Evaluate all `input` args. // Evaluate all `input` args.
@@ -469,8 +475,13 @@ impl PatternSequence {
} }
// Invoke the extractor. // Invoke the extractor.
let arg_values = self let arg_values = self.add_extract(
.add_extract(inputs, input_tys, output_tys, term, infallible); inputs,
input_tys,
output_tys,
term,
*infallible,
);
for (pat, &val) in output_pats.iter().zip(arg_values.iter()) { for (pat, &val) in output_pats.iter().zip(arg_values.iter()) {
self.gen_pattern( self.gen_pattern(
@@ -594,10 +605,13 @@ impl ExprSequence {
.push((self.gen_expr(typeenv, termenv, &*arg_expr, &vars), arg_ty)); .push((self.gen_expr(typeenv, termenv, &*arg_expr, &vars), arg_ty));
} }
match &termdata.kind { match &termdata.kind {
&TermKind::EnumVariant { variant } => { TermKind::EnumVariant { variant } => {
self.add_create_variant(&arg_values_tys[..], ty, variant) self.add_create_variant(&arg_values_tys[..], ty, *variant)
} }
&TermKind::InternalConstructor => { TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
..
} => {
self.add_construct( self.add_construct(
&arg_values_tys[..], &arg_values_tys[..],
ty, ty,
@@ -605,7 +619,10 @@ impl ExprSequence {
/* infallible = */ false, /* infallible = */ false,
) )
} }
&TermKind::ExternalConstructor { .. } => { TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
..
} => {
self.add_construct( self.add_construct(
&arg_values_tys[..], &arg_values_tys[..],
ty, ty,
@@ -613,7 +630,10 @@ impl ExprSequence {
/* infallible = */ true, /* infallible = */ true,
) )
} }
otherwise => panic!("Should have been caught by typechecking: {:?}", otherwise), TermKind::Decl {
constructor_kind: None,
..
} => panic!("Should have been caught by typechecking"),
} }
} }
} }

View File

@@ -210,9 +210,31 @@ pub enum TermKind {
/// `(A.A1 ...)` then the variant ID corresponds to `A1`. /// `(A.A1 ...)` then the variant ID corresponds to `A1`.
variant: VariantId, variant: VariantId,
}, },
/// A term declared via a `(decl ...)` form.
Decl {
/// The kind of this term's constructor, if any.
constructor_kind: Option<ConstructorKind>,
/// The kind of this term's extractor, if any.
extractor_kind: Option<ExtractorKind>,
},
}
/// The kind of a constructor for a term.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ConstructorKind {
/// A term with "internal" rules that work in the forward direction. Becomes /// A term with "internal" rules that work in the forward direction. Becomes
/// a compiled Rust function in the generated code. /// a compiled Rust function in the generated code.
InternalConstructor, InternalConstructor,
/// A term defined solely by an external constructor function.
ExternalConstructor {
/// The external name of the constructor function.
name: Sym,
},
}
/// The kind of an extractor for a term.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExtractorKind {
/// A term that defines an "extractor macro" in the LHS of a pattern. Its /// A term that defines an "extractor macro" in the LHS of a pattern. Its
/// arguments take patterns and are simply substituted with the given /// arguments take patterns and are simply substituted with the given
/// patterns when used. /// patterns when used.
@@ -228,14 +250,9 @@ pub enum TermKind {
arg_polarity: Vec<ArgPolarity>, arg_polarity: Vec<ArgPolarity>,
/// Is the external extractor infallible? /// Is the external extractor infallible?
infallible: bool, infallible: bool,
/// The position where this external extractor was declared.
pos: Pos,
}, },
/// A term defined solely by an external constructor function.
ExternalConstructor {
/// The external name of the constructor function.
name: Sym,
},
/// Declared but no body or externs associated (yet).
Declared,
} }
pub use crate::ast::ArgPolarity; pub use crate::ast::ArgPolarity;
@@ -261,40 +278,69 @@ impl Term {
self.ret_ty self.ret_ty
} }
/// Is this term a constructor? /// Is this term an enum variant?
pub fn is_constructor(&self) -> bool { pub fn is_enum_variant(&self) -> bool {
match &self.kind { matches!(self.kind, TermKind::EnumVariant { .. })
&TermKind::InternalConstructor { .. } | &TermKind::ExternalConstructor { .. } => true,
_ => false,
}
} }
fn is_declared(&self) -> bool { /// Does this term have a constructor?
matches!(self.kind, TermKind::Declared) pub fn has_constructor(&self) -> bool {
matches!(
self.kind,
TermKind::EnumVariant { .. }
| TermKind::Decl {
constructor_kind: Some(_),
..
}
)
} }
/// Is this term external? /// Does this term have an extractor?
pub fn is_external(&self) -> bool { pub fn has_extractor(&self) -> bool {
match &self.kind { matches!(
&TermKind::ExternalExtractor { .. } | &TermKind::ExternalConstructor { .. } => true, self.kind,
_ => false, TermKind::EnumVariant { .. }
} | TermKind::Decl {
extractor_kind: Some(_),
..
}
)
} }
/// Get this term's external function signature, if any. /// Is this term's extractor external?
pub fn to_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> { pub fn has_external_extractor(&self) -> bool {
matches!(
self.kind,
TermKind::Decl {
extractor_kind: Some(ExtractorKind::ExternalExtractor { .. }),
..
}
)
}
/// Is this term's constructor external?
pub fn has_external_constructor(&self) -> bool {
matches!(
self.kind,
TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
..
}
)
}
/// Get this term's extractor's external function signature, if any.
pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
match &self.kind { match &self.kind {
&TermKind::ExternalConstructor { name } => Some(ExternalSig { TermKind::Decl {
func_name: tyenv.syms[name.index()].clone(), extractor_kind:
full_name: format!("C::{}", tyenv.syms[name.index()]), Some(ExtractorKind::ExternalExtractor {
param_tys: self.arg_tys.clone(), name,
ret_tys: vec![self.ret_ty], ref arg_polarity,
infallible: true, infallible,
}), ..
&TermKind::ExternalExtractor { }),
name, ..
ref arg_polarity,
infallible,
} => { } => {
let mut arg_tys = vec![]; let mut arg_tys = vec![];
let mut ret_tys = vec![]; let mut ret_tys = vec![];
@@ -314,10 +360,30 @@ impl Term {
full_name: format!("C::{}", tyenv.syms[name.index()]), full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: arg_tys, param_tys: arg_tys,
ret_tys, ret_tys,
infallible, infallible: *infallible,
}) })
} }
&TermKind::InternalConstructor { .. } => { _ => None,
}
}
/// Get this term's constructor's external function signature, if any.
pub fn constructor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
match &self.kind {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { name }),
..
} => Some(ExternalSig {
func_name: tyenv.syms[name.index()].clone(),
full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: self.arg_tys.clone(),
ret_tys: vec![self.ret_ty],
infallible: true,
}),
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor { .. }),
..
} => {
let name = format!("constructor_{}", tyenv.syms[self.name.index()]); let name = format!("constructor_{}", tyenv.syms[self.name.index()]);
Some(ExternalSig { Some(ExternalSig {
func_name: name.clone(), func_name: name.clone(),
@@ -740,7 +806,10 @@ impl TermEnv {
name, name,
arg_tys, arg_tys,
ret_ty, ret_ty,
kind: TermKind::Declared, kind: TermKind::Decl {
constructor_kind: None,
extractor_kind: None,
},
}); });
} }
_ => {} _ => {}
@@ -813,15 +882,35 @@ impl TermEnv {
} }
}; };
let termdata = &mut self.terms[term.index()]; let termdata = &mut self.terms[term.index()];
match &termdata.kind { match &mut termdata.kind {
&TermKind::Declared => { TermKind::Decl {
termdata.kind = TermKind::InternalConstructor; constructor_kind, ..
} => {
match constructor_kind {
None => {
*constructor_kind = Some(ConstructorKind::InternalConstructor);
}
Some(ConstructorKind::InternalConstructor) => {
// OK, no error; multiple rules can apply to
// one internal constructor term.
}
Some(ConstructorKind::ExternalConstructor { .. }) => {
tyenv.report_error(
pos,
"Rule LHS root term is incorrect kind; cannot \
be external constructor"
.to_string(),
);
continue;
}
}
} }
&TermKind::InternalConstructor => { TermKind::EnumVariant { .. } => {
// OK, no error; multiple rules can apply to one internal constructor term. tyenv.report_error(
} pos,
_ => { "Rule LHS root term is incorrect kind; cannot be enum variant"
tyenv.report_error(pos, "Rule LHS root term is incorrect kind; cannot be internal constructor".to_string()); .to_string(),
);
continue; continue;
} }
} }
@@ -870,17 +959,36 @@ impl TermEnv {
extractor_call_graph.insert(sym, callees); extractor_call_graph.insert(sym, callees);
let termdata = &mut self.terms[term.index()]; let termdata = &mut self.terms[term.index()];
match &termdata.kind { match &mut termdata.kind {
&TermKind::Declared => { TermKind::EnumVariant { .. } => {
termdata.kind = TermKind::InternalExtractor { template };
}
_ => {
tyenv.report_error( tyenv.report_error(
ext.pos, ext.pos,
"Extractor macro body defined on term of incorrect kind".to_string(), "Extractor macro body defined on term of incorrect kind; cannot be an \
enum variant"
.into(),
); );
continue; continue;
} }
TermKind::Decl { extractor_kind, .. } => match extractor_kind {
None => {
*extractor_kind = Some(ExtractorKind::InternalExtractor { template });
}
Some(ext_kind) => {
tyenv.report_error(
ext.pos,
"Duplicate extractor definition".to_string(),
);
let pos = match ext_kind {
ExtractorKind::InternalExtractor { template } => template.pos(),
ExtractorKind::ExternalExtractor { pos, .. } => *pos,
};
tyenv.report_error(
pos,
"Extractor was already defined here".to_string(),
);
continue;
}
},
} }
} }
} }
@@ -914,8 +1022,16 @@ impl TermEnv {
} }
}; };
let pos = match &self.terms[term.index()].kind { let pos = match &self.terms[term.index()].kind {
TermKind::InternalExtractor { template } => template.pos(), TermKind::Decl {
_ => unreachable!(), extractor_kind: Some(ExtractorKind::InternalExtractor { template }),
..
} => template.pos(),
_ => {
// Again, there must have already been errors
// recorded.
assert!(!tyenv.errors.is_empty());
continue 'outer;
}
}; };
let path: Vec<_> = path let path: Vec<_> = path
@@ -944,11 +1060,37 @@ impl TermEnv {
vars: vec![], vars: vec![],
}; };
let rule_term = match rule.pattern.root_term() {
Some(name) => {
let sym = tyenv.intern_mut(name);
match self.term_map.get(&sym) {
Some(term) => *term,
None => {
tyenv.report_error(
pos,
"Cannot define a rule for an unknown term".to_string(),
);
continue;
}
}
}
None => {
tyenv.report_error(
pos,
"Rule does not have a term at the root of its left-hand side"
.to_string(),
);
continue;
}
};
let (lhs, ty) = unwrap_or_continue!(self.translate_pattern( let (lhs, ty) = unwrap_or_continue!(self.translate_pattern(
tyenv, tyenv,
rule_term,
&rule.pattern, &rule.pattern,
None, None,
&mut bindings &mut bindings,
/* is_root = */ true,
)); ));
let rhs = unwrap_or_continue!(self.translate_expr( let rhs = unwrap_or_continue!(self.translate_expr(
tyenv, tyenv,
@@ -984,15 +1126,35 @@ impl TermEnv {
} }
}; };
let termdata = &mut self.terms[term_id.index()]; let termdata = &mut self.terms[term_id.index()];
match &termdata.kind { match &mut termdata.kind {
&TermKind::Declared => { TermKind::Decl {
termdata.kind = TermKind::ExternalConstructor { name: func_sym }; constructor_kind, ..
} } => match constructor_kind {
_ => { None => {
*constructor_kind =
Some(ConstructorKind::ExternalConstructor { name: func_sym });
}
Some(ConstructorKind::InternalConstructor) => {
tyenv.report_error(
pos,
format!(
"External constructor declared on term that already has rules: {}",
term.0,
),
);
}
Some(ConstructorKind::ExternalConstructor { .. }) => {
tyenv.report_error(
pos,
"Duplicate external constructor definition".to_string(),
);
}
},
TermKind::EnumVariant { .. } => {
tyenv.report_error( tyenv.report_error(
pos, pos,
format!( format!(
"Constructor defined on term of improper type '{}'", "External constructor cannot be defined on enum variant: {}",
term.0, term.0,
), ),
); );
@@ -1031,18 +1193,45 @@ impl TermEnv {
vec![ArgPolarity::Output; termdata.arg_tys.len()] vec![ArgPolarity::Output; termdata.arg_tys.len()]
}; };
match &termdata.kind { match &mut termdata.kind {
&TermKind::Declared => { TermKind::Decl { extractor_kind, .. } => match extractor_kind {
termdata.kind = TermKind::ExternalExtractor { None => {
name: func_sym, *extractor_kind = Some(ExtractorKind::ExternalExtractor {
arg_polarity, name: func_sym,
infallible, arg_polarity,
}; infallible,
} pos,
_ => { });
}
Some(ExtractorKind::ExternalExtractor { pos: pos2, .. }) => {
tyenv.report_error(
pos,
"Duplicate external extractor definition".to_string(),
);
tyenv.report_error(
*pos2,
"External extractor already defined".to_string(),
);
continue;
}
Some(ExtractorKind::InternalExtractor { template }) => {
tyenv.report_error(
pos,
"Cannot define external extractor for term that already has an \
internal extractor macro body defined"
.to_string(),
);
tyenv.report_error(
template.pos(),
"Internal extractor macro body already defined".to_string(),
);
continue;
}
},
TermKind::EnumVariant { .. } => {
tyenv.report_error( tyenv.report_error(
pos, pos,
format!("Extractor defined on term of improper type '{}'", term.0), format!("Cannot define extractor for enum variant '{}'", term.0),
); );
continue; continue;
} }
@@ -1058,7 +1247,8 @@ impl TermEnv {
if let ast::Def::Decl(decl) = def { if let ast::Def::Decl(decl) = def {
let sym = tyenv.intern_mut(&decl.term); let sym = tyenv.intern_mut(&decl.term);
let term = self.term_map[&sym]; let term = self.term_map[&sym];
if self.terms[term.index()].is_declared() { let term = &self.terms[term.index()];
if !term.has_constructor() && !term.has_extractor() {
tyenv.report_error( tyenv.report_error(
decl.pos, decl.pos,
format!( format!(
@@ -1074,9 +1264,11 @@ impl TermEnv {
fn translate_pattern( fn translate_pattern(
&self, &self,
tyenv: &mut TypeEnv, tyenv: &mut TypeEnv,
rule_term: TermId,
pat: &ast::Pattern, pat: &ast::Pattern,
expected_ty: Option<TypeId>, expected_ty: Option<TypeId>,
bindings: &mut Bindings, bindings: &mut Bindings,
is_root: bool,
) -> Option<(Pattern, TypeId)> { ) -> Option<(Pattern, TypeId)> {
log::trace!("translate_pattern: {:?}", pat); log::trace!("translate_pattern: {:?}", pat);
log::trace!("translate_pattern: bindings = {:?}", bindings); log::trace!("translate_pattern: bindings = {:?}", bindings);
@@ -1135,9 +1327,11 @@ impl TermEnv {
for subpat in subpats { for subpat in subpats {
let (subpat, ty) = unwrap_or_continue!(self.translate_pattern( let (subpat, ty) = unwrap_or_continue!(self.translate_pattern(
tyenv, tyenv,
rule_term,
&*subpat, &*subpat,
expected_ty, expected_ty,
bindings bindings,
/* is_root = */ false,
)); ));
expected_ty = expected_ty.or(Some(ty)); expected_ty = expected_ty.or(Some(ty));
children.push(subpat); children.push(subpat);
@@ -1155,8 +1349,14 @@ impl TermEnv {
pos, pos,
} => { } => {
// Do the subpattern first so we can resolve the type for sure. // Do the subpattern first so we can resolve the type for sure.
let (subpat, ty) = let (subpat, ty) = self.translate_pattern(
self.translate_pattern(tyenv, &*subpat, expected_ty, bindings)?; tyenv,
rule_term,
&*subpat,
expected_ty,
bindings,
/* is_root = */ false,
)?;
let name = tyenv.intern_mut(var); let name = tyenv.intern_mut(var);
if bindings.vars.iter().any(|bv| bv.name == name) { if bindings.vars.iter().any(|bv| bv.name == name) {
@@ -1254,15 +1454,43 @@ impl TermEnv {
let termdata = &self.terms[tid.index()]; let termdata = &self.terms[tid.index()];
match &termdata.kind { match &termdata.kind {
&TermKind::EnumVariant { .. } => { TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
..
} if is_root && *tid == rule_term => {
// This is just the `(foo ...)` pseudo-pattern inside a
// `(rule (foo ...) ...)` form. Just keep checking the
// sub-patterns.
for arg in args { for arg in args {
if let &ast::TermArgPattern::Expr(_) = arg { if let ast::TermArgPattern::Expr(e) = arg {
tyenv.report_error(pos, format!("Term in pattern '{}' cannot have an injected expr, because it is an enum variant", sym.0)); tyenv.report_error(
e.pos(),
"cannot use output-polarity expression with top-level rules"
.to_string(),
);
} }
} }
} }
&TermKind::ExternalExtractor { TermKind::EnumVariant { .. } => {
ref arg_polarity, .. for arg in args {
if let &ast::TermArgPattern::Expr(_) = arg {
tyenv.report_error(
pos,
format!(
"Term in pattern '{}' cannot have an injected expr, because \
it is an enum variant",
sym.0
)
);
}
}
}
TermKind::Decl {
extractor_kind:
Some(ExtractorKind::ExternalExtractor {
ref arg_polarity, ..
}),
..
} => { } => {
for (arg, pol) in args.iter().zip(arg_polarity.iter()) { for (arg, pol) in args.iter().zip(arg_polarity.iter()) {
match (arg, pol) { match (arg, pol) {
@@ -1276,12 +1504,20 @@ impl TermEnv {
} }
(_, &ArgPolarity::Output) => {} (_, &ArgPolarity::Output) => {}
(&ast::TermArgPattern::Pattern(ref p), &ArgPolarity::Input) => { (&ast::TermArgPattern::Pattern(ref p), &ArgPolarity::Input) => {
tyenv.report_error(p.pos(), "Non-expression used in pattern but expression required for input-polarity extractor arg".to_string()); tyenv.report_error(
p.pos(),
"Non-expression used in pattern but expression required for \
input-polarity extractor arg"
.to_string()
);
} }
} }
} }
} }
&TermKind::InternalExtractor { ref template } => { TermKind::Decl {
extractor_kind: Some(ExtractorKind::InternalExtractor { ref template }),
..
} => {
// Expand the extractor macro! We create a map // Expand the extractor macro! We create a map
// from macro args to AST pattern trees and // from macro args to AST pattern trees and
// then evaluate the template with these // then evaluate the template with these
@@ -1291,7 +1527,12 @@ impl TermEnv {
let sub_ast = match template_arg { let sub_ast = match template_arg {
&ast::TermArgPattern::Pattern(ref pat) => pat.clone(), &ast::TermArgPattern::Pattern(ref pat) => pat.clone(),
&ast::TermArgPattern::Expr(_) => { &ast::TermArgPattern::Expr(_) => {
tyenv.report_error(pos, "Cannot expand an extractor macro with an expression in a macro argument".to_string()); tyenv.report_error(
pos,
"Cannot expand an extractor macro with an expression in a \
macro argument"
.to_string(),
);
return None; return None;
} }
}; };
@@ -1299,15 +1540,26 @@ impl TermEnv {
} }
log::trace!("internal extractor macro args = {:?}", args); log::trace!("internal extractor macro args = {:?}", args);
let pat = template.subst_macro_args(&macro_args[..])?; let pat = template.subst_macro_args(&macro_args[..])?;
return self.translate_pattern(tyenv, &pat, expected_ty, bindings); return self.translate_pattern(
tyenv,
rule_term,
&pat,
expected_ty,
bindings,
/* is_root = */ false,
);
} }
&TermKind::ExternalConstructor { .. } | &TermKind::InternalConstructor => { TermKind::Decl {
// OK. extractor_kind: None,
} ..
&TermKind::Declared => { } => {
tyenv.report_error( tyenv.report_error(
pos, pos,
format!("Declared but undefined term '{}' used", sym.0), format!(
"Cannot use term '{}' that does not have a defined extractor in a \
left-hand side pattern",
sym.0
),
); );
} }
} }
@@ -1319,6 +1571,7 @@ impl TermEnv {
let arg_ty = unwrap_or_continue!(term.arg_tys.get(i).copied()); let arg_ty = unwrap_or_continue!(term.arg_tys.get(i).copied());
let (subpat, _) = unwrap_or_continue!(self.translate_pattern_term_arg( let (subpat, _) = unwrap_or_continue!(self.translate_pattern_term_arg(
tyenv, tyenv,
rule_term,
pos, pos,
arg, arg,
Some(arg_ty), Some(arg_ty),
@@ -1336,6 +1589,7 @@ impl TermEnv {
fn translate_pattern_term_arg( fn translate_pattern_term_arg(
&self, &self,
tyenv: &mut TypeEnv, tyenv: &mut TypeEnv,
rule_term: TermId,
pos: Pos, pos: Pos,
pat: &ast::TermArgPattern, pat: &ast::TermArgPattern,
expected_ty: Option<TypeId>, expected_ty: Option<TypeId>,
@@ -1343,7 +1597,14 @@ impl TermEnv {
) -> Option<(TermArgPattern, TypeId)> { ) -> Option<(TermArgPattern, TypeId)> {
match pat { match pat {
&ast::TermArgPattern::Pattern(ref pat) => { &ast::TermArgPattern::Pattern(ref pat) => {
let (subpat, ty) = self.translate_pattern(tyenv, pat, expected_ty, bindings)?; let (subpat, ty) = self.translate_pattern(
tyenv,
rule_term,
pat,
expected_ty,
bindings,
/* is_root = */ false,
)?;
Some((TermArgPattern::Pattern(subpat), ty)) Some((TermArgPattern::Pattern(subpat), ty))
} }
&ast::TermArgPattern::Expr(ref expr) => { &ast::TermArgPattern::Expr(ref expr) => {

View File

@@ -0,0 +1,17 @@
(type i32 (primitive i32))
(type B (enum (B (x i32) (y i32))))
;; `isub` has a constructor and extractor.
(decl isub (i32 i32) B)
(rule (isub x y)
(B.B x y))
(extractor (isub x y)
(B.B x y))
;; `value_array_2` has both an external extractor and an external constructor.
(type Value (primitive Value))
(type ValueArray2 extern (enum))
(decl value_array_2 (Value Value) ValueArray2)
(extern extractor infallible value_array_2 unpack_value_array_2)
(extern constructor value_array_2 pack_value_array_2)