diff --git a/CHANGELOG.md b/CHANGELOG.md index d6716bd2..d90b4f46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,12 @@ and this project does not currently adhere to a particular versioning scheme. ### Fixed -- Fixed readback of numeric operations. ([#467][gh-467]) +- Fix readback of numeric operations. ([#467][gh-467]) ### Added -- `log` and `atan2` builtin functions. ([#583][gh-583]) +- Add `log` and `atan2` builtin functions. ([#583][gh-583]) +- Create new type of top-level definition for writing native HVM definitions. ([#586][gh-586]) ## [0.2.35] - 2024-06-06 @@ -30,7 +31,7 @@ and this project does not currently adhere to a particular versioning scheme. ### Added -- Create `<=` and `>=` operators. ([#451][gh-451]) +- Add syntax for "less than or equal" `<=` and "greater than or equal" `>=` numeric operators. ([#451][gh-451]) ## [0.2.33] - 2024-06-05 @@ -340,4 +341,5 @@ and this project does not currently adhere to a particular versioning scheme. [gh-526]: https://github.com/HigherOrderCO/Bend/issues/526 [gh-528]: https://github.com/HigherOrderCO/Bend/issues/528 [gh-583]: https://github.com/HigherOrderCO/Bend/issues/583 +[gh-586]: https://github.com/HigherOrderCO/Bend/issues/586 [Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.35...HEAD diff --git a/Cargo.lock b/Cargo.lock index 1680320a..a451eab4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys", ] @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -92,9 +92,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -126,9 +126,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -192,9 +192,9 @@ checksum = "809e18805660d7b6b2e2b9f316a5099521b5998d5cba4dda11b5157a21aaef03" [[package]] name = "hvm" -version = "2.0.17" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf1c2a5333c940ab1c5b6c64e0f3242739332446b572b07a87f46753c9f69e" +checksum = "281675413d2dd76cb1c53ec0d318bb672f5d70fb825929af6bcadd9b7f78c11b" dependencies = [ "TSPL", "cc", @@ -266,9 +266,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "loaned" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16fbe026127b8ff026fcc7419ddf201d47807295690de255bb68fced2ba15af" +checksum = "a4c980a418236e2d8f7c239f73e49afc38e7f71772fcd3fc723d95c3d93a7591" [[package]] name = "num_cpus" @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -366,9 +366,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "walkdir" diff --git a/Cargo.toml b/Cargo.toml index 6cc9ecde..4ca2da6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ cli = ["dep:clap"] TSPL = "0.0.12" clap = { version = "4.4.1", features = ["derive"], optional = true } highlight_error = "0.1.1" -hvm = "=2.0.17" +hvm = "=2.0.19" indexmap = "2.2.3" interner = "0.2.1" itertools = "0.11.0" diff --git a/docs/syntax.md b/docs/syntax.md index 758aaca1..03fd1fca 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -1172,3 +1172,26 @@ The syntax above is desugared to: ``` (Nat.succ (Nat.succ (Nat.succ List.nil))) ``` + +# Native HVM definitions + +```py +# This function causes two ports to be linked and returns *. +# This can be used to interpret a lambda as an application and apply something to it for example. +# It can be used like this: `let * = (link_ports @x x y)` +hvm link_ports: + (a (b *)) + & (c a) ~ (d e) + & (e b) ~ (d c) +``` + +It's also possible to define functions using HVM syntax. This can be +thought of as a way to write "HVM assembly" directly in a Bend program. +You can find the reference of this syntax in the [HVM paper](https://github.com/HigherOrderCO/HVM/blob/main/paper/PAPER.pdf). + +This is meant for writing things that would otherwise be hard or +impossible to write in normal Bend syntax. + +It will also ignore all term-level compiler passes and so can be +useful for writing programs with exact behaviour that won't ever be +changed or optimized by the compiler. \ No newline at end of file diff --git a/src/fun/check/unbound_refs.rs b/src/fun/check/unbound_refs.rs index 74f8d480..837ace1c 100644 --- a/src/fun/check/unbound_refs.rs +++ b/src/fun/check/unbound_refs.rs @@ -1,6 +1,6 @@ use crate::{ diagnostics::Diagnostics, - fun::{Ctx, Definitions, Name, Term}, + fun::{Book, Ctx, Name, Term}, maybe_grow, }; use std::collections::HashSet; @@ -11,7 +11,7 @@ impl Ctx<'_> { for def in self.book.defs.values() { let mut unbounds = HashSet::new(); for rule in def.rules.iter() { - rule.body.check_unbound_refs(&self.book.defs, &mut unbounds); + rule.body.check_unbound_refs(self.book, &mut unbounds); } for unbound in unbounds { self.info.add_rule_error(format!("Reference to undefined function '{unbound}'"), def.name.clone()); @@ -22,15 +22,15 @@ impl Ctx<'_> { } impl Term { - pub fn check_unbound_refs(&self, defs: &Definitions, unbounds: &mut HashSet) { + pub fn check_unbound_refs(&self, book: &Book, unbounds: &mut HashSet) { maybe_grow(|| { if let Term::Ref { nam } = self { - if !defs.contains_key(nam) { + if !(book.defs.contains_key(nam) || book.hvm_defs.contains_key(nam)) { unbounds.insert(nam.clone()); } } for child in self.children() { - child.check_unbound_refs(defs, unbounds); + child.check_unbound_refs(book, unbounds); } }) } diff --git a/src/fun/display.rs b/src/fun/display.rs index c888c293..e4180c0f 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -221,7 +221,11 @@ impl fmt::Display for Definition { impl fmt::Display for Book { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", DisplayJoin(|| self.defs.values(), "\n\n")) + write!(f, "{}", DisplayJoin(|| self.defs.values(), "\n\n"))?; + for def in self.hvm_defs.values() { + writeln!(f, "hvm {}:\n{}\n", def.name, def.body.show())?; + } + Ok(()) } } @@ -289,7 +293,14 @@ fn var_as_str(nam: &Option) -> &str { impl Book { pub fn display_pretty(&self) -> impl fmt::Display + '_ { - display!("{}", DisplayJoin(|| self.defs.values().map(|def| def.display_pretty()), "\n\n")) + display!( + "{}\n{}", + DisplayJoin(|| self.defs.values().map(|def| def.display_pretty()), "\n\n"), + DisplayJoin( + || self.hvm_defs.values().map(|def| display!("hvm {}:\n{}", def.name, def.body.show())), + "\n" + ) + ) } } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 045bd0f4..d9667a24 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -35,13 +35,16 @@ impl Ctx<'_> { /// The representation of a program. #[derive(Debug, Clone, Default)] pub struct Book { - /// The function definitions. + /// Function definitions. pub defs: Definitions, - /// The algebraic datatypes defined by the program + /// HVM native function definitions. + pub hvm_defs: HvmDefinitions, + + /// Algebraic datatype definitions. pub adts: Adts, - /// To which type does each constructor belong to. + /// Map of constructor name to type name. pub ctrs: Constructors, /// A custom or default "main" entrypoint. @@ -49,6 +52,7 @@ pub struct Book { } pub type Definitions = IndexMap; +pub type HvmDefinitions = IndexMap; pub type Adts = IndexMap; pub type Constructors = IndexMap; @@ -60,6 +64,14 @@ pub struct Definition { pub builtin: bool, } +/// An HVM native definition. +#[derive(Debug, Clone)] +pub struct HvmDefinition { + pub name: Name, + pub body: hvm::ast::Net, + pub builtin: bool, +} + /// A pattern matching rule of a definition. #[derive(Debug, Clone, Default, PartialEq)] pub struct Rule { diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 068e1c5a..95d5144f 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1,9 +1,11 @@ +use std::ops::Range; + use crate::{ fun::{ - display::DisplayFn, Adt, Book, CtrField, Definition, FanKind, MatchRule, Name, Num, Op, Pattern, Rule, - Tag, Term, STRINGS, + display::DisplayFn, Adt, Book, CtrField, Definition, FanKind, HvmDefinition, MatchRule, Name, Num, Op, + Pattern, Rule, Tag, Term, STRINGS, }, - imp::parser::PyParser, + imp::{parser::PyParser, Enum, Variant}, maybe_grow, }; use highlight_error::highlight_error; @@ -76,7 +78,7 @@ impl<'a> TermParser<'a> { let (obj, nxt_indent) = prs.parse_object(indent)?; self.index = prs.index; let end_idx = *self.index(); - prs.add_object(obj, &mut book, ini_idx, end_idx, builtin)?; + self.add_object(obj, &mut book, ini_idx..end_idx, builtin)?; indent = nxt_indent; last_rule = None; continue; @@ -88,7 +90,7 @@ impl<'a> TermParser<'a> { let (def, nxt_indent) = prs.parse_def(indent)?; self.index = prs.index; let end_idx = *self.index(); - prs.add_def(def, &mut book, ini_idx, end_idx, builtin)?; + self.add_imp_def(def, &mut book, ini_idx..end_idx, builtin)?; indent = nxt_indent; last_rule = None; continue; @@ -101,50 +103,43 @@ impl<'a> TermParser<'a> { let _ = self.labelled(|p| p.parse_top_level_name(), "datatype name")?; + // Imp type definition if self.starts_with(":") { let mut prs = PyParser { input: self.input, index: rewind_index }; let (r#enum, nxt_indent) = prs.parse_type(indent)?; self.index = prs.index; let end_idx = *self.index(); - prs.add_type(r#enum, &mut book, ini_idx, end_idx, builtin)?; + self.add_imp_type(r#enum, &mut book, ini_idx..end_idx, builtin)?; indent = nxt_indent; last_rule = None; continue; + // Fun type definition } else { self.index = rewind_index; let (nam, adt) = self.parse_datatype(builtin)?; let end_idx = *self.index(); - self.with_ctx(book.add_adt(nam, adt), ini_idx, end_idx)?; + self.add_fun_type(&mut book, nam, adt, ini_idx..end_idx)?; indent = self.advance_newlines()?; last_rule = None; continue; } } + // HVM native function definition + if self.try_parse_keyword("hvm") { + let def = self.parse_hvm(builtin)?; + let end_idx = *self.index(); + self.add_hvm(def, &mut book, ini_idx..end_idx)?; + indent = self.advance_newlines()?; + last_rule = None; + continue; + } + // Fun function definition let ini_idx = *self.index(); let (name, rule) = self.parse_rule()?; let end_idx = *self.index(); - // Add to book - if let Some(def) = book.defs.get_mut(&name) { - if let Some(last_rule) = last_rule { - if last_rule == name { - // Continuing with a new rule to the current definition - def.rules.push(rule); - } else { - // Trying to add a new rule to a previous definition, coming from a different rule. - let msg = Self::redefinition_of_function_msg(builtin, &name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - } else { - // Trying to add a new rule to a previous definition, coming from another kind of top-level. - let msg = Self::redefinition_of_function_msg(builtin, &name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - } else { - // Adding the first rule of a new definition - book.defs.insert(name.clone(), Definition { name: name.clone(), rules: vec![rule], builtin }); - } + self.add_fun_def(&name, rule, builtin, &last_rule, &mut book, ini_idx..end_idx)?; indent = self.advance_newlines()?; last_rule = Some(name); } @@ -191,6 +186,21 @@ impl<'a> TermParser<'a> { } } + fn parse_hvm(&mut self, builtin: bool) -> ParseResult { + self.skip_trivia_inline()?; + let name = self.parse_bend_name()?; + self.skip_trivia_inline()?; + self.consume_exactly(":")?; + self.consume_new_line()?; + // TODO: This will have the wrong index + let ini_idx = *self.index(); + let mut p = hvm::ast::CoreParser::new(&self.input[*self.index()..]); + let body = p.parse_net()?; + *self.index() = ini_idx + *p.index(); + let def = HvmDefinition { name: name.clone(), body, builtin }; + Ok(def) + } + fn parse_rule(&mut self) -> ParseResult<(Name, Rule)> { // (name pat*) = term // name pat* = term @@ -240,7 +250,7 @@ impl<'a> TermParser<'a> { // Ctr unexpected_tag(self)?; let Pattern::Var(Some(name)) = head else { - return self.expected_spanned("constructor name", head_ini_idx, head_end_idx); + return self.expected_spanned("constructor name", head_ini_idx..head_end_idx); }; let els = self.list_like(|p| p.parse_pattern(simple), "", ")", "", false, 0)?; return Ok(Pattern::Ctr(name, els)); @@ -645,7 +655,7 @@ impl<'a> TermParser<'a> { && !self.peek_many(2).is_some_and(|x| x.chars().nth(1).unwrap().is_ascii_digit()) { let msg = "Tagged terms not supported for hvm32.".to_string(); - return self.with_ctx(Err(msg), index, index + 1); + return self.with_ctx(Err(msg), index..index + 1); } else { None }; @@ -654,7 +664,7 @@ impl<'a> TermParser<'a> { Ok((tag, move |slf: &mut Self| { if has_tag { let msg = "\x1b[1m- unexpected tag:\x1b[0m".to_string(); - slf.with_ctx(Err(msg), index, end_index) + slf.with_ctx(Err(msg), index..end_index) } else { Ok(()) } @@ -674,7 +684,7 @@ impl<'a> TermParser<'a> { Ok((Some(std::mem::take(nam)), self.parse_term()?)) } (Term::Var { nam }, false) => Ok((Some(nam.clone()), Term::Var { nam: std::mem::take(nam) })), - (_, true) => self.expected_spanned("argument name", ini_idx, end_idx), + (_, true) => self.expected_spanned("argument name", ini_idx..end_idx), (arg, false) => Ok((Some(Name::new("%arg")), std::mem::take(arg))), } } @@ -712,6 +722,142 @@ impl<'a> TermParser<'a> { let bod = self.parse_term()?; Ok((nam, vec![], bod)) } + + fn add_fun_def( + &mut self, + name: &Name, + rule: Rule, + builtin: bool, + last_rule: &Option, + book: &mut Book, + span: Range, + ) -> ParseResult<()> { + match (book.defs.get_mut(name), last_rule) { + // Continuing with a new rule to the current definition + (Some(def), Some(last_rule)) if last_rule == name => { + def.rules.push(rule); + } + // Trying to add a new rule to a previous definition, coming from a different rule. + (Some(def), Some(_)) => { + let msg = Self::redefinition_of_function_msg(def.builtin, name); + return self.with_ctx(Err(msg), span); + } + // Trying to add a new rule to a previous definition, coming from another kind of top-level. + (Some(def), None) => { + let msg = Self::redefinition_of_function_msg(def.builtin, name); + return self.with_ctx(Err(msg), span); + } + // Adding the first rule of a new definition + (None, _) => { + self.check_top_level_redefinition(name, book, span)?; + book.defs.insert(name.clone(), Definition { name: name.clone(), rules: vec![rule], builtin }); + } + } + Ok(()) + } + + fn add_imp_def( + &mut self, + mut def: crate::imp::Definition, + book: &mut Book, + span: Range, + builtin: bool, + ) -> ParseResult<()> { + self.check_top_level_redefinition(&def.name, book, span)?; + def.order_kwargs(book)?; + def.gen_map_get(); + let def = def.to_fun(builtin)?; + book.defs.insert(def.name.clone(), def); + Ok(()) + } + + fn add_hvm(&mut self, def: HvmDefinition, book: &mut Book, span: Range) -> ParseResult<()> { + self.check_top_level_redefinition(&def.name, book, span)?; + book.hvm_defs.insert(def.name.clone(), def); + Ok(()) + } + + fn add_imp_type( + &mut self, + enum_: Enum, + book: &mut Book, + span: Range, + builtin: bool, + ) -> ParseResult<()> { + self.check_type_redefinition(&enum_.name, book, span.clone())?; + let mut adt = Adt { ctrs: Default::default(), builtin }; + for variant in enum_.variants { + self.check_top_level_redefinition(&enum_.name, book, span.clone())?; + book.ctrs.insert(variant.name.clone(), enum_.name.clone()); + adt.ctrs.insert(variant.name, variant.fields); + } + book.adts.insert(enum_.name.clone(), adt); + Ok(()) + } + + fn add_fun_type(&mut self, book: &mut Book, nam: Name, adt: Adt, span: Range) -> ParseResult<()> { + if book.adts.contains_key(&nam) { + let msg = TermParser::redefinition_of_type_msg(&nam); + return self.with_ctx(Err(msg), span); + } else { + for ctr in adt.ctrs.keys() { + match book.ctrs.entry(ctr.clone()) { + indexmap::map::Entry::Vacant(e) => _ = e.insert(nam.clone()), + indexmap::map::Entry::Occupied(e) => { + let msg = TermParser::redefinition_of_constructor_msg(e.key()); + return self.with_ctx(Err(msg), span); + } + } + } + book.adts.insert(nam.clone(), adt); + } + Ok(()) + } + + fn add_object( + &mut self, + obj: Variant, + book: &mut Book, + span: Range, + builtin: bool, + ) -> ParseResult<()> { + self.check_type_redefinition(&obj.name, book, span.clone())?; + self.check_top_level_redefinition(&obj.name, book, span)?; + let mut adt = Adt { ctrs: Default::default(), builtin }; + book.ctrs.insert(obj.name.clone(), obj.name.clone()); + adt.ctrs.insert(obj.name.clone(), obj.fields); + book.adts.insert(obj.name, adt); + Ok(()) + } + + fn check_top_level_redefinition( + &mut self, + name: &Name, + book: &mut Book, + span: Range, + ) -> ParseResult<()> { + if let Some(def) = book.defs.get(name) { + let msg = Self::redefinition_of_function_msg(def.builtin, name); + return self.with_ctx(Err(msg), span); + } + if book.ctrs.contains_key(name) { + let msg = Self::redefinition_of_constructor_msg(name); + return self.with_ctx(Err(msg), span); + } + if book.hvm_defs.contains_key(name) { + let msg = Self::redefinition_of_hvm_msg(false, name); + return self.with_ctx(Err(msg), span); + } + Ok(()) + } + + fn check_type_redefinition(&mut self, name: &Name, book: &mut Book, span: Range) -> ParseResult<()> { + if book.adts.contains_key(name) { + let msg = Self::redefinition_of_type_msg(name); + return self.with_ctx(Err(msg), span); + } + Ok(()) + } } impl<'a> Parser<'a> for TermParser<'a> { @@ -729,7 +875,7 @@ impl<'a> Parser<'a> for TermParser<'a> { fn expected(&mut self, exp: &str) -> ParseResult { let ini_idx = *self.index(); let end_idx = *self.index() + 1; - self.expected_spanned(exp, ini_idx, end_idx) + self.expected_spanned(exp, ini_idx..end_idx) } /// Consumes an instance of the given string, erroring if it is not found. @@ -799,22 +945,7 @@ impl Indent { } } -impl Book { - fn add_adt(&mut self, nam: Name, adt: Adt) -> ParseResult<()> { - if self.adts.contains_key(&nam) { - Err(TermParser::redefinition_of_type_msg(&nam))? - } else { - for ctr in adt.ctrs.keys() { - match self.ctrs.entry(ctr.clone()) { - indexmap::map::Entry::Vacant(e) => _ = e.insert(nam.clone()), - indexmap::map::Entry::Occupied(e) => Err(TermParser::redefinition_of_constructor_msg(e.key()))?, - } - } - self.adts.insert(nam.clone(), adt); - } - Ok(()) - } -} +impl Book {} impl<'a> ParserCommons<'a> for TermParser<'a> {} @@ -836,10 +967,10 @@ pub trait ParserCommons<'a>: Parser<'a> { let end_idx = *self.index(); if name.contains("__") { let msg = format!("{kind} names are not allowed to contain \"__\"."); - self.with_ctx(Err(msg), ini_idx, end_idx) + self.with_ctx(Err(msg), ini_idx..end_idx) } else if name.starts_with("//") { let msg = format!("{kind} names are not allowed to start with \"//\"."); - self.with_ctx(Err(msg), ini_idx, end_idx) + self.with_ctx(Err(msg), ini_idx..end_idx) } else { Ok(name) } @@ -893,7 +1024,7 @@ pub trait ParserCommons<'a>: Parser<'a> { while let Some(c) = self.peek_one() { if c == '\t' { let idx = *self.index(); - return self.with_ctx(Err("Tabs are not accepted for indentation.".to_string()), idx, idx); + return self.with_ctx(Err("Tabs are not accepted for indentation.".to_string()), idx..idx); } if " ".contains(c) { self.advance_one(); @@ -922,21 +1053,16 @@ pub trait ParserCommons<'a>: Parser<'a> { Ok(()) } - fn expected_spanned(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> ParseResult { + fn expected_spanned(&mut self, exp: &str, span: Range) -> ParseResult { let is_eof = self.is_eof(); let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); let msg = format!("\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", exp, detected); - self.with_ctx(Err(msg), ini_idx, end_idx) + self.with_ctx(Err(msg), span) } - fn with_ctx( - &mut self, - res: Result, - ini_idx: usize, - end_idx: usize, - ) -> ParseResult { + fn with_ctx(&mut self, res: Result, span: Range) -> ParseResult { res.map_err(|msg| { - let ctx = highlight_error(ini_idx, end_idx, self.input()); + let ctx = highlight_error(span.start, span.end, self.input()); format!("{msg}\n{ctx}") }) } @@ -985,7 +1111,7 @@ pub trait ParserCommons<'a>: Parser<'a> { if !next_is_name { Ok(()) } else { - self.expected_spanned(&format!("keyword '{keyword}'"), ini_idx, end_idx + 1) + self.expected_spanned(&format!("keyword '{keyword}'"), ini_idx..end_idx + 1) } } @@ -1200,7 +1326,7 @@ pub trait ParserCommons<'a>: Parser<'a> { fn num_range_err(&mut self, ini_idx: usize, typ: &str) -> ParseResult { let msg = format!("\x1b[1mNumber literal outside of range for {}.\x1b[0m", typ); let end_idx = *self.index(); - self.with_ctx(Err(msg), ini_idx, end_idx) + self.with_ctx(Err(msg), ini_idx..end_idx) } /// Parses up to 4 base64 characters surrounded by "`". @@ -1238,6 +1364,14 @@ pub trait ParserCommons<'a>: Parser<'a> { } } + fn redefinition_of_hvm_msg(builtin: bool, function_name: &str) -> String { + if builtin { + format!("Redefinition of builtin (native HVM function) '{function_name}'.") + } else { + format!("Redefinition of native HVM function '{function_name}'.") + } + } + fn redefinition_of_constructor_msg(constructor_name: &str) -> String { if crate::fun::builtins::BUILTIN_CTRS.contains(&constructor_name) { format!("Redefinition of builtin (constructor) '{constructor_name}'.") diff --git a/src/fun/term_to_net.rs b/src/fun/term_to_net.rs index d48eb9a7..49fa8b5c 100644 --- a/src/fun/term_to_net.rs +++ b/src/fun/term_to_net.rs @@ -38,6 +38,11 @@ pub fn book_to_hvm(book: &Book, diags: &mut Diagnostics) -> Result<(hvm::ast::Bo } } + // TODO: native hvm nets ignore labels + for def in book.hvm_defs.values() { + hvm_book.defs.insert(def.name.to_string(), def.body.clone()); + } + labels.con.finish(); labels.dup.finish(); diff --git a/src/fun/transform/definition_pruning.rs b/src/fun/transform/definition_pruning.rs index 6fbe9e8a..2270909d 100644 --- a/src/fun/transform/definition_pruning.rs +++ b/src/fun/transform/definition_pruning.rs @@ -3,6 +3,7 @@ use crate::{ fun::{Book, Ctx, Name, Term}, maybe_grow, }; +use hvm::ast::{Net, Tree}; use std::collections::{hash_map::Entry, HashMap}; #[derive(Clone, Copy, Debug, PartialEq)] @@ -27,7 +28,7 @@ impl Ctx<'_> { if let Some(main) = &self.book.entrypoint { let def = self.book.defs.get(main).unwrap(); used.insert(main.clone(), Used::Main); - self.book.find_used_definitions(&def.rule().body, Used::Main, &mut used); + self.book.find_used_definitions_from_term(&def.rule().body, Used::Main, &mut used); } // Get the functions that are accessible from non-builtins. @@ -38,7 +39,13 @@ impl Ctx<'_> { } else { used.insert(def.name.clone(), Used::NonBuiltin); } - self.book.find_used_definitions(&def.rule().body, Used::NonBuiltin, &mut used); + self.book.find_used_definitions_from_term(&def.rule().body, Used::NonBuiltin, &mut used); + } + } + for def in self.book.hvm_defs.values() { + if !def.builtin && !(used.get(&def.name) == Some(&Used::Main)) { + used.insert(def.name.clone(), Used::NonBuiltin); + self.book.find_used_definitions_from_hvm_net(&def.body, Used::NonBuiltin, &mut used); } } @@ -77,8 +84,8 @@ impl Ctx<'_> { } 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) { + /// Finds all used definitions on the book, starting from the given term. + fn find_used_definitions_from_term(&self, term: &Term, used: Used, uses: &mut Definitions) { maybe_grow(|| { let mut to_find = vec![term]; @@ -103,14 +110,44 @@ impl Book { }) } + fn find_used_definitions_from_hvm_net(&self, net: &Net, used: Used, uses: &mut Definitions) { + maybe_grow(|| { + let mut to_find = [&net.root] + .into_iter() + .chain(net.rbag.iter().flat_map(|(_, lft, rgt)| [lft, rgt])) + .collect::>(); + + while let Some(term) = to_find.pop() { + match term { + Tree::Ref { nam } => self.insert_used(&Name::new(nam), used, uses), + Tree::Con { fst, snd } + | Tree::Dup { fst, snd } + | Tree::Opr { fst, snd } + | Tree::Swi { fst, snd } => { + to_find.push(fst); + to_find.push(snd); + } + Tree::Era | Tree::Var { .. } | Tree::Num { .. } => {} + } + } + }) + } + fn insert_used(&self, def_name: &Name, used: Used, uses: &mut Definitions) { 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 + // 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 let Some(def) = self.defs.get(def_name) { + for rule in &def.rules { + self.find_used_definitions_from_term(&rule.body, used, uses); + } + } else if let Some(def) = self.hvm_defs.get(def_name) { + self.find_used_definitions_from_hvm_net(&def.body, used, uses); + } else { + unreachable!() } } } diff --git a/src/fun/transform/resolve_refs.rs b/src/fun/transform/resolve_refs.rs index bf7aaff7..11f2ae7c 100644 --- a/src/fun/transform/resolve_refs.rs +++ b/src/fun/transform/resolve_refs.rs @@ -20,7 +20,8 @@ impl Ctx<'_> { pub fn resolve_refs(&mut self) -> Result<(), Diagnostics> { self.info.start_pass(); - let def_names = self.book.defs.keys().cloned().collect::>(); + let def_names = + self.book.defs.keys().cloned().chain(self.book.hvm_defs.keys().cloned()).collect::>(); for (def_name, def) in &mut self.book.defs { for rule in def.rules.iter_mut() { let mut scope = HashMap::new(); diff --git a/src/hvm/mod.rs b/src/hvm/mod.rs index f14a183e..56ccfa83 100644 --- a/src/hvm/mod.rs +++ b/src/hvm/mod.rs @@ -1,4 +1,4 @@ -use crate::{fun::display::DisplayFn, multi_iterator}; +use crate::multi_iterator; use hvm::ast::{Net, Tree}; pub mod add_recursive_priority; @@ -36,122 +36,23 @@ pub fn net_trees_mut(net: &mut Net) -> impl DoubleEndedIterator impl std::fmt::Display + '_ { - DisplayFn(|f| { - for (nam, def) in book.defs.iter() { - writeln!(f, "@{} = {}", nam, display_hvm_tree(&def.root))?; - for (pri, a, b) in def.rbag.iter() { - writeln!(f, " &{}{} ~ {}", if *pri { "!" } else { " " }, display_hvm_tree(a), display_hvm_tree(b))?; - } - writeln!(f)?; - } - Ok(()) - }) -} - -// TODO: We have to reimplement these because hvm prints partially applied numbers incorrectly. -// https://github.com/HigherOrderCO/HVM/issues/350 -pub fn display_hvm_numb(numb: &hvm::ast::Numb) -> impl std::fmt::Display + '_ { - let numb = hvm::hvm::Numb(numb.0); - match numb.get_typ() { - hvm::hvm::TY_SYM => match numb.get_sym() as hvm::hvm::Tag { - hvm::hvm::OP_ADD => "[+]".to_string(), - hvm::hvm::OP_SUB => "[-]".to_string(), - hvm::hvm::FP_SUB => "[:-]".to_string(), - hvm::hvm::OP_MUL => "[*]".to_string(), - hvm::hvm::OP_DIV => "[/]".to_string(), - hvm::hvm::FP_DIV => "[:/]".to_string(), - hvm::hvm::OP_REM => "[%]".to_string(), - hvm::hvm::FP_REM => "[:%]".to_string(), - hvm::hvm::OP_EQ => "[=]".to_string(), - hvm::hvm::OP_NEQ => "[!]".to_string(), - hvm::hvm::OP_LT => "[<]".to_string(), - hvm::hvm::OP_GT => "[>]".to_string(), - hvm::hvm::OP_AND => "[&]".to_string(), - hvm::hvm::OP_OR => "[|]".to_string(), - hvm::hvm::OP_XOR => "[^]".to_string(), - hvm::hvm::OP_SHL => "[<<]".to_string(), - hvm::hvm::FP_SHL => "[:<<]".to_string(), - hvm::hvm::OP_SHR => "[>>]".to_string(), - hvm::hvm::FP_SHR => "[:>>]".to_string(), - _ => "[?]".to_string(), - }, - hvm::hvm::TY_U24 => { - let val = numb.get_u24(); - format!("{}", val) - } - hvm::hvm::TY_I24 => { - let val = numb.get_i24(); - format!("{:+}", val) - } - hvm::hvm::TY_F24 => { - let val = numb.get_f24(); - if val.is_infinite() { - if val.is_sign_positive() { - "+inf".to_string() - } else { - "-inf".to_string() - } - } else if val.is_nan() { - "+NaN".to_string() +pub fn hvm_book_show_pretty(book: &hvm::ast::Book) -> String { + let mut s = String::new(); + for (nam, def) in book.defs.iter() { + s.push_str(&format!("@{} = {}\n", nam, def.root.show())); + for (pri, a, b) in def.rbag.iter() { + s.push_str(" &"); + if *pri { + s.push('!'); } else { - format!("{:?}", val) + s.push(' '); } + s.push_str(&a.show()); + s.push_str(" ~ "); + s.push_str(&b.show()); + s.push('\n'); } - _ => { - let typ = numb.get_typ(); - let val = numb.get_u24(); - format!( - "[{}{}]", - match typ { - hvm::hvm::OP_ADD => "+", - hvm::hvm::OP_SUB => "-", - hvm::hvm::FP_SUB => ":-", - hvm::hvm::OP_MUL => "*", - hvm::hvm::OP_DIV => "/", - hvm::hvm::FP_DIV => ":/", - hvm::hvm::OP_REM => "%", - hvm::hvm::FP_REM => ":%", - hvm::hvm::OP_EQ => "=", - hvm::hvm::OP_NEQ => "!", - hvm::hvm::OP_LT => "<", - hvm::hvm::OP_GT => ">", - hvm::hvm::OP_AND => "&", - hvm::hvm::OP_OR => "|", - hvm::hvm::OP_XOR => "^", - hvm::hvm::OP_SHL => "<<", - hvm::hvm::FP_SHL => ":<<", - hvm::hvm::OP_SHR => ">>", - hvm::hvm::FP_SHR => ":>>", - _ => "?", - }, - val - ) - } - } -} - -pub fn display_hvm_tree(tree: &hvm::ast::Tree) -> impl std::fmt::Display + '_ { - match tree { - Tree::Var { nam } => nam.to_string(), - Tree::Ref { nam } => format!("@{}", nam), - Tree::Era => "*".to_string(), - Tree::Num { val } => format!("{}", display_hvm_numb(val)), - Tree::Con { fst, snd } => format!("({} {})", display_hvm_tree(fst), display_hvm_tree(snd)), - Tree::Dup { fst, snd } => format!("{{{} {}}}", display_hvm_tree(fst), display_hvm_tree(snd)), - Tree::Opr { fst, snd } => format!("$({} {})", display_hvm_tree(fst), display_hvm_tree(snd)), - Tree::Swi { fst, snd } => format!("?({} {})", display_hvm_tree(fst), display_hvm_tree(snd)), - } -} - -pub fn display_hvm_net(net: &hvm::ast::Net) -> impl std::fmt::Display + '_ { - let mut s = display_hvm_tree(&net.root).to_string(); - for (par, fst, snd) in &net.rbag { - s.push_str(" & "); - s.push_str(if *par { "!" } else { "" }); - s.push_str(&display_hvm_tree(fst).to_string()); - s.push_str(" ~ "); - s.push_str(&display_hvm_tree(snd).to_string()); + s.push('\n'); } s } diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 19991873..7b604ff1 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1,7 +1,7 @@ use crate::{ fun::{ parser::{is_num_char, Indent, ParseResult, ParserCommons}, - Adt, Book, CtrField, Name, Num, Op, STRINGS, + CtrField, Name, Num, Op, STRINGS, }, imp::{AssignPattern, Definition, Enum, Expr, InPlaceOp, MatchArm, Stmt, Variant}, maybe_grow, @@ -36,7 +36,7 @@ impl<'a> Parser<'a> for PyParser<'a> { fn expected(&mut self, exp: &str) -> ParseResult { let ini_idx = *self.index(); let end_idx = *self.index() + 1; - self.expected_spanned(exp, ini_idx, end_idx) + self.expected_spanned(exp, ini_idx..end_idx) } /// Consumes an instance of the given string, erroring if it is not found. @@ -152,7 +152,7 @@ impl<'a> PyParser<'a> { kwargs.push((bnd, arg)); } else if must_be_named { let msg = "Positional arguments are not allowed to go after named arguments.".to_string(); - return self.with_ctx(Err(msg), ini_idx, end_idx); + return self.with_ctx(Err(msg), ini_idx..end_idx); } else { args.push(arg); } @@ -176,7 +176,7 @@ impl<'a> PyParser<'a> { return Ok(Expr::MapGet { nam, key: Box::new(key) }); } else { let end_idx = *self.index(); - return self.expected_spanned("Map variable name", ini_idx, end_idx); + return self.expected_spanned("Map variable name", ini_idx..end_idx); } } @@ -187,7 +187,7 @@ impl<'a> PyParser<'a> { return Ok(Expr::Ctr { name: nam, args: Vec::new(), kwargs }); } else { let end_idx = *self.index(); - return self.expected_spanned("Constructor name", ini_idx, end_idx); + return self.expected_spanned("Constructor name", ini_idx..end_idx); } } @@ -361,7 +361,7 @@ impl<'a> PyParser<'a> { } else { let msg = "Unexpected '=' in unnamed argument.".to_string(); let idx = *self.index(); - self.with_ctx(Err(msg), idx, idx + 1) + self.with_ctx(Err(msg), idx..idx + 1) } } else { Ok((None, arg)) @@ -490,7 +490,7 @@ impl<'a> PyParser<'a> { match &pat { AssignPattern::Var(..) => {} AssignPattern::MapSet(..) => {} - _ => self.expected_spanned("Var or Map accessor", ini_idx, end_idx)?, + _ => self.expected_spanned("Var or Map accessor", ini_idx..end_idx)?, } if let Some(op) = self.parse_in_place_op()? { let val = self.parse_expr(true)?; @@ -502,7 +502,7 @@ impl<'a> PyParser<'a> { return Ok((stmt, nxt_indent)); } - self.expected_spanned("statement", ini_idx, end_idx) + self.expected_spanned("statement", ini_idx..end_idx) } fn parse_in_place_op(&mut self) -> ParseResult> { @@ -644,7 +644,7 @@ impl<'a> PyParser<'a> { self.advance_one(); Ok((Some(nam), self.parse_expr(true)?)) } - (_, true) => self.expected_spanned("argument name", ini_idx, end_idx), + (_, true) => self.expected_spanned("argument name", ini_idx..end_idx), (Expr::Var { nam }, false) => Ok((Some(nam.clone()), Expr::Var { nam })), (arg, false) => Ok((Some(Name::new("%arg")), arg)), } @@ -705,7 +705,7 @@ impl<'a> PyParser<'a> { let (fst_case, fst_stmt, mut nxt_indent) = self.parse_switch_case(indent)?; let end_idx = *self.index(); if fst_case != Some(0) { - return self.expected_spanned("case 0", ini_idx, end_idx); + return self.expected_spanned("case 0", ini_idx..end_idx); } let mut arms = vec![fst_stmt]; let mut should_continue = fst_case == Some(0); @@ -967,7 +967,7 @@ impl<'a> PyParser<'a> { if indent != Indent::Val(0) { let msg = "Indentation error. Functions defined with 'def' must be at the start of the line."; let idx = *self.index(); - return self.with_ctx(Err(msg), idx, idx + 1); + return self.with_ctx(Err(msg), idx..idx + 1); } self.skip_trivia_inline()?; @@ -995,7 +995,7 @@ impl<'a> PyParser<'a> { if indent != Indent::Val(0) { let msg = "Indentation error. Types defined with 'type' must be at the start of the line."; let idx = *self.index(); - return self.with_ctx(Err(msg), idx, idx + 1); + return self.with_ctx(Err(msg), idx..idx + 1); } self.skip_trivia_inline()?; @@ -1036,7 +1036,7 @@ impl<'a> PyParser<'a> { if indent != Indent::Val(0) { let msg = "Indentation error. Types defined with 'object' must be at the start of the line."; let idx = *self.index(); - return self.with_ctx(Err(msg), idx, idx + 1); + return self.with_ctx(Err(msg), idx..idx + 1); } self.skip_trivia_inline()?; @@ -1061,103 +1061,24 @@ impl<'a> PyParser<'a> { Ok(CtrField { nam, rec }) } - pub fn add_def( - &mut self, - mut def: Definition, - book: &mut Book, - ini_idx: usize, - end_idx: usize, - builtin: bool, - ) -> ParseResult<()> { - if let Some(def) = book.defs.get(&def.name) { - let msg = Self::redefinition_of_function_msg(def.builtin, &def.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - if book.ctrs.contains_key(&def.name) { - let msg = Self::redefinition_of_constructor_msg(&def.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - def.order_kwargs(book)?; - def.gen_map_get(); - let def = def.to_fun(builtin)?; - book.defs.insert(def.name.clone(), def); - Ok(()) - } - - pub fn add_type( - &mut self, - r#enum: Enum, - book: &mut Book, - ini_idx: usize, - end_idx: usize, - builtin: bool, - ) -> ParseResult<()> { - if book.adts.contains_key(&r#enum.name) { - let msg = PyParser::redefinition_of_type_msg(&r#enum.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - let mut adt = Adt { ctrs: Default::default(), builtin }; - for variant in r#enum.variants { - if let Some(def) = book.defs.get(&variant.name) { - let msg = PyParser::redefinition_of_function_msg(def.builtin, &variant.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - if book.ctrs.contains_key(&variant.name) { - let msg = PyParser::redefinition_of_constructor_msg(&variant.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - book.ctrs.insert(variant.name.clone(), r#enum.name.clone()); - adt.ctrs.insert(variant.name, variant.fields); - } - book.adts.insert(r#enum.name.clone(), adt); - Ok(()) - } - - pub fn add_object( - &mut self, - obj: Variant, - book: &mut Book, - ini_idx: usize, - end_idx: usize, - builtin: bool, - ) -> ParseResult<()> { - if book.adts.contains_key(&obj.name) { - let msg = PyParser::redefinition_of_type_msg(&obj.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - let mut adt = Adt { ctrs: Default::default(), builtin }; - if let Some(def) = book.defs.get(&obj.name) { - let msg = PyParser::redefinition_of_function_msg(def.builtin, &obj.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - if book.ctrs.contains_key(&obj.name) { - let msg = PyParser::redefinition_of_constructor_msg(&obj.name); - return self.with_ctx(Err(msg), ini_idx, end_idx); - } - book.ctrs.insert(obj.name.clone(), obj.name.clone()); - adt.ctrs.insert(obj.name.clone(), obj.fields); - book.adts.insert(obj.name, adt); - Ok(()) - } - fn expected_indent(&mut self, expected: Indent, got: Indent) -> ParseResult { match (expected, got) { (Indent::Eof, Indent::Eof) => unreachable!(), (Indent::Eof, Indent::Val(got)) => { let msg = format!("Indentation error. Expected end-of-input, got {} spaces.", got); let idx = *self.index(); - self.with_ctx(Err(msg), idx, idx + 1) + self.with_ctx(Err(msg), idx..idx + 1) } (Indent::Val(expected), Indent::Eof) => { let msg = format!("Indentation error. Expected {} spaces, got end-of-input.", expected); let idx = *self.index(); - self.with_ctx(Err(msg), idx, idx + 1) + self.with_ctx(Err(msg), idx..idx + 1) } (Indent::Val(expected), Indent::Val(got)) => { if got != expected { let msg = format!("Indentation error. Expected {} spaces, got {}.", expected, got); let idx = *self.index(); - self.with_ctx(Err(msg), idx, idx + 1) + self.with_ctx(Err(msg), idx..idx + 1) } else { unreachable!() } diff --git a/src/lib.rs b/src/lib.rs index 4e985d6b..aedaab42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,8 @@ use crate::{ hvm::{ add_recursive_priority::add_recursive_priority, check_net_size::{check_net_sizes, MAX_NET_SIZE}, - display_hvm_book, eta_reduce::eta_reduce_hvm_net, + hvm_book_show_pretty, inline::inline_hvm_book, mutual_recursion, prune::prune_hvm_book, @@ -249,7 +249,7 @@ fn run_hvm(book: &::hvm::ast::Book, cmd: &str, run_opts: &RunOpts) -> Result Result<(), Diagnostics> { let compile_res = compile_book(&mut book, opts, diagnostics_cfg, None)?; eprint!("{}", compile_res.diagnostics); - println!("{}", display_hvm_book(&compile_res.hvm_book)); + println!("{}", hvm_book_show_pretty(&compile_res.hvm_book)); } Mode::Run(RunArgs { pretty, run_opts, comp_opts, warn_opts, path, arguments }) @@ -342,8 +342,7 @@ fn execute_cli_mode(mut cli: Cli) -> Result<(), Diagnostics> { let compile_res = compile_book(&mut book, opts, diagnostics_cfg, None)?; let out_path = ".out.hvm"; - std::fs::write(out_path, display_hvm_book(&compile_res.hvm_book).to_string()) - .map_err(|x| x.to_string())?; + std::fs::write(out_path, hvm_book_show_pretty(&compile_res.hvm_book)).map_err(|x| x.to_string())?; let gen_fn = |out_path: &str| { let mut process = std::process::Command::new(hvm_bin); diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index 4ef815c9..8204b5f8 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -2,7 +2,7 @@ use bend::{ compile_book, desugar_book, diagnostics::{Diagnostics, DiagnosticsConfig, Severity}, fun::{load_book::do_parse_book, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Name, Term}, - hvm::display_hvm_book, + hvm::hvm_book_show_pretty, net::hvm_to_net::hvm_to_net, run_book, AdtEncoding, CompileOpts, RunOpts, }; @@ -108,7 +108,7 @@ fn compile_file() { let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Allow, ..Default::default() }; let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?; - Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book))) + Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } @@ -124,7 +124,7 @@ fn compile_file_o_all() { }; let res = compile_book(&mut book, opts, diagnostics_cfg, None)?; - Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book))) + Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } @@ -135,7 +135,7 @@ fn compile_file_o_no_all() { let compile_opts = CompileOpts::default().set_no_all(); let diagnostics_cfg = DiagnosticsConfig::default(); let res = compile_book(&mut book, compile_opts, diagnostics_cfg, None)?; - Ok(format!("{}", display_hvm_book(&res.hvm_book))) + Ok(hvm_book_show_pretty(&res.hvm_book).to_string()) }) } @@ -351,7 +351,7 @@ fn compile_entrypoint() { book.entrypoint = Some(Name::new("foo")); let diagnostics_cfg = DiagnosticsConfig { ..DiagnosticsConfig::new(Severity::Error, true) }; let res = compile_book(&mut book, CompileOpts::default(), diagnostics_cfg, None)?; - Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book))) + Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } @@ -398,7 +398,7 @@ fn mutual_recursion() { let mut book = do_parse_book(code, path, Book::builtins())?; let opts = CompileOpts { merge: true, ..CompileOpts::default() }; let res = compile_book(&mut book, opts, diagnostics_cfg, None)?; - Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book))) + Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } @@ -476,7 +476,7 @@ fn scott_triggers_unused() { let diagnostics_cfg = DiagnosticsConfig { unused_definition: Severity::Error, ..DiagnosticsConfig::default() }; let res = compile_book(&mut book, opts, diagnostics_cfg, None)?; - Ok(format!("{}{}", res.diagnostics, display_hvm_book(&res.hvm_book))) + Ok(format!("{}{}", res.diagnostics, hvm_book_show_pretty(&res.hvm_book))) }) } diff --git a/tests/golden_tests/run_file/hvm_def_cast.bend b/tests/golden_tests/run_file/hvm_def_cast.bend new file mode 100644 index 00000000..0fb66229 --- /dev/null +++ b/tests/golden_tests/run_file/hvm_def_cast.bend @@ -0,0 +1,5 @@ +# Expected +42.0 +hvm to_f24: + ($([f24] a) a) + +main = (to_f24 42) \ No newline at end of file diff --git a/tests/golden_tests/run_file/hvm_def_two_defs.bend b/tests/golden_tests/run_file/hvm_def_two_defs.bend new file mode 100644 index 00000000..3c8e7133 --- /dev/null +++ b/tests/golden_tests/run_file/hvm_def_two_defs.bend @@ -0,0 +1,8 @@ +# Expected result: -0.349 +hvm log_: + (x ($([|] $(x ret)) ret)) + +hvm atan2_: + ($([&] $(y ret)) (y ret)) + +main = (log_ (atan2_ 1.0 1.0) 2.0) \ No newline at end of file diff --git a/tests/snapshots/cli__no_check_net_size.bend.snap b/tests/snapshots/cli__no_check_net_size.bend.snap index 2e3952c4..9ded86db 100644 --- a/tests/snapshots/cli__no_check_net_size.bend.snap +++ b/tests/snapshots/cli__no_check_net_size.bend.snap @@ -22,7 +22,7 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend @Gen.go__C0 = a & @Arr/Leaf ~ a -@Gen.go__C1 = ({a d} ({$([*2] $([|1] e)) $([*2] b)} g)) +@Gen.go__C1 = ({a d} ({$([*0x0000002] $([|0x0000001] e)) $([*0x0000002] b)} g)) & @Arr/Node ~ (c (f g)) &!@Gen.go ~ (a (b c)) &!@Gen.go ~ (d (e f)) @@ -74,7 +74,7 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend @Merge__C9 = ((@Merge__C4 a) a) -@Radix = ({$([&8388608] a) {$([&4194304] b) {$([&2097152] c) {$([&1048576] d) {$([&524288] e) {$([&262144] f) {$([&131072] g) {$([&65536] h) {$([&32768] i) {$([&16384] j) {$([&8192] k) {$([&4096] l) {$([&2048] m) {$([&1024] n) {$([&512] o) {$([&256] p) {$([&128] q) {$([&64] r) {$([&32] s) {$([&16] t) {$([&8] u) {$([&4] v) {$([&2] w) $([&1] x)}}}}}}}}}}}}}}}}}}}}}}} vb) +@Radix = ({$([&0x0800000] a) {$([&0x0400000] b) {$([&0x0200000] c) {$([&0x0100000] d) {$([&0x0080000] e) {$([&0x0040000] f) {$([&0x0020000] g) {$([&0x0010000] h) {$([&0x0008000] i) {$([&0x0004000] j) {$([&0x0002000] k) {$([&0x0001000] l) {$([&0x0000800] m) {$([&0x0000400] n) {$([&0x0000200] o) {$([&0x0000100] p) {$([&0x0000080] q) {$([&0x0000040] r) {$([&0x0000020] s) {$([&0x0000010] t) {$([&0x0000008] u) {$([&0x0000004] v) {$([&0x0000002] w) $([&0x0000001] x)}}}}}}}}}}}}}}}}}}}}}}} vb) & @Swap ~ (a (ub (@Map_/Free vb))) & @Swap ~ (b (tb (@Map_/Free ub))) & @Swap ~ (c (sb (@Map_/Free tb))) @@ -141,7 +141,7 @@ input_file: tests/golden_tests/cli/no_check_net_size.bend @ToArr__C0 = a & @Arr/Leaf ~ a -@ToArr__C1 = (* (b (e ({$([*2] $([+1] d)) $([*2] $([+0] a))} g)))) +@ToArr__C1 = (* (b (e ({$([*0x0000002] $([+0x0000001] d)) $([*0x0000002] $([+0x0000000] a))} g)))) & @Arr/Node ~ (c (f g)) &!@ToArr ~ (a (b c)) &!@ToArr ~ (d (e f)) diff --git a/tests/snapshots/compile_file__addition.bend.snap b/tests/snapshots/compile_file__addition.bend.snap index 33590b7f..5b081f50 100644 --- a/tests/snapshots/compile_file__addition.bend.snap +++ b/tests/snapshots/compile_file__addition.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file/addition.bend --- @main = c & (a b) ~ (8 c) - & $(1 $([+] $(a b))) ~ [+1] + & $(1 $([+] $(a b))) ~ [+0x0000001] diff --git a/tests/snapshots/compile_file__addition_const.bend.snap b/tests/snapshots/compile_file__addition_const.bend.snap index 3869551f..3bad45fd 100644 --- a/tests/snapshots/compile_file__addition_const.bend.snap +++ b/tests/snapshots/compile_file__addition_const.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/addition_const.bend --- @main = a - & $(2 a) ~ [+1] + & $(2 a) ~ [+0x0000001] diff --git a/tests/snapshots/compile_file__elif.bend.snap b/tests/snapshots/compile_file__elif.bend.snap index a714134e..2aff836c 100644 --- a/tests/snapshots/compile_file__elif.bend.snap +++ b/tests/snapshots/compile_file__elif.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/elif.bend --- @main = j - & $(2 ?(((d (e (?(((b (?(((?((0 (* 2)) a) a) (* (* 3))) (b c)) c)) (* (* (* 4)))) (d (e f))) f))) (* (* (* (* 1))))) (g (h (i j))))) ~ [=1] - & $(1 g) ~ [<2] - & $(2 h) ~ [>3] - & $(2 i) ~ [=2] + & $(2 ?(((d (e (?(((b (?(((?((0 (* 2)) a) a) (* (* 3))) (b c)) c)) (* (* (* 4)))) (d (e f))) f))) (* (* (* (* 1))))) (g (h (i j))))) ~ [=0x0000001] + & $(1 g) ~ [<0x0000002] + & $(2 h) ~ [>0x0000003] + & $(2 i) ~ [=0x0000002] diff --git a/tests/snapshots/compile_file__error_data_def_name.bend.snap b/tests/snapshots/compile_file__error_data_def_name.bend.snap index dcc275ec..c9baf72d 100644 --- a/tests/snapshots/compile_file__error_data_def_name.bend.snap +++ b/tests/snapshots/compile_file__error_data_def_name.bend.snap @@ -3,4 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/error_data_def_name.bend --- Errors: -Function 'A/A' has the same name as a previously defined constructor +In tests/golden_tests/compile_file/error_data_def_name.bend : +Redefinition of constructor 'A/A'. + 2 | A/A = 0 diff --git a/tests/snapshots/compile_file__f24_oper.bend.snap b/tests/snapshots/compile_file__f24_oper.bend.snap index d47a9d57..ed998f9d 100644 --- a/tests/snapshots/compile_file__f24_oper.bend.snap +++ b/tests/snapshots/compile_file__f24_oper.bend.snap @@ -3,5 +3,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/f24_oper.bend --- @main = b - & $(1.2399902 $([/] $(a b))) ~ [*4388912] - & $(-235.1211 a) ~ [+0] + & $(1.2399902 $([/] $(a b))) ~ [*0x042F830] + & $(-235.1211 a) ~ [+0x0000000] diff --git a/tests/snapshots/compile_file__i24_oper.bend.snap b/tests/snapshots/compile_file__i24_oper.bend.snap index 9b185222..6fdc13e1 100644 --- a/tests/snapshots/compile_file__i24_oper.bend.snap +++ b/tests/snapshots/compile_file__i24_oper.bend.snap @@ -3,5 +3,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/i24_oper.bend --- @main = b - & $(-1 $([*] $(a b))) ~ [+1] - & $(+14 a) ~ [-134217716] + & $(-1 $([*] $(a b))) ~ [+0x0000001] + & $(+14 a) ~ [-0x7FFFFF4] diff --git a/tests/snapshots/compile_file__match.bend.snap b/tests/snapshots/compile_file__match.bend.snap index 62a75b0b..02c55008 100644 --- a/tests/snapshots/compile_file__match.bend.snap +++ b/tests/snapshots/compile_file__match.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/match.bend --- @main = c - & $(1 ?(((a (* a)) (* (* (b b)))) c)) ~ [+0] + & $(1 ?(((a (* a)) (* (* (b b)))) c)) ~ [+0x0000000] diff --git a/tests/snapshots/compile_file__nums.bend.snap b/tests/snapshots/compile_file__nums.bend.snap index 2ced2822..1226a45e 100644 --- a/tests/snapshots/compile_file__nums.bend.snap +++ b/tests/snapshots/compile_file__nums.bend.snap @@ -3,5 +3,5 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/nums.bend --- @main = b - & $(a b) ~ [+16777215] - & $(1000 a) ~ [+5] + & $(a b) ~ [+0x0FFFFFF] + & $(1000 a) ~ [+0x0000005] diff --git a/tests/snapshots/compile_file__op2.bend.snap b/tests/snapshots/compile_file__op2.bend.snap index 2a02eefc..31d7c015 100644 --- a/tests/snapshots/compile_file__op2.bend.snap +++ b/tests/snapshots/compile_file__op2.bend.snap @@ -3,4 +3,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/op2.bend --- @main = a - & $(1 $([=2] $([&3] $([|4] $([<5] $([>6] $([:/7] $([*8] $([:-9] $([+10] $([:%11] a))))))))))) ~ [!0] + & $(1 $([=0x0000002] $([&0x0000003] $([|0x0000004] $([<0x0000005] $([>0x0000006] $([:/0x0000007] $([*0x0000008] $([:-0x0000009] $([+0x000000A] $([:%0x000000B] a))))))))))) ~ [!0x0000000] diff --git a/tests/snapshots/compile_file__redex_order_recursive.bend.snap b/tests/snapshots/compile_file__redex_order_recursive.bend.snap index 112e68d0..ad6e513e 100644 --- a/tests/snapshots/compile_file__redex_order_recursive.bend.snap +++ b/tests/snapshots/compile_file__redex_order_recursive.bend.snap @@ -22,7 +22,7 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend @List.len = ((@List.len__C1 a) a) @List.len__C0 = (* (* (a c))) - & $(b c) ~ [+1] + & $(b c) ~ [+0x0000001] & @List.len ~ (a b) @List.len__C1 = (?((0 @List.len__C0) a) a) @@ -31,7 +31,7 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend @List.len_tr__C0 = (* (* (a (b d)))) & @List.len_tr ~ (a (c d)) - & $(b c) ~ [+1] + & $(b c) ~ [+0x0000001] @List.len_tr__C1 = (?(((a a) @List.len_tr__C0) b) b) @@ -100,7 +100,7 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend @Tree.height = ((@Tree.height__C1 a) a) @Tree.height__C0 = (a (c f)) - & $(e f) ~ [+1] + & $(e f) ~ [+0x0000001] & @max ~ (b (d e)) &!@Tree.height ~ (a b) &!@Tree.height ~ (c d) @@ -130,7 +130,7 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend @Tree.nodes = ((@Tree.nodes__C1 a) a) @Tree.nodes__C0 = (a (b e)) - & $(d e) ~ [+1] + & $(d e) ~ [+0x0000001] &!@Tree.nodes ~ (a $([+] $(c d))) &!@Tree.nodes ~ (b c) @@ -170,4 +170,4 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend @tail_recursive__C0 = (a (b d)) & @tail_recursive ~ (a (c d)) - & $(b c) ~ [+1] + & $(b c) ~ [+0x0000001] diff --git a/tests/snapshots/compile_file__switch_in_switch_arg.bend.snap b/tests/snapshots/compile_file__switch_in_switch_arg.bend.snap index dc5e8947..96639f9c 100644 --- a/tests/snapshots/compile_file__switch_in_switch_arg.bend.snap +++ b/tests/snapshots/compile_file__switch_in_switch_arg.bend.snap @@ -2,4 +2,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/switch_in_switch_arg.bend --- -@main = (?((0 (a a)) ?((0 ($([+1] b) b)) c)) c) +@main = (?((0 (a a)) ?((0 ($([+0x0000001] b) b)) c)) c) diff --git a/tests/snapshots/compile_file_o_all__addition.bend.snap b/tests/snapshots/compile_file_o_all__addition.bend.snap index a16b23d8..b72b50a8 100644 --- a/tests/snapshots/compile_file_o_all__addition.bend.snap +++ b/tests/snapshots/compile_file_o_all__addition.bend.snap @@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file_o_all/addition.bend --- @main = c & (a b) ~ (8 c) - & $(1 $([+] $(a b))) ~ [+1] + & $(1 $([+] $(a b))) ~ [+0x0000001] diff --git a/tests/snapshots/compile_file_o_all__addition_var_fst.bend.snap b/tests/snapshots/compile_file_o_all__addition_var_fst.bend.snap index a0b68605..fa1770aa 100644 --- a/tests/snapshots/compile_file_o_all__addition_var_fst.bend.snap +++ b/tests/snapshots/compile_file_o_all__addition_var_fst.bend.snap @@ -2,4 +2,4 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/addition_var_fst.bend --- -@main = ($([+1] a) a) +@main = ($([+0x0000001] a) a) diff --git a/tests/snapshots/compile_file_o_all__num_pattern_with_var.bend.snap b/tests/snapshots/compile_file_o_all__num_pattern_with_var.bend.snap index d7e1b252..7db1b2cf 100644 --- a/tests/snapshots/compile_file_o_all__num_pattern_with_var.bend.snap +++ b/tests/snapshots/compile_file_o_all__num_pattern_with_var.bend.snap @@ -4,7 +4,7 @@ input_file: tests/golden_tests/compile_file_o_all/num_pattern_with_var.bend --- @Foo = ((@Foo__C2 a) a) -@Foo__C0 = ($([+1] a) a) +@Foo__C0 = ($([+0x0000001] a) a) @Foo__C1 = (* (?((0 @Foo__C0) a) a)) diff --git a/tests/snapshots/mutual_recursion__len.bend.snap b/tests/snapshots/mutual_recursion__len.bend.snap index 0af2bb0e..34126165 100644 --- a/tests/snapshots/mutual_recursion__len.bend.snap +++ b/tests/snapshots/mutual_recursion__len.bend.snap @@ -5,7 +5,7 @@ input_file: tests/golden_tests/mutual_recursion/len.bend @Len = ((@Len__C1 a) a) @Len__C0 = (* (* (a c))) - & $(b c) ~ [+1] + & $(b c) ~ [+0x0000001] & @Len ~ (a b) @Len__C1 = (?((0 @Len__C0) a) a) diff --git a/tests/snapshots/run_file__hvm_def_cast.bend.snap b/tests/snapshots/run_file__hvm_def_cast.bend.snap new file mode 100644 index 00000000..6887f1d3 --- /dev/null +++ b/tests/snapshots/run_file__hvm_def_cast.bend.snap @@ -0,0 +1,9 @@ +--- +source: tests/golden_tests.rs +input_file: tests/golden_tests/run_file/hvm_def_cast.bend +--- +NumScott: +42.000 + +Scott: +42.000 diff --git a/tests/snapshots/run_file__hvm_def_two_defs.bend.snap b/tests/snapshots/run_file__hvm_def_two_defs.bend.snap new file mode 100644 index 00000000..2ade08f4 --- /dev/null +++ b/tests/snapshots/run_file__hvm_def_two_defs.bend.snap @@ -0,0 +1,9 @@ +--- +source: tests/golden_tests.rs +input_file: tests/golden_tests/run_file/hvm_def_two_defs.bend +--- +NumScott: +-0.349 + +Scott: +-0.349 diff --git a/tests/snapshots/run_file__override_list_ctr.bend.snap b/tests/snapshots/run_file__override_list_ctr.bend.snap index dec088c9..a0b5243a 100644 --- a/tests/snapshots/run_file__override_list_ctr.bend.snap +++ b/tests/snapshots/run_file__override_list_ctr.bend.snap @@ -3,4 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/override_list_ctr.bend --- Errors: -Function 'List/Nil' has the same name as a previously defined constructor +In tests/golden_tests/run_file/override_list_ctr.bend : +Redefinition of builtin (constructor) 'List/Nil'. + 1 | List/Nil = * diff --git a/tests/snapshots/run_file__override_str_ctr.bend.snap b/tests/snapshots/run_file__override_str_ctr.bend.snap index ba76039a..995b1c5d 100644 --- a/tests/snapshots/run_file__override_str_ctr.bend.snap +++ b/tests/snapshots/run_file__override_str_ctr.bend.snap @@ -3,4 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/override_str_ctr.bend --- Errors: -Function 'String/Cons' has the same name as a previously defined constructor +In tests/golden_tests/run_file/override_str_ctr.bend : +Redefinition of builtin (constructor) 'String/Cons'. + 1 | String/Cons = *