ISLE: add support for multi-extractors and multi-constructors. (#4908)
* ISLE: add support for multi-extractors and multi-constructors. This support allows for rules that process multiple matching values per extractor call on the left-hand side, and as a result, can produce multiple values from the constructor whose body they define. This is useful in situations where we are matching on an input data structure that can have multiple "nodes" for a given value or ID, for example in an e-graph. * Review feedback: all multi-ctors and multi-etors return iterators; no `Vec` case. * Add additional warning suppressions to generated-code toplevels to be consistent with new islec output.
This commit is contained in:
@@ -4,6 +4,6 @@
|
||||
// mod generated_code;` trick either.
|
||||
#![allow(dead_code, unreachable_code, unreachable_patterns)]
|
||||
#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]
|
||||
|
||||
include!(concat!(env!("ISLE_DIR"), "/isle_aarch64.rs"));
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
// mod generated_code;` trick either.
|
||||
#![allow(dead_code, unreachable_code, unreachable_patterns)]
|
||||
#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]
|
||||
|
||||
include!(concat!(env!("ISLE_DIR"), "/isle_s390x.rs"));
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
// mod generated_code;` trick either.
|
||||
#![allow(dead_code, unreachable_code, unreachable_patterns)]
|
||||
#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]
|
||||
|
||||
include!(concat!(env!("ISLE_DIR"), "/isle_x64.rs"));
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
(type u32 (primitive u32))
|
||||
|
||||
(decl multi A (u32) u32)
|
||||
(extractor (A x) x)
|
||||
@@ -0,0 +1,15 @@
|
||||
(type u32 (primitive u32))
|
||||
|
||||
(decl multi A (u32) u32)
|
||||
(decl multi B (u32) u32)
|
||||
(decl multi C (u32) u32)
|
||||
(decl multi D (u32) u32)
|
||||
|
||||
(extern constructor B ctor_B)
|
||||
(extern extractor C etor_C)
|
||||
|
||||
(rule (A x)
|
||||
(B x))
|
||||
|
||||
(rule (D (C x))
|
||||
(B x))
|
||||
@@ -0,0 +1,57 @@
|
||||
mod multi_constructor;
|
||||
|
||||
pub(crate) type ConstructorVec<T> = Vec<T>;
|
||||
|
||||
struct Context;
|
||||
|
||||
struct It {
|
||||
i: u32,
|
||||
limit: u32,
|
||||
}
|
||||
|
||||
impl multi_constructor::ContextIter for It {
|
||||
type Context = Context;
|
||||
type Output = u32;
|
||||
fn next(&mut self, _ctx: &mut Self::Context) -> Option<u32> {
|
||||
if self.i >= self.limit {
|
||||
None
|
||||
} else {
|
||||
let i = self.i;
|
||||
self.i += 1;
|
||||
Some(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl multi_constructor::Context for Context {
|
||||
type etor_C_iter = It;
|
||||
fn etor_C(&mut self, value: u32) -> Option<It> {
|
||||
Some(It { i: 0, limit: value })
|
||||
}
|
||||
|
||||
type ctor_B_iter = multi_constructor::ContextIterWrapper<u32, std::vec::IntoIter<u32>, Context>;
|
||||
fn ctor_B(&mut self, value: u32) -> Option<Self::ctor_B_iter> {
|
||||
Some((0..value).rev().collect::<Vec<_>>().into_iter().into())
|
||||
}
|
||||
}
|
||||
|
||||
struct IterWithContext<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Context>> {
|
||||
ctx: &'a mut Context,
|
||||
it: I,
|
||||
}
|
||||
|
||||
impl<'a, Item, I: multi_constructor::ContextIter<Output = Item, Context = Context>> Iterator for IterWithContext<'a, Item, I> {
|
||||
type Item = Item;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it.next(self.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut ctx = Context;
|
||||
let l1 = multi_constructor::constructor_A(&mut ctx, 10).unwrap();
|
||||
let l2 = multi_constructor::constructor_D(&mut ctx, 5).unwrap();
|
||||
let l1 = IterWithContext { ctx: &mut ctx, it: l1 }.collect::<Vec<_>>();
|
||||
let l2 = IterWithContext { ctx: &mut ctx, it: l2 }.collect::<Vec<_>>();
|
||||
println!("l1 = {:?} l2 = {:?}", l1, l2);
|
||||
}
|
||||
14
cranelift/isle/isle/isle_examples/link/multi_extractor.isle
Normal file
14
cranelift/isle/isle/isle_examples/link/multi_extractor.isle
Normal file
@@ -0,0 +1,14 @@
|
||||
(type u32 (primitive u32))
|
||||
(type A extern (enum (B) (C)))
|
||||
|
||||
(decl multi E1 (A u32) u32)
|
||||
|
||||
(extern extractor E1 e1_etor)
|
||||
|
||||
(decl Rule (u32) u32)
|
||||
|
||||
(rule (Rule (E1 a idx))
|
||||
(if-let (A.B) a)
|
||||
idx)
|
||||
(rule (Rule _)
|
||||
32)
|
||||
@@ -0,0 +1,46 @@
|
||||
mod multi_extractor;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum A {
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
struct It {
|
||||
i: u32,
|
||||
arg: u32,
|
||||
}
|
||||
|
||||
impl multi_extractor::ContextIter for It {
|
||||
type Context = Context;
|
||||
type Output = (A, u32);
|
||||
fn next(&mut self, _ctx: &mut Self::Context) -> Option<Self::Output> {
|
||||
if self.i >= 32 {
|
||||
None
|
||||
} else {
|
||||
let idx = self.i;
|
||||
self.i += 1;
|
||||
let a = if self.arg & (1u32 << idx) != 0 {
|
||||
A::B
|
||||
} else {
|
||||
A::C
|
||||
};
|
||||
Some((a, idx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Context;
|
||||
impl multi_extractor::Context for Context {
|
||||
type e1_etor_iter = It;
|
||||
fn e1_etor(&mut self, arg0: u32) -> Option<It> {
|
||||
Some(It { i: 0, arg: arg0 })
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut ctx = Context;
|
||||
let x = multi_extractor::constructor_Rule(&mut ctx, 0xf0);
|
||||
let y = multi_extractor::constructor_Rule(&mut ctx, 0);
|
||||
println!("x = {:?} y = {:?}", x, y);
|
||||
}
|
||||
@@ -72,6 +72,10 @@ pub struct Decl {
|
||||
pub ret_ty: Ident,
|
||||
/// Whether this term's constructor is pure.
|
||||
pub pure: bool,
|
||||
/// Whether this term can exist with some multiplicity: an
|
||||
/// extractor or a constructor that matches multiple times, or
|
||||
/// produces multiple values.
|
||||
pub multi: bool,
|
||||
pub pos: Pos,
|
||||
}
|
||||
|
||||
|
||||
@@ -87,35 +87,66 @@ impl<'a> Codegen<'a> {
|
||||
"#![allow(unused_imports, unused_variables, non_snake_case, unused_mut)]"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(code, "#![allow(irrefutable_let_patterns)]").unwrap();
|
||||
writeln!(
|
||||
code,
|
||||
"#![allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
writeln!(code, "\nuse super::*; // Pulls in all external types.").unwrap();
|
||||
writeln!(code, "use std::marker::PhantomData;").unwrap();
|
||||
}
|
||||
|
||||
fn generate_trait_sig(&self, code: &mut String, indent: &str, sig: &ExternalSig) {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}fn {name}(&mut self, {params}) -> {opt_start}{open_paren}{rets}{close_paren}{opt_end};",
|
||||
indent = indent,
|
||||
name = sig.func_name,
|
||||
params = sig.param_tys
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
opt_start = if sig.infallible { "" } else { "Option<" },
|
||||
let ret_tuple = format!(
|
||||
"{open_paren}{rets}{close_paren}",
|
||||
open_paren = if sig.ret_tys.len() != 1 { "(" } else { "" },
|
||||
rets = sig.ret_tys
|
||||
rets = sig
|
||||
.ret_tys
|
||||
.iter()
|
||||
.map(|&ty| self.type_name(ty, /* by_ref = */ false))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
close_paren = if sig.ret_tys.len() != 1 { ")" } else { "" },
|
||||
opt_end = if sig.infallible { "" } else { ">" },
|
||||
);
|
||||
|
||||
let ret_ty = match (sig.multi, sig.infallible) {
|
||||
(false, false) => format!("Option<{}>", ret_tuple),
|
||||
(false, true) => format!("{}", ret_tuple),
|
||||
(true, false) => format!("Option<Self::{}_iter>", sig.func_name),
|
||||
_ => panic!(
|
||||
"Unsupported multiplicity/infallible combo: {:?}, {}",
|
||||
sig.multi, sig.infallible
|
||||
),
|
||||
};
|
||||
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}fn {name}(&mut self, {params}) -> {ret_ty};",
|
||||
indent = indent,
|
||||
name = sig.func_name,
|
||||
params = sig
|
||||
.param_tys
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, &ty)| format!("arg{}: {}", i, self.type_name(ty, /* by_ref = */ true)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
ret_ty = ret_ty,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if sig.multi {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}type {name}_iter: ContextIter<Context = Self, Output = {output}>;",
|
||||
indent = indent,
|
||||
name = sig.func_name,
|
||||
output = ret_tuple,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_ctx_trait(&self, code: &mut String) {
|
||||
@@ -147,6 +178,34 @@ impl<'a> Codegen<'a> {
|
||||
}
|
||||
}
|
||||
writeln!(code, "}}").unwrap();
|
||||
writeln!(
|
||||
code,
|
||||
r#"
|
||||
pub trait ContextIter {{
|
||||
type Context;
|
||||
type Output;
|
||||
fn next(&mut self, ctx: &mut Self::Context) -> Option<Self::Output>;
|
||||
}}
|
||||
|
||||
pub struct ContextIterWrapper<Item, I: Iterator < Item = Item>, C: Context> {{
|
||||
iter: I,
|
||||
_ctx: PhantomData<C>,
|
||||
}}
|
||||
impl<Item, I: Iterator<Item = Item>, C: Context> From<I> for ContextIterWrapper<Item, I, C> {{
|
||||
fn from(iter: I) -> Self {{
|
||||
Self {{ iter, _ctx: PhantomData }}
|
||||
}}
|
||||
}}
|
||||
impl<Item, I: Iterator<Item = Item>, C: Context> ContextIter for ContextIterWrapper<Item, I, C> {{
|
||||
type Context = C;
|
||||
type Output = Item;
|
||||
fn next(&mut self, _ctx: &mut Self::Context) -> Option<Self::Output> {{
|
||||
self.iter.next()
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn generate_internal_types(&self, code: &mut String) {
|
||||
@@ -299,6 +358,11 @@ impl<'a> Codegen<'a> {
|
||||
.join(", ");
|
||||
assert_eq!(sig.ret_tys.len(), 1);
|
||||
let ret = self.type_name(sig.ret_tys[0], false);
|
||||
let ret = if sig.multi {
|
||||
format!("impl ContextIter<Context = C, Output = {}>", ret)
|
||||
} else {
|
||||
ret
|
||||
};
|
||||
|
||||
writeln!(
|
||||
code,
|
||||
@@ -313,12 +377,30 @@ impl<'a> Codegen<'a> {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if sig.multi {
|
||||
writeln!(code, "let mut returns = ConstructorVec::new();").unwrap();
|
||||
}
|
||||
|
||||
let mut body_ctx: BodyContext = Default::default();
|
||||
let returned =
|
||||
self.generate_body(code, /* depth = */ 0, trie, " ", &mut body_ctx);
|
||||
let returned = self.generate_body(
|
||||
code,
|
||||
/* depth = */ 0,
|
||||
trie,
|
||||
" ",
|
||||
&mut body_ctx,
|
||||
sig.multi,
|
||||
);
|
||||
if !returned {
|
||||
if sig.multi {
|
||||
writeln!(
|
||||
code,
|
||||
" return Some(ContextIterWrapper::from(returns.into_iter()));"
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
writeln!(code, " return None;").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(code, "}}").unwrap();
|
||||
}
|
||||
@@ -332,8 +414,9 @@ impl<'a> Codegen<'a> {
|
||||
indent: &str,
|
||||
ctx: &mut BodyContext,
|
||||
returns: &mut Vec<(usize, String)>,
|
||||
) {
|
||||
) -> bool {
|
||||
log!("generate_expr_inst: {:?}", inst);
|
||||
let mut new_scope = false;
|
||||
match inst {
|
||||
&ExprInst::ConstInt { ty, val } => {
|
||||
let value = Value::Expr {
|
||||
@@ -422,6 +505,7 @@ impl<'a> Codegen<'a> {
|
||||
ref inputs,
|
||||
term,
|
||||
infallible,
|
||||
multi,
|
||||
..
|
||||
} => {
|
||||
let mut input_exprs = vec![];
|
||||
@@ -442,6 +526,8 @@ impl<'a> Codegen<'a> {
|
||||
let termdata = &self.termenv.terms[term.index()];
|
||||
let sig = termdata.constructor_sig(self.typeenv).unwrap();
|
||||
assert_eq!(input_exprs.len(), sig.param_tys.len());
|
||||
|
||||
if !multi {
|
||||
let fallible_try = if infallible { "" } else { "?" };
|
||||
writeln!(
|
||||
code,
|
||||
@@ -453,6 +539,23 @@ impl<'a> Codegen<'a> {
|
||||
fallible_try,
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
writeln!(
|
||||
code,
|
||||
"{}let mut it = {}(ctx, {})?;",
|
||||
indent,
|
||||
sig.full_name,
|
||||
input_exprs.join(", "),
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
code,
|
||||
"{}while let Some({}) = it.next(ctx) {{",
|
||||
indent, outputname,
|
||||
)
|
||||
.unwrap();
|
||||
new_scope = true;
|
||||
}
|
||||
self.define_val(&output, ctx, /* is_ref = */ false, termdata.ret_ty);
|
||||
}
|
||||
&ExprInst::Return {
|
||||
@@ -462,6 +565,8 @@ impl<'a> Codegen<'a> {
|
||||
returns.push((index, value_expr));
|
||||
}
|
||||
}
|
||||
|
||||
new_scope
|
||||
}
|
||||
|
||||
fn match_variant_binders(
|
||||
@@ -489,7 +594,7 @@ impl<'a> Codegen<'a> {
|
||||
}
|
||||
|
||||
/// Returns a `bool` indicating whether this pattern inst is
|
||||
/// infallible.
|
||||
/// infallible, and the number of scopes opened.
|
||||
fn generate_pattern_inst(
|
||||
&self,
|
||||
code: &mut String,
|
||||
@@ -497,7 +602,7 @@ impl<'a> Codegen<'a> {
|
||||
inst: &PatternInst,
|
||||
indent: &str,
|
||||
ctx: &mut BodyContext,
|
||||
) -> bool {
|
||||
) -> (bool, usize) {
|
||||
match inst {
|
||||
&PatternInst::Arg { index, ty } => {
|
||||
let output = Value::Pattern {
|
||||
@@ -519,13 +624,13 @@ impl<'a> Codegen<'a> {
|
||||
is_ref,
|
||||
ty,
|
||||
);
|
||||
true
|
||||
(true, 0)
|
||||
}
|
||||
&PatternInst::MatchEqual { ref a, ref b, .. } => {
|
||||
let a = self.value_by_ref(a, ctx);
|
||||
let b = self.value_by_ref(b, ctx);
|
||||
writeln!(code, "{}if {} == {} {{", indent, a, b).unwrap();
|
||||
false
|
||||
(false, 1)
|
||||
}
|
||||
&PatternInst::MatchInt {
|
||||
ref input,
|
||||
@@ -536,13 +641,13 @@ impl<'a> Codegen<'a> {
|
||||
let int_val = self.const_int(int_val, ty);
|
||||
let input = self.value_by_val(input, ctx);
|
||||
writeln!(code, "{}if {} == {} {{", indent, input, int_val).unwrap();
|
||||
false
|
||||
(false, 1)
|
||||
}
|
||||
&PatternInst::MatchPrim { ref input, val, .. } => {
|
||||
let input = self.value_by_val(input, ctx);
|
||||
let sym = &self.typeenv.syms[val.index()];
|
||||
writeln!(code, "{}if {} == {} {{", indent, input, sym).unwrap();
|
||||
false
|
||||
(false, 1)
|
||||
}
|
||||
&PatternInst::MatchVariant {
|
||||
ref input,
|
||||
@@ -570,13 +675,14 @@ impl<'a> Codegen<'a> {
|
||||
indent, ty_name, variantname, args, input
|
||||
)
|
||||
.unwrap();
|
||||
false
|
||||
(false, 1)
|
||||
}
|
||||
&PatternInst::Extract {
|
||||
ref inputs,
|
||||
ref output_tys,
|
||||
term,
|
||||
infallible,
|
||||
multi,
|
||||
..
|
||||
} => {
|
||||
let termdata = &self.termenv.terms[term.index()];
|
||||
@@ -599,32 +705,61 @@ impl<'a> Codegen<'a> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if infallible {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}let {open_paren}{vars}{close_paren} = {name}(ctx, {args});",
|
||||
indent = indent,
|
||||
let bind_pattern = format!(
|
||||
"{open_paren}{vars}{close_paren}",
|
||||
open_paren = if output_binders.len() == 1 { "" } else { "(" },
|
||||
vars = output_binders.join(", "),
|
||||
close_paren = if output_binders.len() == 1 { "" } else { ")" },
|
||||
close_paren = if output_binders.len() == 1 { "" } else { ")" }
|
||||
);
|
||||
let etor_call = format!(
|
||||
"{name}(ctx, {args})",
|
||||
name = sig.full_name,
|
||||
args = input_values.join(", "),
|
||||
)
|
||||
.unwrap();
|
||||
true
|
||||
} else {
|
||||
args = input_values.join(", ")
|
||||
);
|
||||
|
||||
match (infallible, multi) {
|
||||
(_, true) => {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}if let Some({open_paren}{vars}{close_paren}) = {name}(ctx, {args}) {{",
|
||||
"{indent}if let Some(mut iter) = {etor_call} {{",
|
||||
indent = indent,
|
||||
open_paren = if output_binders.len() == 1 { "" } else { "(" },
|
||||
vars = output_binders.join(", "),
|
||||
close_paren = if output_binders.len() == 1 { "" } else { ")" },
|
||||
name = sig.full_name,
|
||||
args = input_values.join(", "),
|
||||
etor_call = etor_call,
|
||||
)
|
||||
.unwrap();
|
||||
false
|
||||
writeln!(
|
||||
code,
|
||||
"{indent} while let Some({bind_pattern}) = iter.next(ctx) {{",
|
||||
indent = indent,
|
||||
bind_pattern = bind_pattern,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(false, 2)
|
||||
}
|
||||
(false, false) => {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}if let Some({bind_pattern}) = {etor_call} {{",
|
||||
indent = indent,
|
||||
bind_pattern = bind_pattern,
|
||||
etor_call = etor_call,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(false, 1)
|
||||
}
|
||||
(true, false) => {
|
||||
writeln!(
|
||||
code,
|
||||
"{indent}let {bind_pattern} = {etor_call};",
|
||||
indent = indent,
|
||||
bind_pattern = bind_pattern,
|
||||
etor_call = etor_call,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(true, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
&PatternInst::Expr {
|
||||
@@ -646,7 +781,7 @@ impl<'a> Codegen<'a> {
|
||||
)
|
||||
.unwrap();
|
||||
self.define_val(&output, ctx, /* is_ref = */ false, ty);
|
||||
true
|
||||
(true, 0)
|
||||
}
|
||||
&PatternInst::Expr {
|
||||
ref seq, output_ty, ..
|
||||
@@ -658,7 +793,15 @@ impl<'a> Codegen<'a> {
|
||||
let mut returns = vec![];
|
||||
for (id, inst) in seq.insts.iter().enumerate() {
|
||||
let id = InstId(id);
|
||||
self.generate_expr_inst(code, id, inst, &subindent, &mut subctx, &mut returns);
|
||||
let new_scope = self.generate_expr_inst(
|
||||
code,
|
||||
id,
|
||||
inst,
|
||||
&subindent,
|
||||
&mut subctx,
|
||||
&mut returns,
|
||||
);
|
||||
assert!(!new_scope);
|
||||
}
|
||||
assert_eq!(returns.len(), 1);
|
||||
writeln!(code, "{}return Some({});", subindent, returns[0].1).unwrap();
|
||||
@@ -678,7 +821,7 @@ impl<'a> Codegen<'a> {
|
||||
.unwrap();
|
||||
self.define_val(&output, ctx, /* is_ref = */ false, output_ty);
|
||||
|
||||
false
|
||||
(false, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,6 +833,7 @@ impl<'a> Codegen<'a> {
|
||||
trie: &TrieNode,
|
||||
indent: &str,
|
||||
ctx: &mut BodyContext,
|
||||
is_multi: bool,
|
||||
) -> bool {
|
||||
log!("generate_body:\n{}", trie.pretty());
|
||||
let mut returned = false;
|
||||
@@ -704,21 +848,37 @@ impl<'a> Codegen<'a> {
|
||||
output.pos.pretty_print_line(&self.typeenv.filenames[..])
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// If this is a leaf node, generate the ExprSequence and return.
|
||||
let mut returns = vec![];
|
||||
let mut scopes = 0;
|
||||
let mut indent = indent.to_string();
|
||||
let orig_indent = indent.clone();
|
||||
for (id, inst) in output.insts.iter().enumerate() {
|
||||
let id = InstId(id);
|
||||
self.generate_expr_inst(code, id, inst, indent, ctx, &mut returns);
|
||||
let new_scope =
|
||||
self.generate_expr_inst(code, id, inst, &indent[..], ctx, &mut returns);
|
||||
if new_scope {
|
||||
scopes += 1;
|
||||
indent.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(returns.len(), 1);
|
||||
if is_multi {
|
||||
writeln!(code, "{}returns.push({});", indent, returns[0].1).unwrap();
|
||||
} else {
|
||||
writeln!(code, "{}return Some({});", indent, returns[0].1).unwrap();
|
||||
}
|
||||
|
||||
returned = true;
|
||||
for _ in 0..scopes {
|
||||
writeln!(code, "{}}}", orig_indent).unwrap();
|
||||
}
|
||||
|
||||
returned = !is_multi;
|
||||
}
|
||||
|
||||
&TrieNode::Decision { ref edges } => {
|
||||
let subindent = format!("{} ", indent);
|
||||
// If this is a decision node, generate each match op
|
||||
// in turn (in priority order). Gather together
|
||||
// adjacent MatchVariant ops with the same input and
|
||||
@@ -764,7 +924,14 @@ impl<'a> Codegen<'a> {
|
||||
// (possibly an empty one). Only use a `match` form if there
|
||||
// are at least two adjacent options.
|
||||
if last - i > 1 {
|
||||
self.generate_body_matches(code, depth, &edges[i..last], indent, ctx);
|
||||
self.generate_body_matches(
|
||||
code,
|
||||
depth,
|
||||
&edges[i..last],
|
||||
indent,
|
||||
ctx,
|
||||
is_multi,
|
||||
);
|
||||
i = last;
|
||||
continue;
|
||||
} else {
|
||||
@@ -777,16 +944,32 @@ impl<'a> Codegen<'a> {
|
||||
|
||||
match symbol {
|
||||
&TrieSymbol::EndOfMatch => {
|
||||
returned = self.generate_body(code, depth + 1, node, indent, ctx);
|
||||
returned = self.generate_body(
|
||||
code,
|
||||
depth + 1,
|
||||
node,
|
||||
indent,
|
||||
ctx,
|
||||
is_multi,
|
||||
);
|
||||
}
|
||||
&TrieSymbol::Match { ref op } => {
|
||||
let id = InstId(depth);
|
||||
let infallible =
|
||||
let (infallible, new_scopes) =
|
||||
self.generate_pattern_inst(code, id, op, indent, ctx);
|
||||
let i = if infallible { indent } else { &subindent[..] };
|
||||
let sub_returned =
|
||||
self.generate_body(code, depth + 1, node, i, ctx);
|
||||
if !infallible {
|
||||
let mut subindent = indent.to_string();
|
||||
for _ in 0..new_scopes {
|
||||
subindent.push_str(" ");
|
||||
}
|
||||
let sub_returned = self.generate_body(
|
||||
code,
|
||||
depth + 1,
|
||||
node,
|
||||
&subindent[..],
|
||||
ctx,
|
||||
is_multi,
|
||||
);
|
||||
for _ in 0..new_scopes {
|
||||
writeln!(code, "{}}}", indent).unwrap();
|
||||
}
|
||||
if infallible && sub_returned {
|
||||
@@ -810,6 +993,7 @@ impl<'a> Codegen<'a> {
|
||||
edges: &[TrieEdge],
|
||||
indent: &str,
|
||||
ctx: &mut BodyContext,
|
||||
is_multi: bool,
|
||||
) {
|
||||
let (input, input_ty) = match &edges[0].symbol {
|
||||
&TrieSymbol::Match {
|
||||
@@ -874,12 +1058,12 @@ impl<'a> Codegen<'a> {
|
||||
)
|
||||
.unwrap();
|
||||
let subindent = format!("{} ", indent);
|
||||
self.generate_body(code, depth + 1, node, &subindent, ctx);
|
||||
self.generate_body(code, depth + 1, node, &subindent, ctx, is_multi);
|
||||
writeln!(code, "{} }}", indent).unwrap();
|
||||
}
|
||||
|
||||
// Always add a catchall, because we don't do exhaustiveness
|
||||
// checking on the MatcHVariants.
|
||||
// checking on the MatchVariants.
|
||||
writeln!(code, "{} _ => {{}}", indent).unwrap();
|
||||
|
||||
writeln!(code, "{}}}", indent).unwrap();
|
||||
|
||||
@@ -105,6 +105,8 @@ pub enum PatternInst {
|
||||
output_tys: Vec<TypeId>,
|
||||
/// This extractor's term.
|
||||
term: TermId,
|
||||
/// Is this a multi-extractor?
|
||||
multi: bool,
|
||||
},
|
||||
|
||||
// NB: This has to go last, since it is infallible, so that when we sort
|
||||
@@ -162,6 +164,8 @@ pub enum ExprInst {
|
||||
term: TermId,
|
||||
/// Whether this constructor is infallible or not.
|
||||
infallible: bool,
|
||||
/// Is this a multi-constructor?
|
||||
multi: bool,
|
||||
},
|
||||
|
||||
/// Set the Nth return value. Produces no values.
|
||||
@@ -305,6 +309,7 @@ impl PatternSequence {
|
||||
output_tys: Vec<TypeId>,
|
||||
term: TermId,
|
||||
infallible: bool,
|
||||
multi: bool,
|
||||
) -> Vec<Value> {
|
||||
let inst = InstId(self.insts.len());
|
||||
let mut outs = vec![];
|
||||
@@ -319,6 +324,7 @@ impl PatternSequence {
|
||||
output_tys,
|
||||
term,
|
||||
infallible,
|
||||
multi,
|
||||
});
|
||||
outs
|
||||
}
|
||||
@@ -429,10 +435,11 @@ impl PatternSequence {
|
||||
panic!("Should have been expanded away")
|
||||
}
|
||||
TermKind::Decl {
|
||||
extractor_kind:
|
||||
Some(ExtractorKind::ExternalExtractor { infallible, .. }),
|
||||
extractor_kind: Some(ExtractorKind::ExternalExtractor { .. }),
|
||||
..
|
||||
} => {
|
||||
let ext_sig = termdata.extractor_sig(typeenv).unwrap();
|
||||
|
||||
// Evaluate all `input` args.
|
||||
let mut inputs = vec![];
|
||||
let mut input_tys = vec![];
|
||||
@@ -451,7 +458,8 @@ impl PatternSequence {
|
||||
input_tys,
|
||||
output_tys,
|
||||
term,
|
||||
*infallible,
|
||||
ext_sig.infallible,
|
||||
ext_sig.multi,
|
||||
);
|
||||
|
||||
for (pat, &val) in output_pats.iter().zip(arg_values.iter()) {
|
||||
@@ -521,6 +529,7 @@ impl ExprSequence {
|
||||
ty: TypeId,
|
||||
term: TermId,
|
||||
infallible: bool,
|
||||
multi: bool,
|
||||
) -> Value {
|
||||
let inst = InstId(self.insts.len());
|
||||
let inputs = inputs.iter().cloned().collect();
|
||||
@@ -529,6 +538,7 @@ impl ExprSequence {
|
||||
ty,
|
||||
term,
|
||||
infallible,
|
||||
multi,
|
||||
});
|
||||
Value::Expr { inst, output: 0 }
|
||||
}
|
||||
@@ -581,6 +591,7 @@ impl ExprSequence {
|
||||
}
|
||||
TermKind::Decl {
|
||||
constructor_kind: Some(ConstructorKind::InternalConstructor),
|
||||
multi,
|
||||
..
|
||||
} => {
|
||||
self.add_construct(
|
||||
@@ -588,11 +599,13 @@ impl ExprSequence {
|
||||
ty,
|
||||
term,
|
||||
/* infallible = */ false,
|
||||
*multi,
|
||||
)
|
||||
}
|
||||
TermKind::Decl {
|
||||
constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
|
||||
pure,
|
||||
multi,
|
||||
..
|
||||
} => {
|
||||
self.add_construct(
|
||||
@@ -600,6 +613,7 @@ impl ExprSequence {
|
||||
ty,
|
||||
term,
|
||||
/* infallible = */ !pure,
|
||||
*multi,
|
||||
)
|
||||
}
|
||||
TermKind::Decl {
|
||||
|
||||
@@ -290,6 +290,12 @@ impl<'a> Parser<'a> {
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let multi = if self.is_sym_str("multi") {
|
||||
self.symbol()?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let term = self.parse_ident()?;
|
||||
|
||||
@@ -307,6 +313,7 @@ impl<'a> Parser<'a> {
|
||||
arg_tys,
|
||||
ret_ty,
|
||||
pure,
|
||||
multi,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -236,6 +236,8 @@ pub enum TermKind {
|
||||
Decl {
|
||||
/// Whether the term is marked as `pure`.
|
||||
pure: bool,
|
||||
/// Whether the term is marked as `multi`.
|
||||
multi: bool,
|
||||
/// The kind of this term's constructor, if any.
|
||||
constructor_kind: Option<ConstructorKind>,
|
||||
/// The kind of this term's extractor, if any.
|
||||
@@ -290,6 +292,9 @@ pub struct ExternalSig {
|
||||
pub ret_tys: Vec<TypeId>,
|
||||
/// Whether this signature is infallible or not.
|
||||
pub infallible: bool,
|
||||
/// "Multiplicity": does the function return multiple values (via
|
||||
/// an iterator)?
|
||||
pub multi: bool,
|
||||
}
|
||||
|
||||
impl Term {
|
||||
@@ -353,6 +358,7 @@ impl Term {
|
||||
pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
|
||||
match &self.kind {
|
||||
TermKind::Decl {
|
||||
multi,
|
||||
extractor_kind:
|
||||
Some(ExtractorKind::ExternalExtractor {
|
||||
name, infallible, ..
|
||||
@@ -363,7 +369,8 @@ impl Term {
|
||||
full_name: format!("C::{}", tyenv.syms[name.index()]),
|
||||
param_tys: vec![self.ret_ty],
|
||||
ret_tys: self.arg_tys.clone(),
|
||||
infallible: *infallible,
|
||||
infallible: *infallible && !*multi,
|
||||
multi: *multi,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
@@ -374,6 +381,7 @@ impl Term {
|
||||
match &self.kind {
|
||||
TermKind::Decl {
|
||||
constructor_kind: Some(ConstructorKind::ExternalConstructor { name }),
|
||||
multi,
|
||||
pure,
|
||||
..
|
||||
} => Some(ExternalSig {
|
||||
@@ -381,10 +389,12 @@ impl Term {
|
||||
full_name: format!("C::{}", tyenv.syms[name.index()]),
|
||||
param_tys: self.arg_tys.clone(),
|
||||
ret_tys: vec![self.ret_ty],
|
||||
infallible: !pure,
|
||||
infallible: !pure && !*multi,
|
||||
multi: *multi,
|
||||
}),
|
||||
TermKind::Decl {
|
||||
constructor_kind: Some(ConstructorKind::InternalConstructor { .. }),
|
||||
multi,
|
||||
..
|
||||
} => {
|
||||
let name = format!("constructor_{}", tyenv.syms[self.name.index()]);
|
||||
@@ -398,6 +408,7 @@ impl Term {
|
||||
// matching at the toplevel (an entry point can
|
||||
// fail to rewrite).
|
||||
infallible: false,
|
||||
multi: *multi,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
@@ -854,6 +865,7 @@ impl TermEnv {
|
||||
constructor_kind: None,
|
||||
extractor_kind: None,
|
||||
pure: decl.pure,
|
||||
multi: decl.multi,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1014,8 +1026,18 @@ impl TermEnv {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
TermKind::Decl { extractor_kind, .. } => match extractor_kind {
|
||||
TermKind::Decl {
|
||||
multi,
|
||||
extractor_kind,
|
||||
..
|
||||
} => match extractor_kind {
|
||||
None => {
|
||||
if *multi {
|
||||
tyenv.report_error(
|
||||
ext.pos,
|
||||
"A term declared with `multi` cannot have an internal extractor.".to_string());
|
||||
continue;
|
||||
}
|
||||
*extractor_kind = Some(ExtractorKind::InternalExtractor { template });
|
||||
}
|
||||
Some(ext_kind) => {
|
||||
|
||||
Reference in New Issue
Block a user