[sc-410] Add untagged scott encoding option for ADTs

This commit is contained in:
Nicolas Abril 2024-02-07 16:19:07 +01:00
parent 291ff8ce10
commit 3e9b3d7e58
47 changed files with 770 additions and 248 deletions

48
Cargo.lock generated
View File

@ -68,12 +68,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.83"
@ -130,7 +124,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.48",
"syn",
]
[[package]]
@ -226,9 +220,8 @@ dependencies = [
"hvm-core",
"indexmap",
"insta",
"itertools 0.11.0",
"itertools",
"logos",
"shrinkwraprs",
"stdext",
"walkdir",
]
@ -256,15 +249,6 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.11.0"
@ -312,7 +296,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex-syntax",
"syn 2.0.48",
"syn",
]
[[package]]
@ -378,19 +362,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "shrinkwraprs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63e6744142336dfb606fe2b068afa2e1cca1ee6a5d8377277a92945d81fa331"
dependencies = [
"bitflags",
"itertools 0.8.2",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "similar"
version = "2.4.0"
@ -422,17 +393,6 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
@ -595,5 +555,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"syn",
]

View File

@ -27,9 +27,8 @@ hvm-core = { git = "https://github.com/HigherOrderCO/hvm-core" }
indexmap = "2.1.0"
itertools = "0.11.0"
logos = "0.13.0"
shrinkwraprs = "0.3.0"
[dev-dependencies]
insta = "1.34.0"
stdext = "0.3.1"
walkdir = "2.3.3"
insta = "1.34.0"

View File

@ -10,7 +10,7 @@ rustup-show:
rustup show
test:
cargo test --workspace
cargo insta test --workspace
clippy:
cargo clippy

View File

