Merge pull request #589 from HigherOrderCO/586-add-option-to-write-raw-hvm-in-bend-programs

#586 Add raw hvm function syntax
This commit is contained in:
Nicolas Abril 2024-06-16 22:47:34 +00:00 committed by GitHub
commit 8b5555c490
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 439 additions and 356 deletions

View File

@ -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

40
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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.

View File

@ -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<Name>) {
pub fn check_unbound_refs(&self, book: &Book, unbounds: &mut HashSet<Name>) {
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);
}
})
}

View File

@ -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<Name>) -> &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"
)
)
}
}

View File

@ -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<Name, Definition>;
pub type HvmDefinitions = IndexMap<Name, HvmDefinition>;
pub type Adts = IndexMap<Name, Adt>;
pub type Constructors = IndexMap<Name, Name>;
@ -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 {

View File

@ -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<HvmDefinition> {
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<Name>,
book: &mut Book,
span: Range<usize>,
) -> 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<usize>,
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<usize>) -> 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<usize>,
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<usize>) -> 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<usize>,
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<usize>,
) -> 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<usize>) -> 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<T>(&mut self, exp: &str) -> ParseResult<T> {
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<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> ParseResult<T> {
fn expected_spanned<T>(&mut self, exp: &str, span: Range<usize>) -> ParseResult<T> {
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<T>(
&mut self,
res: Result<T, impl std::fmt::Display>,
ini_idx: usize,
end_idx: usize,
) -> ParseResult<T> {
fn with_ctx<T>(&mut self, res: Result<T, impl std::fmt::Display>, span: Range<usize>) -> ParseResult<T> {
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<T>(&mut self, ini_idx: usize, typ: &str) -> ParseResult<T> {
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}'.")

View File

@ -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();

View File

@ -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::<Vec<_>>();
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!()
}
}
}

View File

@ -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::<HashSet<_>>();
let def_names =
self.book.defs.keys().cloned().chain(self.book.hvm_defs.keys().cloned()).collect::<HashSet<_>>();
for (def_name, def) in &mut self.book.defs {
for rule in def.rules.iter_mut() {
let mut scope = HashMap::new();

View File

@ -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<Item = &mut Tree
[&mut net.root].into_iter().chain(net.rbag.iter_mut().flat_map(|(_, fst, snd)| [fst, snd]))
}
pub fn display_hvm_book(book: &hvm::ast::Book) -> 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
}

View File

@ -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<T>(&mut self, exp: &str) -> ParseResult<T> {
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<Option<InPlaceOp>> {
@ -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<T>(&mut self, expected: Indent, got: Indent) -> ParseResult<T> {
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!()
}

View File

@ -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<Str
}
let out_path = ".out.hvm";
std::fs::write(out_path, display_hvm_book(book).to_string()).map_err(|x| x.to_string())?;
std::fs::write(out_path, hvm_book_show_pretty(book)).map_err(|x| x.to_string())?;
let mut process = std::process::Command::new(run_opts.hvm_path.clone())
.arg(cmd)
.arg(out_path)

View File

@ -2,7 +2,7 @@ use bend::{
check_book, compile_book, desugar_book,
diagnostics::{Diagnostics, DiagnosticsConfig, Severity},
fun::{Book, Name},
hvm::display_hvm_book,
hvm::hvm_book_show_pretty,
load_file_to_book, run_book, AdtEncoding, CompileOpts, OptLevel, RunOpts,
};
use clap::{Args, CommandFactory, Parser, Subcommand};
@ -300,7 +300,7 @@ fn execute_cli_mode(mut cli: Cli) -> 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);

View File

@ -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)))
})
}

View File

@ -0,0 +1,5 @@
# Expected +42.0
hvm to_f24:
($([f24] a) a)
main = (to_f24 42)

View File

@ -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)

View File

@ -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))

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 = *

View File

@ -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 = *