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();
writeln!(code, "pub trait Context {{").unwrap();
for term in &self.termenv.terms {
if term.is_external() {
let ext_sig = term.to_sig(self.typeenv).unwrap();
if term.has_external_extractor() {
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);
}
}
@@ -241,11 +245,11 @@ impl<'a> Codegen<'a> {
// Skip terms that are enum variants or that have external
// constructors/extractors.
if !termdata.is_constructor() || termdata.is_external() {
if !termdata.has_constructor() || termdata.has_external_constructor() {
continue;
}
let sig = termdata.to_sig(self.typeenv).unwrap();
let sig = termdata.constructor_sig(self.typeenv).unwrap();
let args = sig
.param_tys
@@ -393,7 +397,7 @@ impl<'a> Codegen<'a> {
};
let outputname = self.value_name(&output);
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());
let fallible_try = if infallible { "" } else { "?" };
writeln!(
@@ -529,7 +533,7 @@ impl<'a> Codegen<'a> {
..
} => {
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
.iter()
@@ -671,7 +675,9 @@ impl<'a> Codegen<'a> {
// variants in order to create a `match` rather than a
// chain of if-lets.
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;
while i < edges.len() {

View File

@@ -400,12 +400,9 @@ impl PatternSequence {
let termdata = &termenv.terms[term.index()];
let arg_tys = &termdata.arg_tys[..];
match &termdata.kind {
&TermKind::Declared => {
panic!("Pattern invocation of undefined term body");
}
&TermKind::EnumVariant { variant } => {
TermKind::EnumVariant { variant } => {
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()) {
let subpat = match subpat {
&TermArgPattern::Pattern(ref pat) => pat,
@@ -420,16 +417,25 @@ impl PatternSequence {
);
}
}
&TermKind::InternalConstructor
| &TermKind::ExternalConstructor { .. } => {
panic!("Should not invoke constructor in pattern");
TermKind::Decl {
extractor_kind: None,
..
} => {
panic!("Pattern invocation of undefined term body")
}
&TermKind::InternalExtractor { .. } => {
panic!("Should have been expanded away");
TermKind::Decl {
extractor_kind: Some(ExtractorKind::InternalExtractor { .. }),
..
} => {
panic!("Should have been expanded away")
}
&TermKind::ExternalExtractor {
ref arg_polarity,
infallible,
TermKind::Decl {
extractor_kind:
Some(ExtractorKind::ExternalExtractor {
ref arg_polarity,
infallible,
..
}),
..
} => {
// Evaluate all `input` args.
@@ -469,8 +475,13 @@ impl PatternSequence {
}
// Invoke the extractor.
let arg_values = self
.add_extract(inputs, input_tys, output_tys, term, infallible);
let arg_values = self.add_extract(
inputs,
input_tys,
output_tys,
term,
*infallible,
);
for (pat, &val) in output_pats.iter().zip(arg_values.iter()) {
self.gen_pattern(
@@ -594,10 +605,13 @@ impl ExprSequence {
.push((self.gen_expr(typeenv, termenv, &*arg_expr, &vars), arg_ty));
}
match &termdata.kind {
&TermKind::EnumVariant { variant } => {
self.add_create_variant(&arg_values_tys[..], ty, variant)
TermKind::EnumVariant { variant } => {
self.add_create_variant(&arg_values_tys[..], ty, *variant)
}
&TermKind::InternalConstructor => {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
..
} => {
self.add_construct(
&arg_values_tys[..],
ty,
@@ -605,7 +619,10 @@ impl ExprSequence {
/* infallible = */ false,
)
}
&TermKind::ExternalConstructor { .. } => {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
..
} => {
self.add_construct(
&arg_values_tys[..],
ty,
@@ -613,7 +630,10 @@ impl ExprSequence {
/* 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`.
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 compiled Rust function in the generated code.
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
/// arguments take patterns and are simply substituted with the given
/// patterns when used.
@@ -228,14 +250,9 @@ pub enum TermKind {
arg_polarity: Vec<ArgPolarity>,
/// Is the external extractor infallible?
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;
@@ -261,40 +278,69 @@ impl Term {
self.ret_ty
}
/// Is this term a constructor?
pub fn is_constructor(&self) -> bool {
match &self.kind {
&TermKind::InternalConstructor { .. } | &TermKind::ExternalConstructor { .. } => true,
_ => false,
}
/// Is this term an enum variant?
pub fn is_enum_variant(&self) -> bool {
matches!(self.kind, TermKind::EnumVariant { .. })
}
fn is_declared(&self) -> bool {
matches!(self.kind, TermKind::Declared)
/// Does this term have a constructor?
pub fn has_constructor(&self) -> bool {
matches!(
self.kind,
TermKind::EnumVariant { .. }
| TermKind::Decl {
constructor_kind: Some(_),
..
}
)
}
/// Is this term external?
pub fn is_external(&self) -> bool {
match &self.kind {
&TermKind::ExternalExtractor { .. } | &TermKind::ExternalConstructor { .. } => true,
_ => false,
}
/// Does this term have an extractor?
pub fn has_extractor(&self) -> bool {
matches!(
self.kind,
TermKind::EnumVariant { .. }
| TermKind::Decl {
extractor_kind: Some(_),
..
}
)
}
/// Get this term's external function signature, if any.
pub fn to_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
/// Is this term's extractor external?
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 {
&TermKind::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::ExternalExtractor {
name,
ref arg_polarity,
infallible,
TermKind::Decl {
extractor_kind:
Some(ExtractorKind::ExternalExtractor {
name,
ref arg_polarity,
infallible,
..
}),
..
} => {
let mut arg_tys = vec![];
let mut ret_tys = vec![];
@@ -314,10 +360,30 @@ impl Term {
full_name: format!("C::{}", tyenv.syms[name.index()]),
param_tys: arg_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()]);
Some(ExternalSig {
func_name: name.clone(),
@@ -740,7 +806,10 @@ impl TermEnv {
name,
arg_tys,
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()];
match &termdata.kind {
&TermKind::Declared => {
termdata.kind = TermKind::InternalConstructor;
match &mut termdata.kind {
TermKind::Decl {
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 => {
// 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 internal constructor".to_string());
TermKind::EnumVariant { .. } => {
tyenv.report_error(
pos,
"Rule LHS root term is incorrect kind; cannot be enum variant"
.to_string(),
);
continue;
}
}
@@ -870,17 +959,36 @@ impl TermEnv {
extractor_call_graph.insert(sym, callees);
let termdata = &mut self.terms[term.index()];
match &termdata.kind {
&TermKind::Declared => {
termdata.kind = TermKind::InternalExtractor { template };
}
_ => {
match &mut termdata.kind {
TermKind::EnumVariant { .. } => {
tyenv.report_error(
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;
}
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 {
TermKind::InternalExtractor { template } => template.pos(),
_ => unreachable!(),
TermKind::Decl {
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
@@ -944,11 +1060,37 @@ impl TermEnv {
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(
tyenv,
rule_term,
&rule.pattern,
None,
&mut bindings
&mut bindings,
/* is_root = */ true,
));
let rhs = unwrap_or_continue!(self.translate_expr(
tyenv,
@@ -984,15 +1126,35 @@ impl TermEnv {
}
};
let termdata = &mut self.terms[term_id.index()];
match &termdata.kind {
&TermKind::Declared => {
termdata.kind = TermKind::ExternalConstructor { name: func_sym };
}
_ => {
match &mut termdata.kind {
TermKind::Decl {
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(
pos,
format!(
"Constructor defined on term of improper type '{}'",
"External constructor cannot be defined on enum variant: {}",
term.0,
),
);
@@ -1031,18 +1193,45 @@ impl TermEnv {
vec![ArgPolarity::Output; termdata.arg_tys.len()]
};
match &termdata.kind {
&TermKind::Declared => {
termdata.kind = TermKind::ExternalExtractor {
name: func_sym,
arg_polarity,
infallible,
};
}
_ => {
match &mut termdata.kind {
TermKind::Decl { extractor_kind, .. } => match extractor_kind {
None => {
*extractor_kind = Some(ExtractorKind::ExternalExtractor {
name: func_sym,
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(
pos,
format!("Extractor defined on term of improper type '{}'", term.0),
format!("Cannot define extractor for enum variant '{}'", term.0),
);
continue;
}
@@ -1058,7 +1247,8 @@ impl TermEnv {
if let ast::Def::Decl(decl) = def {
let sym = tyenv.intern_mut(&decl.term);
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(
decl.pos,
format!(
@@ -1074,9 +1264,11 @@ impl TermEnv {
fn translate_pattern(
&self,
tyenv: &mut TypeEnv,
rule_term: TermId,
pat: &ast::Pattern,
expected_ty: Option<TypeId>,
bindings: &mut Bindings,
is_root: bool,
) -> Option<(Pattern, TypeId)> {
log::trace!("translate_pattern: {:?}", pat);
log::trace!("translate_pattern: bindings = {:?}", bindings);
@@ -1135,9 +1327,11 @@ impl TermEnv {
for subpat in subpats {
let (subpat, ty) = unwrap_or_continue!(self.translate_pattern(
tyenv,
rule_term,
&*subpat,
expected_ty,
bindings
bindings,
/* is_root = */ false,
));
expected_ty = expected_ty.or(Some(ty));
children.push(subpat);
@@ -1155,8 +1349,14 @@ impl TermEnv {
pos,
} => {
// Do the subpattern first so we can resolve the type for sure.
let (subpat, ty) =
self.translate_pattern(tyenv, &*subpat, expected_ty, bindings)?;
let (subpat, ty) = self.translate_pattern(
tyenv,
rule_term,
&*subpat,
expected_ty,
bindings,
/* is_root = */ false,
)?;
let name = tyenv.intern_mut(var);
if bindings.vars.iter().any(|bv| bv.name == name) {
@@ -1254,15 +1454,43 @@ impl TermEnv {
let termdata = &self.terms[tid.index()];
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 {
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));
if let ast::TermArgPattern::Expr(e) = arg {
tyenv.report_error(
e.pos(),
"cannot use output-polarity expression with top-level rules"
.to_string(),
);
}
}
}
&TermKind::ExternalExtractor {
ref arg_polarity, ..
TermKind::EnumVariant { .. } => {
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()) {
match (arg, pol) {
@@ -1276,12 +1504,20 @@ impl TermEnv {
}
(_, &ArgPolarity::Output) => {}
(&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
// from macro args to AST pattern trees and
// then evaluate the template with these
@@ -1291,7 +1527,12 @@ impl TermEnv {
let sub_ast = match template_arg {
&ast::TermArgPattern::Pattern(ref pat) => pat.clone(),
&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;
}
};
@@ -1299,15 +1540,26 @@ impl TermEnv {
}
log::trace!("internal extractor macro args = {:?}", 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 => {
// OK.
}
&TermKind::Declared => {
TermKind::Decl {
extractor_kind: None,
..
} => {
tyenv.report_error(
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 (subpat, _) = unwrap_or_continue!(self.translate_pattern_term_arg(
tyenv,
rule_term,
pos,
arg,
Some(arg_ty),
@@ -1336,6 +1589,7 @@ impl TermEnv {
fn translate_pattern_term_arg(
&self,
tyenv: &mut TypeEnv,
rule_term: TermId,
pos: Pos,
pat: &ast::TermArgPattern,
expected_ty: Option<TypeId>,
@@ -1343,7 +1597,14 @@ impl TermEnv {
) -> Option<(TermArgPattern, TypeId)> {
match 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))
}
&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)