@ -10,10 +10,10 @@ use itertools::Itertools;
use net::{hvmc_to_net::hvmc_to_net, net_to_hvmc::nets_to_hvmc};
use std::time::Instant;
use term::{
book_to_nets, net_to_term,
net_to_term::ReadbackErrors,
book_to_nets,
net_to_term::{net_to_term, ReadbackErrors},
term_to_net::{HvmcNames, Labels},
Book, DefName, Name, Term,
AdtEncoding, Book, DefName, Name, Term,
};
pub mod hvmc_net;
@ -56,9 +56,9 @@ pub fn desugar_book(
let mut warnings = Vec::new();
let main = book.check_has_entrypoint(entrypoint)?;
book.check_shared_names()?;
book.generate_scott_adts();
book.encode_adts(opts.adt_encoding);
book.encode_builtins();
encode_pattern_matching(book, &mut warnings)?;
encode_pattern_matching(book, &mut warnings, opts.adt_encoding)?;
// sanity check
book.check_unbound_vars()?;
book.normalize_native_matches()?;
@ -77,7 +77,7 @@ pub fn desugar_book(
if opts.simplify_main {
book.simplify_main_ref(&main);
}
book.prune(Some(&main), opts.prune, &mut warnings);
book.prune(Some(&main), opts.prune, opts.adt_encoding, &mut warnings);
if opts.inline {
book.inline();
}
@ -87,7 +87,11 @@ pub fn desugar_book(
Ok((main, warnings))
}
pub fn encode_pattern_matching(book: &mut Book, warnings: &mut Vec<Warning>) -> Result<(), String> {
pub fn encode_pattern_matching(
book: &mut Book,
warnings: &mut Vec<Warning>,
adt_encoding: AdtEncoding,
) -> Result<(), String> {
book.check_arity()?;
book.resolve_ctrs_in_pats();
book.check_unbound_pats()?;
@ -102,7 +106,7 @@ pub fn encode_pattern_matching(book: &mut Book, warnings: &mut Vec<Warning>) ->
book.flatten_rules();
let def_types = book.infer_def_types()?;
book.check_exhaustive_patterns(&def_types)?;
book.encode_pattern_matching_functions(&def_types);
book.encode_pattern_matching_functions(&def_types, adt_encoding);
Ok(())
}
@ -119,10 +123,11 @@ pub fn run_book(
display_warnings(warning_opts, &warnings)?;
let debug_hook = run_opts.debug_hook(&book, &hvmc_names, &labels);
let debug_hook = run_opts.debug_hook(&book, &hvmc_names, &labels, &compile_opts.adt_encoding);
let (res_lnet, stats) = run_compiled(&core_book, mem_size, run_opts, debug_hook, &book.hvmc_entrypoint());
let net = hvmc_to_net(&res_lnet, &hvmc_names.hvmc_to_hvml);
let (res_term, readback_errors) = net_to_term(&net, &book, &labels, run_opts.linear);
let (res_term, readback_errors) =
net_to_term(&net, &book, &labels, run_opts.linear, compile_opts.adt_encoding);
let info = RunInfo { stats, readback_errors, net: res_lnet };
Ok((res_term, info))
}
@ -199,11 +204,12 @@ impl RunOpts {
book: &'a Book,
hvmc_names: &'a HvmcNames,
labels: &'a Labels,
adt_encoding: &'a AdtEncoding,
) -> Option<impl FnMut(&Net) + 'a> {
self.debug.then_some({
|net: &_| {
let net = hvmc_to_net(net, &hvmc_names.hvmc_to_hvml);
let (res_term, errors) = net_to_term(&net, book, labels, self.linear);
let (res_term, errors) = net_to_term(&net, book, labels, self.linear, *adt_encoding);
println!("{}{}\n---------------------------------------", errors.display(), res_term.display())
}
})
@ -212,6 +218,9 @@ impl RunOpts {
#[derive(Clone, Copy, Debug, Default)]
pub struct CompileOpts {
/// Selects the encoding for the ADT syntax.
pub adt_encoding: AdtEncoding,
/// Enables [term::transform::eta_reduction].
pub eta: bool,
@ -253,6 +262,7 @@ impl CompileOpts {
pre_reduce_refs: true,
merge: true,
inline: true,
adt_encoding: Default::default(),
}
}

View File

@ -1,8 +1,9 @@
use clap::{Args, CommandFactory, Parser, Subcommand};
use hvmc::ast::{show_book, show_net};
use hvml::{
check_book, compile_book, desugar_book, load_file_to_book, run_book, term::Name, CompileOpts, RunInfo,
RunOpts, WarnState, WarningOpts,
check_book, compile_book, desugar_book, load_file_to_book, run_book,
term::{AdtEncoding, Name},
CompileOpts, RunInfo, RunOpts, WarnState, WarningOpts,
};
use std::{path::PathBuf, vec::IntoIter};
@ -267,6 +268,8 @@ pub enum OptArgs {
NoMerge,
Inline,
NoInline,
AdtScott,
AdtTaggedScott,
}
impl OptArgs {
@ -295,6 +298,8 @@ impl OptArgs {
NoMerge => opts.merge = false,
Inline => opts.inline = true,
NoInline => opts.inline = false,
AdtScott => opts.adt_encoding = AdtEncoding::Scott,
AdtTaggedScott => opts.adt_encoding = AdtEncoding::TaggedScott,
}
}
opts

View File

@ -19,10 +19,7 @@ pub fn nets_to_hvmc(nets: HashMap<String, INet>, hvmc_names: &HvmcNames) -> Resu
}
/// Convert an inet-encoded definition into an hvmc AST inet.
pub fn net_to_hvmc(
inet: &INet,
hvml_to_hvmc_name: &HashMap<DefName, Val>,
) -> Result<Net, String> {
pub fn net_to_hvmc(inet: &INet, hvml_to_hvmc_name: &HashMap<DefName, Val>) -> Result<Net, String> {
let (net_root, redxs) = get_tree_roots(inet)?;
let mut port_to_var_id: HashMap<Port, VarId> = HashMap::new();
let root = if let Some(net_root) = net_root {

View File

@ -1,4 +1,4 @@
use super::{parser::parse_definition_book, Book, DefName, Origin, Pattern, Tag, TagName, Term, VarName};
use super::{parser::parse_definition_book, Book, DefName, Origin, Pattern, Term};
use hvmc::run::Val;
const BUILTINS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/term/builtins.hvm"));
@ -105,21 +105,3 @@ impl Pattern {
})
}
}
impl Tag {
pub fn string() -> Self {
Tag::adt_name(&TagName::new(STRING))
}
pub fn string_scons_head() -> Self {
Tag::adt_field(&DefName::new(STRING), &DefName::new(SCONS), &VarName::new(HEAD))
}
pub fn list() -> Self {
Tag::adt_name(&TagName::new(LIST))
}
pub fn list_lcons_head() -> Self {
Tag::adt_field(&DefName::new(LIST), &DefName::new(LCONS), &VarName::new(HEAD))
}
}

View File

@ -248,7 +248,7 @@ impl ReadbackError {
ReadbackError::InvalidBind => write!(f, "Invalid Bind"),
ReadbackError::InvalidAdt => write!(f, "Invalid Adt"),
ReadbackError::UnexpectedTag(exp, fnd) => {
write!(f, "Unexpected tag found during Adt readback, expected '#{}', but found ", exp)?;
write!(f, "Unexpected tag found during Adt readback, expected '{}', but found ", exp.display())?;
match fnd {
Tag::Static => write!(f, "no tag"),

View File

@ -192,6 +192,14 @@ pub struct Adt {
pub origin: Origin,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum AdtEncoding {
Scott,
#[default]
TaggedScott,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Default)]
pub struct Name(pub Arc<String>);
@ -730,11 +738,11 @@ impl Type {
Type::None => vec![],
Type::Any => vec![],
Type::Tup => vec![Pattern::Tup(
Box::new(Pattern::Var(Some(Name::new("fst")))),
Box::new(Pattern::Var(Some(Name::new("snd")))),
Box::new(Pattern::Var(Some(Name::new("%fst")))),
Box::new(Pattern::Var(Some(Name::new("%snd")))),
)],
Type::Num => {
vec![Pattern::Num(MatchNum::Zero), Pattern::Num(MatchNum::Succ(Some(Some(Name::new("pred")))))]
vec![Pattern::Num(MatchNum::Zero), Pattern::Num(MatchNum::Succ(Some(Some(Name::new("%pred")))))]
}
Type::Adt(adt) => {
// TODO: Should return just a ref to ctrs and not clone.

View File

@ -1,6 +1,8 @@
use crate::{
net::{INet, NodeId, NodeKind::*, Port, SlotId, ROOT},
term::{num_to_name, term_to_net::Labels, Book, MatchNum, Op, Pattern, Tag, TagName, Term, Val, VarName},
term::{
num_to_name, term_to_net::Labels, AdtEncoding, Book, MatchNum, Op, Pattern, Tag, Term, Val, VarName,
},
};
use hvmc::run::Loc;
use indexmap::IndexSet;
@ -8,7 +10,13 @@ use std::collections::{HashMap, HashSet};
// TODO: Display scopeless lambdas as such
/// Converts an Interaction-INet to a Lambda Calculus term
pub fn net_to_term(net: &INet, book: &Book, labels: &Labels, linear: bool) -> (Term, ReadbackErrors) {
pub fn net_to_term(
net: &INet,
book: &Book,
labels: &Labels,
linear: bool,
adt_encoding: AdtEncoding,
) -> (Term, ReadbackErrors) {
let mut reader = Reader {
net,
labels,
@ -40,7 +48,7 @@ pub fn net_to_term(net: &INet, book: &Book, labels: &Labels, linear: bool) -> (T
debug_assert_eq!(result, None);
}
reader.resugar_adts(&mut term);
reader.resugar_adts(&mut term, adt_encoding);
(term, reader.errors)
}
@ -467,7 +475,7 @@ pub enum ReadbackError {
InvalidAdt,
InvalidAdtMatch,
InvalidStrTerm(Term),
UnexpectedTag(TagName, Tag),
UnexpectedTag(Tag, Tag),
}
impl ReadbackError {

View File

@ -1,17 +1,26 @@
use super::{
net_to_term::{ReadbackError, Reader}, transform::encode_adts::adt_field_tag, Adt, DefName, Pattern, Tag, Term, LIST, LNIL, SNIL, STRING
net_to_term::{ReadbackError, Reader},
Adt, AdtEncoding, DefName, Pattern, Tag, Term, LCONS, LIST, LNIL, SNIL, STRING,
};
use std::borrow::BorrowMut;
impl<'a> Reader<'a> {
pub fn resugar_adts(&mut self, term: &mut Term) {
pub fn resugar_adts(&mut self, term: &mut Term, adt_encoding: AdtEncoding) {
match adt_encoding {
// No way of resugaring simple scott encoded terms.
AdtEncoding::Scott => (),
AdtEncoding::TaggedScott => self.resugar_tagged_scott(term),
}
}
pub fn resugar_tagged_scott(&mut self, term: &mut Term) {
match term {
Term::Lam { tag: Tag::Named(adt_name), bod, .. } | Term::Chn { tag: Tag::Named(adt_name), bod, .. } => {
let Some((adt_name, adt)) = self.book.adts.get_key_value(adt_name) else {
return self.resugar_adts(bod);
return self.resugar_tagged_scott(bod);
};
self.resugar_adt_cons(term, adt, adt_name);
self.resugar_ctr_tagged_scott(term, adt, adt_name);
match adt_name.0.as_str() {
STRING => {
@ -24,16 +33,16 @@ impl<'a> Reader<'a> {
}
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => self.resugar_adts(bod),
Term::Lam { bod, .. } | Term::Chn { bod, .. } => self.resugar_tagged_scott(bod),
Term::App { tag: Tag::Named(adt_name), fun, arg } => {
let Some((adt_name, adt)) = self.book.adts.get_key_value(adt_name) else {
self.resugar_adts(fun);
self.resugar_adts(arg);
self.resugar_tagged_scott(fun);
self.resugar_tagged_scott(arg);
return;
};
self.resugar_adt_match(term, adt_name, adt);
self.resugar_match_tagged_scott(term, adt_name, adt);
}
Term::App { fun: fst, arg: snd, .. }
@ -42,14 +51,14 @@ impl<'a> Reader<'a> {
| Term::Dup { val: fst, nxt: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Opx { fst, snd, .. } => {
self.resugar_adts(fst);
self.resugar_adts(snd);
self.resugar_tagged_scott(fst);
self.resugar_tagged_scott(snd);
}
Term::Match { scrutinee, arms } => {
self.resugar_adts(scrutinee);
self.resugar_tagged_scott(scrutinee);
for (_, arm) in arms {
self.resugar_adts(arm);
self.resugar_tagged_scott(arm);
}
}
@ -80,7 +89,7 @@ impl<'a> Reader<'a> {
/// // Which gets resugared as:
/// (Some (Some None))
/// ```
fn resugar_adt_cons(&mut self, term: &mut Term, adt: &Adt, adt_name: &DefName) {
fn resugar_ctr_tagged_scott(&mut self, term: &mut Term, adt: &Adt, adt_name: &DefName) {
let mut app = &mut *term;
let mut current_arm = None;
@ -108,15 +117,15 @@ impl<'a> Reader<'a> {
for field in ctr_args.iter().rev() {
self.deref(cur);
let adt_field_tag = adt_field_tag(adt_name, ctr, field);
let expected_tag = Tag::adt_field(adt_name, ctr, field);
match cur {
Term::App { tag: Tag::Named(tag_name), .. } if tag_name == &adt_field_tag => {
Term::App { tag, .. } if tag == &expected_tag => {
let Term::App { tag, fun, .. } = cur.borrow_mut() else { unreachable!() };
*tag = Tag::Static;
cur = fun;
}
Term::App { tag, .. } => return self.error(ReadbackError::UnexpectedTag(adt_field_tag, tag.clone())),
Term::App { tag, .. } => return self.error(ReadbackError::UnexpectedTag(expected_tag, tag.clone())),
_ => return self.error(ReadbackError::InvalidAdt),
}
}
@ -129,7 +138,7 @@ impl<'a> Reader<'a> {
*cur = Term::r#ref(ctr);
*term = std::mem::take(app);
self.resugar_adts(term);
self.resugar_tagged_scott(term);
}
/// Reconstructs adt-tagged applications as a match term
@ -160,7 +169,7 @@ impl<'a> Reader<'a> {
/// (None): λ* 3
/// } b)
/// ```
fn resugar_adt_match(&mut self, term: &mut Term, adt_name: &DefName, adt: &Adt) {
fn resugar_match_tagged_scott(&mut self, term: &mut Term, adt_name: &DefName, adt: &Adt) {
let mut cur = &mut *term;
let mut arms = Vec::new();
@ -174,10 +183,10 @@ impl<'a> Reader<'a> {
for field in ctr_args {
self.deref(arm);
let adt_field_tag = adt_field_tag(adt_name, ctr, field);
let expected_tag = Tag::adt_field(adt_name, ctr, field);
match arm {
Term::Lam { tag: Tag::Named(tag), .. } if tag == &adt_field_tag => {
Term::Lam { tag, .. } if tag == &expected_tag => {
let Term::Lam { nam, bod, .. } = arm.borrow_mut() else { unreachable!() };
args.push(nam.clone().map_or(Pattern::Var(None), |x| Pattern::Var(Some(x))));
@ -185,13 +194,12 @@ impl<'a> Reader<'a> {
}
_ => {
if let Term::Lam { tag, .. } = arm {
self.error(ReadbackError::UnexpectedTag(adt_field_tag.clone(), tag.clone()));
self.error(ReadbackError::UnexpectedTag(expected_tag.clone(), tag.clone()));
}
let nam = self.namegen.unique();
args.push(Pattern::Var(Some(nam.clone())));
let tag = Tag::Named(adt_field_tag);
*arm = Term::tagged_app(tag, std::mem::take(arm), Term::Var { nam });
*arm = Term::tagged_app(expected_tag, std::mem::take(arm), Term::Var { nam });
}
}
}
@ -207,7 +215,7 @@ impl<'a> Reader<'a> {
let arms = arms.into_iter().rev().map(|(pat, term)| (pat, std::mem::take(term))).collect();
*term = Term::Match { scrutinee, arms };
self.resugar_adts(term);
self.resugar_tagged_scott(term);
}
fn resugar_string(term: &mut Term) -> Term {
@ -236,16 +244,19 @@ impl<'a> Reader<'a> {
fn resugar_list(term: &mut Term) -> Term {
match term {
// (LCons el tail)
Term::App { fun: box Term::App { fun: ctr, arg: el, .. }, arg: tail, .. } => {
Term::App {
fun: box Term::App { fun: box Term::Ref { def_name: ctr }, arg: el, .. },
arg: tail,
..
} => {
let tail = Self::resugar_list(tail);
if let Term::List { els: tail } = tail {
if ctr.as_str() == LCONS && let Term::List { els: tail } = tail {
let mut els = vec![*el.clone()];
els.extend(tail);
Term::List { els }
} else {
// Make the constructor again
let fun = Term::App { tag: Tag::Static, fun: ctr.clone(), arg: el.clone() };
Term::App { tag: Tag::Static, fun: Box::new(fun), arg: Box::new(tail) }
Term::call(Term::Ref { def_name: ctr.clone() }, [*el.clone(), tail])
}
}
// (LNil)

View File

@ -1,7 +1,7 @@
use std::collections::{hash_map::Entry, HashMap};
use crate::{
term::{Adt, Book, DefName, Origin, Tag, Term},
term::{Adt, AdtEncoding, Book, DefName, Origin, Tag, Term, LCONS, LNIL, SCONS, SNIL},
Warning,
};
use indexmap::IndexSet;
@ -27,13 +27,19 @@ type Definitions = HashMap<DefName, Used>;
impl Book {
/// If `prune_all`, removes all unused definitions and adts starting from Main.
/// Otherwise, prunes only the builtins not accessible from any non-built-in definition
pub fn prune(&mut self, main: Option<&DefName>, prune_all: bool, warnings: &mut Vec<Warning>) {
pub fn prune(
&mut self,
main: Option<&DefName>,
prune_all: bool,
adt_encoding: AdtEncoding,
warnings: &mut Vec<Warning>,
) {
let mut used = Definitions::new();
if let Some(main) = main {
let def = self.defs.get(main).unwrap();
used.insert(main.clone(), Used::Main);
self.find_used_definitions(&def.rule().body, Used::Main, &mut used);
self.find_used_definitions(&def.rule().body, Used::Main, &mut used, adt_encoding);
}
// Even if we don't prune all the defs, we need check what built-ins are accessible through user code
@ -49,18 +55,17 @@ impl Book {
_ => {}
}
self.find_used_definitions(&rule.body, Used::Unused, &mut used);
self.find_used_definitions(&rule.body, Used::Unused, &mut used, adt_encoding);
}
}
}
}
let names = self.defs.keys().cloned().collect::<IndexSet<DefName>>();
// Filter defs from the 'used' hashmap that are not accessible from main
let filter = |(name, used)| if used == Used::Unused { None } else { Some(name) };
let used: IndexSet<DefName> = used.into_iter().filter_map(filter).collect();
let names = self.defs.keys().cloned().collect::<IndexSet<DefName>>();
let unused = names.difference(&used).cloned();
self.prune_unused(unused, prune_all, warnings);
@ -83,69 +88,88 @@ impl Book {
}
/// Finds all used definitions on every term that can have a def_id.
fn find_used_definitions(&self, term: &Term, used: Used, uses: &mut Definitions) {
fn find_used_definitions(
&self,
term: &Term,
used: Used,
uses: &mut Definitions,
adt_encoding: AdtEncoding,
) {
self.find_manual_adt_encoding(term, uses, adt_encoding);
match term {
Term::Ref { def_name } => match self.ctrs.get(def_name) {
Some(name) => self.insert_ctrs_used(name, uses),
None => self.insert_used(def_name, used, uses),
Some(name) => self.insert_ctrs_used(name, uses, adt_encoding),
None => self.insert_used(def_name, used, uses, adt_encoding),
},
Term::Lam { tag: Tag::Named(name), bod, .. } | Term::Chn { tag: Tag::Named(name), bod, .. } => {
// mark constructors of the Adt with same name as the tag in case of the user
// manually encoded a constructor with tagged lambdas
self.insert_ctrs_used(name, uses);
self.find_used_definitions(bod, used, uses);
Term::Lam { bod, .. } | Term::Chn { bod, .. } => {
self.find_used_definitions(bod, used, uses, adt_encoding)
}
Term::Lam { bod, .. } | Term::Chn { bod, .. } => self.find_used_definitions(bod, used, uses),
Term::Let { val, nxt, .. } | Term::Dup { val, nxt, .. } => {
self.find_used_definitions(val, used, uses);
self.find_used_definitions(nxt, used, uses);
}
Term::App { tag, fun, arg } => {
// mark constructors of the Adt with same name as the tag in case of the user
// manually encoded a pattern matching with tagged applications
if let Tag::Named(name) = tag {
self.insert_ctrs_used(name, uses);
}
self.find_used_definitions(fun, used, uses);
self.find_used_definitions(arg, used, uses);
}
Term::Sup { fst, snd, .. } | Term::Tup { fst, snd } | Term::Opx { fst, snd, .. } => {
self.find_used_definitions(fst, used, uses);
self.find_used_definitions(snd, used, uses);
Term::Let { val: fst, nxt: snd, .. }
| Term::Dup { val: fst, nxt: snd, .. }
| Term::App { fun: fst, arg: snd, .. }
| Term::Sup { fst, snd, .. }
| Term::Tup { fst, snd }
| Term::Opx { fst, snd, .. } => {
self.find_used_definitions(fst, used, uses, adt_encoding);
self.find_used_definitions(snd, used, uses, adt_encoding);
}
Term::Match { scrutinee, arms } => {
self.find_used_definitions(scrutinee, used, uses);
self.find_used_definitions(scrutinee, used, uses, adt_encoding);
for (_, term) in arms {
self.find_used_definitions(term, used, uses);
self.find_used_definitions(term, used, uses, adt_encoding);
}
}
Term::Var { .. }
| Term::Lnk { .. }
| Term::Num { .. }
| Term::Str { .. }
| Term::List { .. }
| Term::Era
| Term::Invalid => (),
Term::List { els } => {
self.insert_ctrs_used(&DefName::new(LCONS), uses, adt_encoding);
self.insert_ctrs_used(&DefName::new(LNIL), uses, adt_encoding);
for term in els {
self.find_used_definitions(term, used, uses, adt_encoding);
}
}
Term::Str { .. } => {
self.insert_ctrs_used(&DefName::new(SCONS), uses, adt_encoding);
self.insert_ctrs_used(&DefName::new(SNIL), uses, adt_encoding);
}
Term::Var { .. } | Term::Lnk { .. } | Term::Num { .. } | Term::Era | Term::Invalid => (),
}
}
fn insert_used(&self, def_name: &DefName, used: Used, uses: &mut Definitions) {
let Entry::Vacant(e) = uses.entry(def_name.clone()) else { return };
e.insert(used);
// This needs to be done for each rule in case the pass it's ran from has not encoded the pattern match
// E.g.: the `flatten_rules` golden test
for rule in &self.defs[def_name].rules {
self.find_used_definitions(&rule.body, used, uses);
/// If the current term is a manual encoding of a constructor or adt, we marked it as used.
fn find_manual_adt_encoding(&self, term: &Term, uses: &mut Definitions, adt_encoding: AdtEncoding) {
match adt_encoding {
AdtEncoding::Scott => (),
// If using the tagged scott encoding of ADTs, we also mark a constructor
// as used if the user manually encoded a ctr or match (or any other use of the ctr tags).
AdtEncoding::TaggedScott => match term {
Term::Lam { tag: Tag::Named(name), .. }
| Term::Chn { tag: Tag::Named(name), .. }
| Term::App { tag: Tag::Named(name), .. } => {
// We don't check dup/sup tags because they use a separate label scope.
self.insert_ctrs_used(name, uses, adt_encoding);
}
_ => (),
},
}
}
fn insert_ctrs_used(&self, name: &DefName, uses: &mut Definitions) {
fn insert_used(&self, def_name: &DefName, used: Used, uses: &mut Definitions, adt_encoding: AdtEncoding) {
if let Entry::Vacant(e) = uses.entry(def_name.clone()) {
e.insert(used);
// This needs to be done for each rule in case the pass it's ran from has not encoded the pattern match
// E.g.: the `flatten_rules` golden test
for rule in &self.defs[def_name].rules {
self.find_used_definitions(&rule.body, used, uses, adt_encoding);
}
}
}
fn insert_ctrs_used(&self, name: &DefName, uses: &mut Definitions, adt_encoding: AdtEncoding) {
if let Some(Adt { ctrs, .. }) = self.adts.get(name) {
for (ctr, _) in ctrs {
self.insert_used(ctr, Used::Adt, uses);
self.insert_used(ctr, Used::Adt, uses, adt_encoding);
}
}
}

View File

@ -1,15 +1,15 @@
use crate::term::{Book, DefName, Definition, Rule, Tag, TagName, Term, VarName};
use crate::term::{AdtEncoding, Book, DefName, Definition, Rule, Tag, Term, VarName};
impl Book {
pub fn generate_scott_adts(&mut self) {
pub fn encode_adts(&mut self, adt_encoding: AdtEncoding) {
let mut defs = vec![];
for (adt_name, adt) in &self.adts {
for (ctr_name, args) in &adt.ctrs {
let ctrs: Vec<_> = adt.ctrs.keys().cloned().collect();
let lam = make_lam(adt_name, args.clone(), ctrs, ctr_name);
let body = encode_ctr(adt_name, args.clone(), ctrs, ctr_name, adt_encoding);
let rules = vec![Rule { pats: vec![], body: lam, origin: adt.origin }];
let rules = vec![Rule { pats: vec![], body, origin: adt.origin }];
let def = Definition { name: ctr_name.clone(), rules };
defs.push((ctr_name.clone(), def));
}
@ -18,22 +18,33 @@ impl Book {
}
}
fn make_lam(adt_name: &DefName, ctr_args: Vec<VarName>, ctrs: Vec<DefName>, ctr_name: &DefName) -> Term {
let ctr = Term::Var { nam: ctr_name.clone() };
let app = ctr_args.iter().cloned().fold(ctr, |acc, nam| {
let tag = Tag::Named(adt_field_tag(adt_name, ctr_name, &nam));
Term::tagged_app(tag, acc, Term::Var { nam })
});
let lam = ctrs
.into_iter()
.rev()
.fold(app, |acc, arg| Term::tagged_lam(Tag::Named(adt_name.clone()), arg.clone(), acc));
ctr_args.into_iter().rev().fold(lam, |acc, arg| Term::named_lam(arg, acc))
}
pub fn adt_field_tag(adt_name: &DefName, ctr_name: &DefName, field_name: &VarName) -> TagName {
format!("{}.{}.{}", adt_name, ctr_name, field_name).into()
fn encode_ctr(
adt_name: &DefName,
ctr_args: Vec<VarName>,
ctrs: Vec<DefName>,
ctr_name: &DefName,
adt_encoding: AdtEncoding,
) -> Term {
match adt_encoding {
// λarg1 λarg2 λarg3 λctr1 λctr2 (ctr2 arg1 arg2 arg3)
AdtEncoding::Scott => {
let ctr = Term::Var { nam: ctr_name.clone() };
let app = ctr_args.iter().cloned().fold(ctr, Term::arg_call);
let lam = ctrs.into_iter().rev().fold(app, |acc, arg| Term::named_lam(arg.clone(), acc));
ctr_args.into_iter().rev().fold(lam, |acc, arg| Term::named_lam(arg, acc))
}
// λarg1 λarg2 #type λctr1 #type λctr2 #type.ctr2.arg2(#type.ctr2.arg1(ctr2 arg1) arg2)
AdtEncoding::TaggedScott => {
let ctr = Term::Var { nam: ctr_name.clone() };
let app = ctr_args.iter().cloned().fold(ctr, |acc, nam| {
let tag = Tag::adt_field(adt_name, ctr_name, &nam);
Term::tagged_app(tag, acc, Term::Var { nam })
});
let lam = ctrs
.into_iter()
.rev()
.fold(app, |acc, arg| Term::tagged_lam(Tag::adt_name(adt_name), arg.clone(), acc));
ctr_args.into_iter().rev().fold(lam, |acc, arg| Term::named_lam(arg, acc))
}
}
}

View File

@ -1,17 +1,21 @@
use crate::term::{
check::type_check::DefinitionTypes, transform::unique_names::UniqueNameGenerator, Book, DefName,
Definition, MatchNum, Pattern, Rule, Tag, Term, Type, VarName,
check::type_check::DefinitionTypes, transform::unique_names::UniqueNameGenerator, AdtEncoding, Book,
DefName, Definition, MatchNum, Pattern, Rule, Tag, Term, Type, VarName,
};
impl Book {
pub fn encode_pattern_matching_functions(&mut self, def_types: &DefinitionTypes) {
pub fn encode_pattern_matching_functions(
&mut self,
def_types: &DefinitionTypes,
adt_encoding: AdtEncoding,
) {
let def_names = self.defs.keys().cloned().collect::<Vec<_>>();
for def_name in def_names {
let def_type = &def_types[&def_name];
let is_matching_def = def_type.iter().any(|t| matches!(t, Type::Adt(_) | Type::Tup | Type::Num));
if is_matching_def {
make_pattern_matching_def(self, &def_name, def_type);
make_pattern_matching_def(self, &def_name, def_type, adt_encoding);
} else {
// For functions with only one rule that doesn't pattern match,
// we just move the variables from arg to body.
@ -31,7 +35,12 @@ fn make_non_pattern_matching_def(def: &mut Definition) {
/// For functions that do pattern match,
/// we break them into a tree of small matching functions
/// with the original rule bodies at the end.
fn make_pattern_matching_def(book: &mut Book, def_name: &DefName, def_type: &[Type]) {
fn make_pattern_matching_def(
book: &mut Book,
def_name: &DefName,
def_type: &[Type],
adt_encoding: AdtEncoding,
) {
// First push the pattern bound vars into the rule body
let rules = &mut book.defs.get_mut(def_name).unwrap().rules;
for rule in rules.iter_mut() {
@ -42,7 +51,7 @@ fn make_pattern_matching_def(book: &mut Book, def_name: &DefName, def_type: &[Ty
// Generate scott-encoded pattern matching
let def = &book.defs[def_name];
let crnt_rules = (0 .. def.rules.len()).collect();
let mut new_body = make_pattern_matching_case(book, def, def_type, crnt_rules, vec![]);
let mut new_body = make_pattern_matching_case(book, def, def_type, crnt_rules, vec![], adt_encoding);
// Simplify the generated term
new_body.eta_reduction();
@ -73,6 +82,7 @@ fn make_pattern_matching_case(
def_type: &[Type],
crnt_rules: Vec<usize>,
match_path: Vec<Pattern>,
adt_encoding: AdtEncoding,
) -> Term {
// This is safe since we check exhaustiveness earlier.
let fst_rule_idx = crnt_rules[0];
@ -100,9 +110,9 @@ fn make_pattern_matching_case(
if is_fst_rule_irrefutable {
// First rule will always be selected, generate leaf case.
make_leaf_case(book, fst_rule, fst_rule_idx, match_path, old_args, new_args)
make_leaf_case(book, fst_rule, fst_rule_idx, match_path, old_args, new_args, adt_encoding)
} else {
make_match_case(book, def, def_type, crnt_rules, match_path, old_args, new_args)
make_match_case(book, def, def_type, crnt_rules, match_path, old_args, new_args, adt_encoding)
}
}
@ -116,6 +126,7 @@ fn make_match_case(
match_path: Vec<Pattern>,
old_args: Vec<VarName>,
new_args: Vec<VarName>,
adt_encoding: AdtEncoding,
) -> Term {
let next_arg_idx = match_path.len();
let next_type = &def_type[next_arg_idx];
@ -135,45 +146,61 @@ fn make_match_case(
.collect();
let mut match_path = match_path.clone();
match_path.push(pat.clone());
let next_case = make_pattern_matching_case(book, def, def_type, next_rules, match_path);
let next_case = make_pattern_matching_case(book, def, def_type, next_rules, match_path, adt_encoding);
next_cases.push(next_case);
}
// Encode the current pattern matching, calling the subfunctions
let match_var = VarName::new("x");
// The match term itself
let term = match next_type {
let term = encode_match(next_type, &match_var, next_cases.into_iter(), adt_encoding);
// The calls to the args of previous matches
let term = old_args.iter().chain(new_args.iter()).cloned().fold(term, Term::arg_call);
// Lambda for pattern matched value
let term = Term::named_lam(match_var, term);
// The bindings of the previous args
let term = add_arg_lams(term, old_args, new_args, match_path.last(), book, adt_encoding);
term
}
fn encode_match(
match_type: &Type,
match_var: &VarName,
mut arms: impl Iterator<Item = Term>,
adt_encoding: AdtEncoding,
) -> Term {
match match_type {
Type::None => unreachable!(),
Type::Any => Term::arg_call(std::mem::take(&mut next_cases[0]), match_var.clone()),
// (arm[0] x)
Type::Any => Term::arg_call(arms.next().unwrap(), match_var.clone()),
// let (%fst, %snd) = x; (arm[0] %fst %snd)
Type::Tup => Term::Let {
pat: Pattern::Tup(
Box::new(Pattern::Var(Some(VarName::new("%fst")))),
Box::new(Pattern::Var(Some(VarName::new("%snd")))),
),
val: Box::new(Term::Var { nam: match_var.clone() }),
nxt: Box::new(Term::call(std::mem::take(&mut next_cases[0]), [
Term::Var { nam: VarName::new("%fst") },
Term::Var { nam: VarName::new("%snd") },
])),
nxt: Box::new(Term::call(arms.next().unwrap(), [Term::Var { nam: VarName::new("%fst") }, Term::Var {
nam: VarName::new("%snd"),
}])),
},
// match x {0: arm[0]; +: arm[1]}
Type::Num => Term::Match {
scrutinee: Box::new(Term::Var { nam: match_var.clone() }),
arms: vec![
(Pattern::Num(MatchNum::Zero), std::mem::take(&mut next_cases[0])),
(Pattern::Num(MatchNum::Succ(None)), std::mem::take(&mut next_cases[1])),
(Pattern::Num(MatchNum::Zero), arms.next().unwrap()),
(Pattern::Num(MatchNum::Succ(None)), arms.next().unwrap()),
],
},
Type::Adt(adt_name) => {
Term::tagged_call(Tag::Named(adt_name.clone()), Term::Var { nam: match_var.clone() }, next_cases)
}
};
// The calls to the args of previous matches
let term = old_args.iter().chain(new_args.iter()).cloned().fold(term, Term::arg_call);
// Lambda for pattern matched value
let term = Term::named_lam(match_var, term);
// The bindings of the previous args
let term = add_arg_lams(term, old_args, new_args, match_path.last(), book);
term
Type::Adt(adt_name) => match adt_encoding {
// (x arm[0] arm[1] ...)
AdtEncoding::Scott => Term::call(Term::Var { nam: match_var.clone() }, arms),
// #adt_name(x arm[0] arm[1] ...)
AdtEncoding::TaggedScott => {
Term::tagged_call(Tag::adt_name(adt_name), Term::Var { nam: match_var.clone() }, arms)
}
},
}
}
/// Builds the function calling one of the original rule bodies.
@ -184,6 +211,7 @@ fn make_leaf_case(
match_path: Vec<Pattern>,
old_args: Vec<VarName>,
new_args: Vec<VarName>,
adt_encoding: AdtEncoding,
) -> Term {
let args = &mut old_args.iter().chain(new_args.iter()).cloned();
@ -207,7 +235,7 @@ fn make_leaf_case(
}
});
// Add the lambdas for the matched args.
let term = add_arg_lams(term, old_args, new_args, match_path.last(), book);
let term = add_arg_lams(term, old_args, new_args, match_path.last(), book, adt_encoding);
term
}
@ -226,6 +254,7 @@ fn add_arg_lams(
new_args: Vec<VarName>,
last_pat: Option<&Pattern>,
book: &Book,
adt_encoding: AdtEncoding,
) -> Term {
// Add lams for old vars
let term = old_args.into_iter().rev().fold(term, |term, arg| Term::named_lam(arg, term));
@ -237,8 +266,12 @@ fn add_arg_lams(
new_args.into_iter().rev().zip(args.iter().rev()).fold(term, |term, (new_arg, pat)| {
let adt = &book.ctrs[ctr];
let Pattern::Var(Some(field)) = pat else { unreachable!() };
let tag = Tag::Named(format!("{adt}.{ctr}.{field}").into());
Term::Lam { tag, nam: Some(new_arg), bod: Box::new(term) }
match adt_encoding {
AdtEncoding::Scott => Term::Lam { tag: Tag::Static, nam: Some(new_arg), bod: Box::new(term) },
AdtEncoding::TaggedScott => {
Term::Lam { tag: Tag::adt_field(adt, ctr, field), nam: Some(new_arg), bod: Box::new(term) }
}
}
})
}
// New vars from other pats

View File

@ -5,7 +5,7 @@ use hvml::{
run_book,
term::{
load_book::do_parse_book, net_to_term::net_to_term, parser::parse_term, term_to_compat_net,
term_to_net::Labels, Book, Name, Term,
term_to_net::Labels, AdtEncoding, Book, Name, Term,
},
CompileOpts, RunOpts, WarningOpts,
};
@ -13,6 +13,7 @@ use insta::assert_snapshot;
use itertools::Itertools;
use std::{
collections::HashMap,
fmt::Write,
fs,
path::{Path, PathBuf},
sync::{Arc, RwLock},
@ -141,7 +142,8 @@ fn readback_lnet() {
let net = do_parse_net(code)?;
let book = Book::default();
let compat_net = hvmc_to_net(&net, &Default::default());
let (term, errors) = net_to_term(&compat_net, &book, &Labels::default(), false);
let (term, errors) =
net_to_term(&compat_net, &book, &Labels::default(), false, hvml::term::AdtEncoding::TaggedScott);
Ok(format!("{}{}", errors.display(), term.display()))
})
}
@ -154,13 +156,13 @@ fn flatten_rules() {
book.check_shared_names()?;
book.encode_builtins();
book.resolve_ctrs_in_pats();
book.generate_scott_adts();
book.encode_adts(AdtEncoding::TaggedScott);
book.desugar_let_destructors();
book.desugar_implicit_match_binds();
book.check_unbound_pats()?;
book.extract_adt_matches(&mut Vec::new())?;
book.flatten_rules();
book.prune(main.as_ref(), false, &mut Vec::new());
book.prune(main.as_ref(), false, AdtEncoding::TaggedScott, &mut Vec::new());
Ok(book.to_string())
})
}
@ -176,15 +178,21 @@ fn parse_file() {
#[test]
fn encode_pattern_match() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let main = book.check_has_entrypoint(None).ok();
book.check_shared_names()?;
book.generate_scott_adts();
book.encode_builtins();
encode_pattern_matching(&mut book, &mut Vec::new())?;
book.prune(main.as_ref(), false, &mut Vec::new());
book.merge_definitions(&main.unwrap_or_default());
Ok(book.to_string())
let mut result = String::new();
for adt_encoding in [AdtEncoding::TaggedScott, AdtEncoding::Scott] {
let mut book = do_parse_book(code, path)?;
let main = book.check_has_entrypoint(None).ok();
book.check_shared_names()?;
book.encode_adts(adt_encoding);
book.encode_builtins();
encode_pattern_matching(&mut book, &mut Vec::new(), adt_encoding)?;
book.prune(main.as_ref(), false, adt_encoding, &mut Vec::new());
book.merge_definitions(&main.clone().unwrap_or_default());
writeln!(result, "{adt_encoding:?}:").unwrap();
writeln!(result, "{book}\n").unwrap();
}
Ok(result)
})
}
@ -198,7 +206,7 @@ fn desugar_file() {
}
#[test]
#[ignore = "To not delay golden tests execution"]
#[ignore = "to not delay golden tests execution"]
fn hangs() {
let expected_normalization_time = 1;

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/adt_tup_era.hvm
---
TaggedScott:
(Main) = (Foo (Pair 1 5))
(Foo) = λa #Tuple (a #Tuple.Pair.a λb #Tuple.Pair.b λc (Foo$F0 b c))
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/adt_tup_era.hvm
(Foo$F0) = λa #Tuple (a #Tuple.Pair.a λb #Tuple.Pair.b λc λd b)
(Pair) = λa λb #Tuple λPair #Tuple.Pair.b (#Tuple.Pair.a (Pair a) b)
Scott:
(Main) = (Foo (Pair 1 5))
(Foo) = λa (a λb λc (Foo$F0 b c))
(Foo$F0) = λa (a λb λc λd b)
(Pair) = λa λb λPair (Pair a b)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/and3.hvm
---
TaggedScott:
(main) = (And (F, (T, F)))
(And) = λa let (b, c) = a; (And$F0 b c)
@ -13,3 +14,18 @@ input_file: tests/golden_tests/encode_pattern_match/and3.hvm
(F) = #Bool λT #Bool λF F
(T) = #Bool λT #Bool λF T
Scott:
(main) = (And (F, (T, F)))
(And) = λa let (b, c) = a; (And$F0 b c)
(And$F0) = λa (a λb let (c, d) = b; (And$F0$F0 c d) λ* F)
(And$F0$F0) = λa (a λb (b T F) λ* F)
(F) = λT λF F
(T) = λT λF T

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/bool.hvm
---
TaggedScott:
(not) = λa #bool (a false true)
(and) = λa #bool (a λb #bool (b true false) λc #bool (c false false))
@ -13,3 +14,18 @@ input_file: tests/golden_tests/encode_pattern_match/bool.hvm
(false) = #bool λtrue #bool λfalse false
(true) = #bool λtrue #bool λfalse true
Scott:
(not) = λa (a false true)
(and) = λa (a λb (b true false) λc (c false false))
(and2) = λa (a λb b λc false)
(and3_$_and4) = λa (a λb (b true false) λc false)
(false) = λtrue λfalse false
(true) = λtrue λfalse true

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/bool_tup.hvm
---
TaggedScott:
(main) = (foo (F, T))
(foo) = λa let (b, c) = a; (foo$F0 b c)
@ -11,3 +12,16 @@ input_file: tests/golden_tests/encode_pattern_match/bool_tup.hvm
(F) = #Bool λT #Bool λF F
(T) = #Bool λT #Bool λF T
Scott:
(main) = (foo (F, T))
(foo) = λa let (b, c) = a; (foo$F0 b c)
(foo$F0) = λa (a λb b λ* F)
(F) = λT λF F
(T) = λT λF T

View File

@ -2,6 +2,14 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/box.hvm
---
TaggedScott:
(unbox) = λa #box (a #box.new.val λb b)
(new) = λval #box λnew #box.new.val (new val)
Scott:
(unbox) = λa (a λb b)
(new) = λval λnew (new val)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/common.hvm
---
TaggedScott:
(West) = #Direction λNorth #Direction λSouth #Direction λEast #Direction λWest West
(East) = #Direction λNorth #Direction λSouth #Direction λEast #Direction λWest East
@ -35,3 +36,40 @@ input_file: tests/golden_tests/encode_pattern_match/common.hvm
(Yellow) = #Light λRed #Light λYellow #Light λGreen Yellow
(Green) = #Light λRed #Light λYellow #Light λGreen Green
Scott:
(West) = λNorth λSouth λEast λWest West
(East) = λNorth λSouth λEast λWest East
(South) = λNorth λSouth λEast λWest South
(North) = λNorth λSouth λEast λWest North
(Filled) = λvalue λFilled λEmpty (Filled value)
(Empty) = λFilled λEmpty Empty
(Some) = λx λSome λNone (Some x)
(None) = λSome λNone None
(Ok) = λa λOk λErr (Ok a)
(Err) = λb λOk λErr (Err b)
(Cons) = λx λxs λCons λNil (Cons x xs)
(Nil) = λCons λNil Nil
(True) = λTrue λFalse True
(False) = λTrue λFalse False
(Red) = λRed λYellow λGreen Red
(Yellow) = λRed λYellow λGreen Yellow
(Green) = λRed λYellow λGreen Green

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/concat.hvm
---
TaggedScott:
(main) = (String.concat (SCons 97 (SCons 98 SNil)) (SCons 99 (SCons 100 SNil)))
(String.concat) = λa λb (String.concat$match$1 a b)
@ -11,3 +12,16 @@ input_file: tests/golden_tests/encode_pattern_match/concat.hvm
(SNil) = #String λSCons #String λSNil SNil
(String.concat$match$1) = λa #String (a #String.SCons.head λb #String.SCons.tail λc λd (SCons b (String.concat c d)) λe e)
Scott:
(main) = (String.concat (SCons 97 (SCons 98 SNil)) (SCons 99 (SCons 100 SNil)))
(String.concat) = λa λb (String.concat$match$1 a b)
(SCons) = λhead λtail λSCons λSNil (SCons head tail)
(SNil) = λSCons λSNil SNil
(String.concat$match$1) = λa (a λb λc λd (SCons b (String.concat c d)) λe e)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/concat_def.hvm
---
TaggedScott:
(main) = (concat (SCons 97 (SCons 98 SNil)) (SCons 99 (SCons 100 SNil)))
(concat) = λa #String (a #String.SCons.head λb #String.SCons.tail λc λd (SCons b (concat c d)) λe e)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/concat_def.hvm
(SCons) = λhead λtail #String λSCons #String λSNil #String.SCons.tail (#String.SCons.head (SCons head) tail)
(SNil) = #String λSCons #String λSNil SNil
Scott:
(main) = (concat (SCons 97 (SCons 98 SNil)) (SCons 99 (SCons 100 SNil)))
(concat) = λa (a λb λc λd (SCons b (concat c d)) λe e)
(SCons) = λhead λtail λSCons λSNil (SCons head tail)
(SNil) = λSCons λSNil SNil

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/def_tups.hvm
---
TaggedScott:
(main) = (go (1, (2, (3, (4, 5)))))
(go) = λa let (b, c) = a; (go$F0 b c)
@ -11,3 +12,16 @@ input_file: tests/golden_tests/encode_pattern_match/def_tups.hvm
(go$F0$F0) = λa λb λc let (d, e) = c; (go$F0$F0$F0 a b d e)
(go$F0$F0$F0) = λa λb λc λd let (e, f) = d; (+ (+ (+ (+ f e) c) b) a)
Scott:
(main) = (go (1, (2, (3, (4, 5)))))
(go) = λa let (b, c) = a; (go$F0 b c)
(go$F0) = λa λb let (c, d) = b; (go$F0$F0 a c d)
(go$F0$F0) = λa λb λc let (d, e) = c; (go$F0$F0$F0 a b d e)
(go$F0$F0$F0) = λa λb λc λd let (e, f) = d; (+ (+ (+ (+ f e) c) b) a)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/definition_merge.hvm
---
TaggedScott:
(False) = #Bool λTrue #Bool λFalse False
(Foo$F3_$_Foo$F2) = λa #Bool (a λb #Bool (b 3 3) λc #Bool (c 3 3))
@ -17,3 +18,22 @@ input_file: tests/golden_tests/encode_pattern_match/definition_merge.hvm
(True) = #Bool λTrue #Bool λFalse True
(Foo) = λa #Either (a #Either.Left.value λb λc (#Either (c #Either.Left.value λd λe (Foo$F0 e d) #Either.Right.value λf λg (Foo$F1 g f)) b) #Either.Right.value λh λi (#Either (i #Either.Left.value λj λk (Foo$F3_$_Foo$F2 k j) #Either.Right.value λl λm (Foo$F3_$_Foo$F2 m l)) h))
Scott:
(False) = λTrue λFalse False
(Foo$F3_$_Foo$F2) = λa (a λb (b 3 3) λc (c 3 3))
(Foo$F1) = λa (a λb (b 2 2) λc (c 2 2))
(Foo$F0) = λa (a λb (b 1 1) λc (c 1 1))
(Left) = λvalue λLeft λRight (Left value)
(Right) = λvalue λLeft λRight (Right value)
(True) = λTrue λFalse True
(Foo) = λa (a λb λc (c λd λe (Foo$F0 e d) λf λg (Foo$F1 g f) b) λh λi (i λj λk (Foo$F3_$_Foo$F2 k j) λl λm (Foo$F3_$_Foo$F2 m l) h))

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/expr.hvm
---
TaggedScott:
(Div) = #Op λAdd #Op λSub #Op λMul #Op λDiv Div
(Mul) = #Op λAdd #Op λSub #Op λMul #Op λDiv Mul
@ -27,3 +28,32 @@ input_file: tests/golden_tests/encode_pattern_match/expr.hvm
(Tup) = λfst λsnd #Expr λVar #Expr λNum #Expr λApp #Expr λFun #Expr λIf #Expr λLet #Expr λDup #Expr λTup #Expr λOp2 #Expr.Tup.snd (#Expr.Tup.fst (Tup fst) snd)
(Op2) = λop λfst λsnd #Expr λVar #Expr λNum #Expr λApp #Expr λFun #Expr λIf #Expr λLet #Expr λDup #Expr λTup #Expr λOp2 #Expr.Op2.snd (#Expr.Op2.fst (#Expr.Op2.op (Op2 op) fst) snd)
Scott:
(Div) = λAdd λSub λMul λDiv Div
(Mul) = λAdd λSub λMul λDiv Mul
(Sub) = λAdd λSub λMul λDiv Sub
(Add) = λAdd λSub λMul λDiv Add
(Var) = λname λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Var name)
(Num) = λval λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Num val)
(App) = λfun λarg λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (App fun arg)
(Fun) = λname λbody λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Fun name body)
(If) = λcond λthen λelse λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (If cond then else)
(Let) = λbind λval λnext λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Let bind val next)
(Dup) = λfst λsnd λval λnext λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Dup fst snd val next)
(Tup) = λfst λsnd λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Tup fst snd)
(Op2) = λop λfst λsnd λVar λNum λApp λFun λIf λLet λDup λTup λOp2 (Op2 op fst snd)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/flatten_era_pat.hvm
---
TaggedScott:
(main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0)
(Fn1) = λa let (b, c) = a; λd (Fn1$F0 b c d)
@ -17,3 +18,22 @@ input_file: tests/golden_tests/encode_pattern_match/flatten_era_pat.hvm
(Fn2$F0$F0) = λa λb λc let (d, e) = c; d
(Fn1$F0) = λa λb let (c, d) = b; λ* c
Scott:
(main) = (Fn2 ((1, 2), (3, (4, (5, 6)))) 0)
(Fn1) = λa let (b, c) = a; λd (Fn1$F0 b c d)
(Fn2) = λa let (b, c) = a; (Fn2$F0 b c)
(Fn3) = λa let (b, c) = a; λd (Fn3$F0 b c d)
(Fn3$F0) = λa match a { 0: λ* λ* 0; +: λb λ* λ* b }
(Fn2$F0) = λa λb let (c, d) = b; (Fn2$F0$F0 a c d)
(Fn2$F0$F0) = λa λb λc let (d, e) = c; d
(Fn1$F0) = λa λb let (c, d) = b; λ* c

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/is_some_some.hvm
---
TaggedScott:
(main) = (some_some (Some 1))
(some_some) = λa #Option (a #Option.Some.x λb (some_some$F0 b) 0)
@ -11,3 +12,16 @@ input_file: tests/golden_tests/encode_pattern_match/is_some_some.hvm
(None) = #Option λSome #Option λNone None
(Some) = λx #Option λSome #Option λNone #Option.Some.x (Some x)
Scott:
(main) = (some_some (Some 1))
(some_some) = λa (a λb (some_some$F0 b) 0)
(some_some$F0) = λa (a λb 1 0)
(None) = λSome λNone None
(Some) = λx λSome λNone (Some x)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/list_merge_sort.hvm
---
TaggedScott:
(If) = λa #Bool (a λb λc b λd λe e)
(Pure) = λx (Cons x Nil)
@ -27,3 +28,32 @@ input_file: tests/golden_tests/encode_pattern_match/list_merge_sort.hvm
(True) = #Bool λTrue #Bool λFalse True
(False) = #Bool λTrue #Bool λFalse False
Scott:
(If) = λa (a λb λc b λd λe e)
(Pure) = λx (Cons x Nil)
(Map) = λa (a λb λc λd (Cons (d b) (Map c d)) λe Nil)
(MergeSort) = λcmp λxs (Unpack cmp (Map xs Pure))
(Unpack) = λa λb (b λc λd λe (Unpack$F0 e c d) λf Nil a)
(MergePair) = λa λb (b λc λd λe (MergePair$F0 e c d) λf Nil a)
(Merge) = λa λb (b λc λd λe λf (f λg λh λi λj λk (If (i j g) let l = (Cons g h); (Cons j (Merge i k l)) let m = (Cons j k); (Cons g (Merge i m h))) λn λo λp (Cons o p) e c d) λq λr r a)
(MergePair$F0) = λa λb λc (c λd λe λf λg (Cons (Merge f g d) (MergePair f e)) λh λi (Cons i Nil) a b)
(Unpack$F0) = λa λb λc (c λd λe λf λg (Unpack f (MergePair f (Cons g (Cons d e)))) λh λi i a b)
(Nil) = λCons λNil Nil
(Cons) = λhead λtail λCons λNil (Cons head tail)
(True) = λTrue λFalse True
(False) = λTrue λFalse False

View File

@ -0,0 +1,27 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_fn.hvm
---
TaggedScott:
(main) = *
(Foo) = λa #String (a #String.SCons.head λb #String.SCons.tail λc 1 0)
(Bar) = λa #List (a #List.LCons.head λb #List.LCons.tail λc 0 1)
(SCons) = λhead λtail #String λSCons #String λSNil #String.SCons.tail (#String.SCons.head (SCons head) tail)
(SNil) = #String λSCons #String λSNil SNil
(LCons) = λhead λtail #List λLCons #List λLNil #List.LCons.tail (#List.LCons.head (LCons head) tail)
(LNil) = #List λLCons #List λLNil LNil
Scott:
(main) = *
(Foo) = λa (a λb λc 1 0)
(Bar) = λa (a λb λc 0 1)

View File

@ -0,0 +1,25 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/list_str_encoding_undeclared_map.hvm
---
TaggedScott:
(main) = λa λb let a = (main$match$1 a); let b = (main$match$2 b); (a, b)
(SCons) = λhead λtail #String λSCons #String λSNil #String.SCons.tail (#String.SCons.head (SCons head) tail)
(SNil) = #String λSCons #String λSNil SNil
(LCons) = λhead λtail #List λLCons #List λLNil #List.LCons.tail (#List.LCons.head (LCons head) tail)
(LNil) = #List λLCons #List λLNil LNil
(main$match$1) = λa #String (a #String.SCons.head λb #String.SCons.tail λc 1 2)
(main$match$2) = λa #List (a #List.LCons.head λb #List.LCons.tail λc 1 2)
Scott:
(main) = λa λb let a = (main$match$2_$_main$match$1 a); let b = (main$match$2_$_main$match$1 b); (a, b)
(main$match$2_$_main$match$1) = λa (a λb λc 1 2)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_in_arm.hvm
---
TaggedScott:
(main) = λx (main$match$1 x)
(main$match$1) = λa #bool (a λ$x $x λb b)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_in_arm.hv
(F) = #bool λT #bool λF F
(T) = #bool λT #bool λF T
Scott:
(main) = λx (main$match$1 x)
(main$match$1) = λa (a λ$x $x λb b)
(F) = λT λF F
(T) = λT λF T

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_var.hvm
---
TaggedScott:
(main) = *
(None) = #Maybe λNone #Maybe λSome None
@ -13,3 +14,18 @@ input_file: tests/golden_tests/encode_pattern_match/match_adt_unscoped_var.hvm
(Foo) = λ$x let %temp%scrutinee = (Some 1); (Bar$match$1_$_Foo$match$1 %temp%scrutinee $x)
(Bar) = (let %temp%scrutinee = (Some 1); (Bar$match$1_$_Foo$match$1 %temp%scrutinee $x) λ$x *)
Scott:
(main) = *
(None) = λNone λSome None
(Some) = λval λNone λSome (Some val)
(Bar$match$1_$_Foo$match$1) = λa (a λb b λc λd c)
(Foo) = λ$x let %temp%scrutinee = (Some 1); (Bar$match$1_$_Foo$match$1 %temp%scrutinee $x)
(Bar) = (let %temp%scrutinee = (Some 1); (Bar$match$1_$_Foo$match$1 %temp%scrutinee $x) λ$x *)

View File

@ -2,6 +2,14 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/match_bind.hvm
---
TaggedScott:
(main) = cheese
(cheese) = let num = (+ 2 3); match num { 0: 653323; +num-1: (+ num num-1) }
Scott:
(main) = cheese
(cheese) = let num = (+ 2 3); match num { 0: 653323; +num-1: (+ num num-1) }

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/match_list_sugar.hvm
---
TaggedScott:
(main) = let %temp%scrutinee = LNil; (main$match$1 %temp%scrutinee)
(main$match$1) = λa #List (a #List.LCons.head λb #List.LCons.tail λc 1 0)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/match_list_sugar.hvm
(LNil) = #List λLCons #List λLNil LNil
(LCons) = λhead λtail #List λLCons #List λLNil #List.LCons.tail (#List.LCons.head (LCons head) tail)
Scott:
(main) = let %temp%scrutinee = LNil; (main$match$1 %temp%scrutinee)
(main$match$1) = λa (a λb λc 1 0)
(LNil) = λLCons λLNil LNil
(LCons) = λhead λtail λLCons λLNil (LCons head tail)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/match_syntax.hvm
---
TaggedScott:
(head) = λx (head$match$1 x)
(head$match$1) = λa #List_ (a #List_.Cons.x λb #List_.Cons.xs λc b Nil)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/match_syntax.hvm
(Nil) = #List_ λCons #List_ λNil Nil
(Cons) = λx λxs #List_ λCons #List_ λNil #List_.Cons.xs (#List_.Cons.x (Cons x) xs)
Scott:
(head) = λx (head$match$1 x)
(head$match$1) = λa (a λb λc b Nil)
(Nil) = λCons λNil Nil
(Cons) = λx λxs λCons λNil (Cons x xs)

View File

@ -2,6 +2,14 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/merge_recursive.hvm
---
TaggedScott:
(foo_2_$_bar_2) = λa λb (a, b)
(foo_1_$_bar_1) = λx (x foo_2_$_bar_2)
Scott:
(foo_2_$_bar_2) = λa λb (a, b)
(foo_1_$_bar_1) = λx (x foo_2_$_bar_2)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/nested_let_tup.hvm
---
TaggedScott:
(main) = let %temp%scrutinee = let %temp%scrutinee = (10, ((1, ((2, 3), 4)), 3)); (main$match$1 %temp%scrutinee); (main$match$3 %temp%scrutinee)
(main$match$3$F0) = λa λb let (c, d) = b; (main$match$2 c)
@ -13,3 +14,18 @@ input_file: tests/golden_tests/encode_pattern_match/nested_let_tup.hvm
(main$match$2) = λa let (b, c) = a; b
(main$match$1) = λa let (b, c) = a; (main$match$1$F0 b c)
Scott:
(main) = let %temp%scrutinee = let %temp%scrutinee = (10, ((1, ((2, 3), 4)), 3)); (main$match$1 %temp%scrutinee); (main$match$3 %temp%scrutinee)
(main$match$3$F0) = λa λb let (c, d) = b; (main$match$2 c)
(main$match$1$F0) = λa λb let (c, d) = b; c
(main$match$3) = λa let (b, c) = a; (main$match$3$F0 b c)
(main$match$2) = λa let (b, c) = a; b
(main$match$1) = λa let (b, c) = a; (main$match$1$F0 b c)

View File

@ -1,11 +0,0 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/no_literal_builtin.hvm
---
(main) = *
(Foo) = λa #String (a #String.SCons.head λb #String.SCons.tail λc 1 0)
(SCons) = λhead λtail #String λSCons #String λSNil #String.SCons.tail (#String.SCons.head (SCons head) tail)
(SNil) = #String λSCons #String λSNil SNil

View File

@ -2,6 +2,14 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/no_patterns.hvm
---
TaggedScott:
(Id_$_Id2) = λx x
(Pair) = λa λb (a, b)
Scott:
(Id_$_Id2) = λx x
(Pair) = λa λb (a, b)

View File

@ -2,8 +2,18 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/non_matching_fst_arg.hvm
---
TaggedScott:
(Foo) = λa λb (#bool (b λc (Foo c c) λd d) a)
(false) = #bool λtrue #bool λfalse false
(true) = #bool λtrue #bool λfalse true
Scott:
(Foo) = λa λb (b λc (Foo c c) λd d a)
(false) = λtrue λfalse false
(true) = λtrue λfalse true

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/ntup_sum.hvm
---
TaggedScott:
(main) = (ntupSum ((1, 3), (3, (2, 1))))
(ntupSum) = λa let (b, c) = a; (ntupSum$F0 b c)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/ntup_sum.hvm
(ntupSum$F0) = λa let (b, c) = a; λd let (e, f) = d; (ntupSum$F0$F0 b c e f)
(ntupSum$F0$F0) = λa λb λc λd let (e, f) = d; (+ a (+ b (+ c (+ e f))))
Scott:
(main) = (ntupSum ((1, 3), (3, (2, 1))))
(ntupSum) = λa let (b, c) = a; (ntupSum$F0 b c)
(ntupSum$F0) = λa let (b, c) = a; λd let (e, f) = d; (ntupSum$F0$F0 b c e f)
(ntupSum$F0$F0) = λa λb λc λd let (e, f) = d; (+ a (+ b (+ c (+ e f))))

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/pattern_match_encoding.hvm
---
TaggedScott:
(main) = (Foo A 2)
(Foo) = λa #MyType (a #MyType.A.a λb 100 #MyType.B.b λc 200 #MyType.C.c λd 200 #MyType.D.d1 λe #MyType.D.d2 λf 200 #MyType.E.e1 λg #MyType.E.e2 λh 200)
@ -15,3 +16,20 @@ input_file: tests/golden_tests/encode_pattern_match/pattern_match_encoding.hvm
(B) = λb #MyType λA #MyType λB #MyType λC #MyType λD #MyType λE #MyType.B.b (B b)
(A) = λa #MyType λA #MyType λB #MyType λC #MyType λD #MyType λE #MyType.A.a (A a)
Scott:
(main) = (Foo A 2)
(Foo) = λa (a λb 100 λc 200 λd 200 λe λf 200 λg λh 200)
(E) = λe1 λe2 λA λB λC λD λE (E e1 e2)
(D) = λd1 λd2 λA λB λC λD λE (D d1 d2)
(C) = λc λA λB λC λD λE (C c)
(B) = λb λA λB λC λD λE (B b)
(A) = λa λA λB λC λD λE (A a)

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/var_only.hvm
---
TaggedScott:
(main) = λx match x { false: Foo; true: False }
(Foo) = λa λb λf (f a)
@ -9,3 +10,14 @@ input_file: tests/golden_tests/encode_pattern_match/var_only.hvm
(True) = #Bool λFalse #Bool λTrue True
(False) = #Bool λFalse #Bool λTrue False
Scott:
(main) = λx match x { false: Foo; true: False }
(Foo) = λa λb λf (f a)
(True) = λFalse λTrue True
(False) = λFalse λTrue False

View File

@ -2,6 +2,7 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/encode_pattern_match/weekday.hvm
---
TaggedScott:
(main) = (λx x Saturday)
(Sunday) = #Weekday λMonday #Weekday λTuesday #Weekday λWednesday #Weekday λThursday #Weekday λFriday #Weekday λSaturday #Weekday λSunday Sunday
@ -17,3 +18,22 @@ input_file: tests/golden_tests/encode_pattern_match/weekday.hvm
(Tuesday) = #Weekday λMonday #Weekday λTuesday #Weekday λWednesday #Weekday λThursday #Weekday λFriday #Weekday λSaturday #Weekday λSunday Tuesday
(Wednesday) = #Weekday λMonday #Weekday λTuesday #Weekday λWednesday #Weekday λThursday #Weekday λFriday #Weekday λSaturday #Weekday λSunday Wednesday
Scott:
(main) = (λx x Saturday)
(Sunday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Sunday
(Saturday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Saturday
(Friday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Friday
(Thursday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Thursday
(Monday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Monday
(Tuesday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Tuesday
(Wednesday) = λMonday λTuesday λWednesday λThursday λFriday λSaturday λSunday Wednesday