mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 03:45:29 +03:00
Merge branch 'main' of github.com:HigherOrderCo/bend
This commit is contained in:
commit
3e9785b707
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -82,7 +82,7 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bend"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"TSPL 0.0.9 (git+https://github.com/developedby/TSPL.git?branch=fix-hvml-bugs)",
|
||||
"clap",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bend"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
6
GUIDE.md
6
GUIDE.md
@ -336,7 +336,7 @@ be turned into a tree!
|
||||
|
||||
The `bend` is the opposite of the `fold`: instead of consuming something, it
|
||||
creates something. The way it works is, in some ways, similar to a while loop,
|
||||
but, admitedly, a little bit more bureaucratic, in 3 ways:
|
||||
but, admittedly, a little bit more bureaucratic, in 3 ways:
|
||||
|
||||
1. You must explicit an initial state.
|
||||
|
||||
@ -387,7 +387,7 @@ itself, forming a tree of descendants. In the end, you have a complete tree!
|
||||
|
||||
The second way to explain is for the devs offended by this ultra simplified
|
||||
example: yes, `bend` is just a syntax-sugar for creating a recursive function,
|
||||
immediatelly calling it, and assigning the result to a variable. In other words,
|
||||
immediately calling it, and assigning the result to a variable. In other words,
|
||||
the program above is equivalent to:
|
||||
|
||||
```python
|
||||
@ -515,4 +515,4 @@ Benchmarks:
|
||||
|
||||
## ...
|
||||
|
||||
TODO: conver IO and so many other aspects :')
|
||||
TODO: cover IO and so many other aspects :')
|
||||
|
@ -9,6 +9,7 @@
|
||||
"arity",
|
||||
"behaviour",
|
||||
"bitand",
|
||||
"Bitonic",
|
||||
"builtins",
|
||||
"callcc",
|
||||
"chumsky",
|
||||
@ -24,6 +25,7 @@
|
||||
"destructures",
|
||||
"desugared",
|
||||
"desugars",
|
||||
"devs",
|
||||
"dref",
|
||||
"dups",
|
||||
"effectful",
|
||||
@ -58,6 +60,7 @@
|
||||
"nullary",
|
||||
"numop",
|
||||
"nums",
|
||||
"OOM's",
|
||||
"oper",
|
||||
"opre",
|
||||
"oprune",
|
||||
@ -67,6 +70,7 @@
|
||||
"postcondition",
|
||||
"powi",
|
||||
"prec",
|
||||
"quadtree",
|
||||
"readback",
|
||||
"recursively",
|
||||
"redex",
|
||||
@ -111,6 +115,7 @@
|
||||
"/`.`/g",
|
||||
"/`..`/g",
|
||||
"/`...`/g",
|
||||
"/`....`/g"
|
||||
"/`....`/g",
|
||||
"/```.*\n.*\n```/g"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ def max(a, b):
|
||||
else:
|
||||
return b
|
||||
```
|
||||
|
||||
```py
|
||||
// Not allowed, early return
|
||||
def Foo(x):
|
||||
@ -120,6 +121,7 @@ def Foo(x):
|
||||
|
||||
return y
|
||||
```
|
||||
|
||||
```py
|
||||
// Not allowed, one of the branches doesn't return
|
||||
def Foo(a, b):
|
||||
@ -226,7 +228,7 @@ bend x = 0:
|
||||
```
|
||||
|
||||
Which binds a variable to the return of an inline recursive function.
|
||||
The function `go` is available inside the `when` arm of the `bend` and calls it recursively.
|
||||
The function `go` is available inside the `when` arm of the `bend` and calls it recursively.
|
||||
|
||||
It is possible to pass multiple state variables, which can be initialized:
|
||||
|
||||
@ -263,6 +265,7 @@ Where `x <- ...` performs a monadic operation.
|
||||
|
||||
Expects `Result` to be a type defined with `type` and a function `Result/bind` to be defined.
|
||||
The monadic bind function should be of type `(Result a) -> (a -> Result b) -> Result b`, like this:
|
||||
|
||||
```
|
||||
def Result/bind(res, nxt):
|
||||
match res:
|
||||
@ -273,6 +276,7 @@ def Result/bind(res, nxt):
|
||||
```
|
||||
|
||||
Other statements are allowed inside the `do` block and it can both return a value at the end and bind a variable, like branching statements do.
|
||||
|
||||
```
|
||||
// Also ok:
|
||||
do Result:
|
||||
@ -350,6 +354,29 @@ callee(expr1, expr2, arg4 = expr3, arg3 = expr4)
|
||||
|
||||
In case named arguments are used, they must come after the positional arguments and the function must be called with exactly the number of arguments of its definition.
|
||||
|
||||
### Eraser
|
||||
|
||||
```python
|
||||
*
|
||||
|
||||
eraser = *
|
||||
|
||||
*(41 + 1) // applies 41 + 1 to `*` erasing the number and returns `*`
|
||||
|
||||
* = 41 + 1 // erases 41 + 1
|
||||
```
|
||||
|
||||
The effect of an eraser is to free memory. Erasers behave like a `null`.
|
||||
|
||||
It's impossible to compare or match eraser values.
|
||||
|
||||
It is implicitly inserted for variables that have not been used:
|
||||
|
||||
```python
|
||||
def constant(x):
|
||||
return 8345
|
||||
```
|
||||
|
||||
### Tuple
|
||||
|
||||
```python
|
||||
@ -476,7 +503,7 @@ fold list:
|
||||
List/nil
|
||||
```
|
||||
|
||||
<div id="core-syntax"></div>
|
||||
<div id="fun-syntax"></div>
|
||||
|
||||
# Fun Syntax
|
||||
|
||||
|
@ -6,4 +6,4 @@ Tree.gen n x = switch n {
|
||||
_: (Tree.node x (Tree.gen n-1 (+ (* x 2) 1)) (Tree.gen n-1 (+ (* x 2) 2)))
|
||||
}
|
||||
|
||||
main = (Tree.gen 8 2)
|
||||
main = (Tree.gen 4 1)
|
||||
|
@ -22,4 +22,4 @@ def gen(depth):
|
||||
|
||||
// returns the sum of 0..2^16
|
||||
def main:
|
||||
return sum(gen(24))
|
||||
return sum(gen(16))
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::{parser::TermParser, Book, Name, Num, Pattern, Term};
|
||||
use crate::maybe_grow;
|
||||
|
||||
const BUILTINS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/fun/builtins.hvm"));
|
||||
const BUILTINS: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/fun/builtins.bend"));
|
||||
|
||||
pub const LIST: &str = "List";
|
||||
pub const LCONS: &str = "List/cons";
|
||||
|
@ -367,7 +367,7 @@ impl<'a> TermParser<'a> {
|
||||
}
|
||||
|
||||
// Native Number
|
||||
if self.peek_one().map_or(false, |c| "0123456789+-".contains(c)) {
|
||||
if self.peek_one().map_or(false, is_num_char) {
|
||||
unexpected_tag(self)?;
|
||||
let num = self.parse_number()?;
|
||||
return Ok(Term::Num { val: num });
|
||||
@ -658,10 +658,14 @@ impl<'a> Parser<'a> for TermParser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_name_char(c: char) -> bool {
|
||||
pub fn is_name_char(c: char) -> bool {
|
||||
c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/'
|
||||
}
|
||||
|
||||
pub fn is_num_char(c: char) -> bool {
|
||||
"0123456789+-".contains(c)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Indent {
|
||||
Val(isize),
|
||||
@ -766,7 +770,7 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
fn consume_new_line(&mut self) -> ParseResult<()> {
|
||||
self.skip_trivia_inline();
|
||||
self.try_consume_exactly("\r");
|
||||
self.consume_exactly("\n")
|
||||
self.labelled(|p| p.consume_exactly("\n"), "newline")
|
||||
}
|
||||
|
||||
/// Skips trivia, returns the number of trivia characters skipped in the last line.
|
||||
@ -969,6 +973,37 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
Ok(opr)
|
||||
}
|
||||
|
||||
fn peek_oper(&mut self) -> Option<Op> {
|
||||
let opr = if self.starts_with("+") {
|
||||
Op::ADD
|
||||
} else if self.starts_with("-") {
|
||||
Op::SUB
|
||||
} else if self.starts_with("*") {
|
||||
Op::MUL
|
||||
} else if self.starts_with("/") {
|
||||
Op::DIV
|
||||
} else if self.starts_with("%") {
|
||||
Op::REM
|
||||
} else if self.starts_with("<") {
|
||||
Op::LTN
|
||||
} else if self.starts_with(">") {
|
||||
Op::GTN
|
||||
} else if self.starts_with("==") {
|
||||
Op::EQL
|
||||
} else if self.starts_with("!=") {
|
||||
Op::NEQ
|
||||
} else if self.starts_with("&") {
|
||||
Op::AND
|
||||
} else if self.starts_with("|") {
|
||||
Op::OR
|
||||
} else if self.starts_with("^") {
|
||||
Op::XOR
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(opr)
|
||||
}
|
||||
|
||||
fn parse_u32(&mut self) -> ParseResult<u32> {
|
||||
let radix = match self.peek_many(2) {
|
||||
Some("0x") => {
|
||||
|
@ -20,6 +20,9 @@ enum Used {
|
||||
/// Should be pruned if `prune_all` or if it's a built-in rule
|
||||
/// Otherwise, if the rule has a non-generated name, a warning about the unused def is returned
|
||||
Unused,
|
||||
|
||||
/// An unused definition but needed for compilation when `-Oprune` is not active.
|
||||
Needed,
|
||||
}
|
||||
|
||||
type Definitions = HashMap<Name, Used>;
|
||||
@ -36,6 +39,14 @@ impl Ctx<'_> {
|
||||
self.book.find_used_definitions(&def.rule().body, Used::Main, &mut used);
|
||||
}
|
||||
|
||||
if !prune_all {
|
||||
for def in self.book.defs.values() {
|
||||
if !def.builtin && !used.contains_key(&def.name) {
|
||||
self.book.find_used_definitions(&def.rule().body, Used::Needed, &mut used);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (def_name, def) in &self.book.defs {
|
||||
if !def.builtin && self.book.ctrs.get(def_name).is_some() {
|
||||
used.insert(def_name.clone(), Used::Adt);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
diagnostics::Diagnostics,
|
||||
fun::{Ctx, Name, Pattern, Term},
|
||||
fun::{Ctx, Name, Term},
|
||||
maybe_grow,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
@ -41,15 +41,9 @@ impl Term {
|
||||
let fun = make_fun_name(typ);
|
||||
|
||||
if def_names.contains(&fun) {
|
||||
let mut fvs = nxt.free_vars();
|
||||
pat.binds().flatten().for_each(|bind| _ = fvs.remove(bind));
|
||||
let fvs = fvs.into_keys().collect::<Vec<_>>();
|
||||
let nxt = fvs
|
||||
.iter()
|
||||
.fold(*nxt.clone(), |nxt, nam| Term::lam(Pattern::Var(Some(nam.clone())), nxt.clone()));
|
||||
let nxt = Term::lam(*pat.clone(), nxt);
|
||||
let term = Term::call(Term::Ref { nam: fun.clone() }, [*val.clone(), nxt]);
|
||||
*self = Term::call(term, fvs.into_iter().map(|nam| Term::Var { nam }));
|
||||
// TODO: come up with a strategy for forwarding free vars to prevent infinite recursion.
|
||||
let nxt = Term::lam(*pat.clone(), std::mem::take(nxt));
|
||||
*self = Term::call(Term::Ref { nam: fun.clone() }, [*val.clone(), nxt]);
|
||||
} else {
|
||||
return Err(format!("Could not find definition {} for type {}.", fun, typ));
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ impl Expr {
|
||||
go(entry, substitutions, id);
|
||||
}
|
||||
}
|
||||
Expr::None | Expr::Str { .. } | Expr::Var { .. } | Expr::Chn { .. } | Expr::Num { .. } => {}
|
||||
Expr::Eraser | Expr::Str { .. } | Expr::Var { .. } | Expr::Chn { .. } | Expr::Num { .. } => {}
|
||||
}
|
||||
}
|
||||
let mut substitutions = Substitutions::new();
|
||||
|
@ -3,19 +3,19 @@ mod order_kwargs;
|
||||
pub mod parser;
|
||||
pub mod to_fun;
|
||||
|
||||
use crate::fun::{CtrField, Name, Op};
|
||||
use crate::fun::{CtrField, Name, Num, Op};
|
||||
use interner::global::GlobalString;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expr {
|
||||
// "None"
|
||||
None,
|
||||
// "*"
|
||||
Eraser,
|
||||
// [a-zA-Z_]+
|
||||
Var { nam: Name },
|
||||
// "$" [a-zA-Z_]+
|
||||
Chn { nam: Name },
|
||||
// [0-9_]+
|
||||
Num { val: u32 },
|
||||
Num { val: Num },
|
||||
// {fun}({args},{kwargs},)
|
||||
Call { fun: Box<Expr>, args: Vec<Expr>, kwargs: Vec<(Name, Expr)> },
|
||||
// "lambda" {names}* ":" {bod}
|
||||
@ -48,6 +48,8 @@ pub struct MatchArm {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AssignPattern {
|
||||
// "*"
|
||||
Eraser,
|
||||
// [a-zA-Z_]+
|
||||
Var(Name),
|
||||
// "$" [a-zA-Z_]+
|
||||
|
@ -150,7 +150,7 @@ impl Expr {
|
||||
}
|
||||
}
|
||||
Expr::MapGet { .. }
|
||||
| Expr::None
|
||||
| Expr::Eraser
|
||||
| Expr::Var { .. }
|
||||
| Expr::Chn { .. }
|
||||
| Expr::Num { .. }
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
fun::{
|
||||
parser::{Indent, ParseResult, ParserCommons},
|
||||
Adt, Book, CtrField, Name, Op, STRINGS,
|
||||
parser::{is_num_char, Indent, ParseResult, ParserCommons},
|
||||
Adt, Book, CtrField, Name, Num, Op, STRINGS,
|
||||
},
|
||||
imp::{AssignPattern, Definition, Enum, Expr, InPlaceOp, MatchArm, Stmt, Variant},
|
||||
maybe_grow,
|
||||
@ -9,7 +9,11 @@ use crate::{
|
||||
use TSPL::Parser;
|
||||
|
||||
const PREC: &[&[Op]] =
|
||||
&[&[Op::EQL, Op::NEQ], &[Op::LTN], &[Op::GTN], &[Op::ADD, Op::SUB], &[Op::MUL, Op::DIV]];
|
||||
&[&[Op::OR], &[Op::XOR], &[Op::AND], &[Op::EQL, Op::NEQ], &[Op::LTN], &[Op::GTN], &[Op::ADD, Op::SUB], &[
|
||||
Op::MUL,
|
||||
Op::DIV,
|
||||
Op::REM,
|
||||
]];
|
||||
|
||||
pub struct PyParser<'i> {
|
||||
pub input: &'i str,
|
||||
@ -71,6 +75,7 @@ impl<'a> PyParser<'a> {
|
||||
let head = self.parse_expr(false)?;
|
||||
self.skip_trivia();
|
||||
if self.starts_with(",") {
|
||||
// A Tuple
|
||||
let mut els = vec![head];
|
||||
while self.try_consume(",") {
|
||||
els.push(self.parse_expr(false)?);
|
||||
@ -79,6 +84,7 @@ impl<'a> PyParser<'a> {
|
||||
Expr::Tup { els }
|
||||
} else {
|
||||
self.consume(")")?;
|
||||
// A parenthesized expression
|
||||
head
|
||||
}
|
||||
}
|
||||
@ -89,7 +95,7 @@ impl<'a> PyParser<'a> {
|
||||
if self.starts_with(":") { self.parse_map_init(head)? } else { self.parse_sup(head)? }
|
||||
}
|
||||
'[' => self.list_or_comprehension()?,
|
||||
'`' => Expr::Num { val: self.parse_quoted_symbol()? },
|
||||
'`' => Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) },
|
||||
'\"' => {
|
||||
let str = self.parse_quoted_string()?;
|
||||
let val = STRINGS.get(str);
|
||||
@ -100,8 +106,12 @@ impl<'a> PyParser<'a> {
|
||||
let nam = self.parse_bend_name()?;
|
||||
Expr::Chn { nam }
|
||||
}
|
||||
c if c.is_ascii_digit() => {
|
||||
let val = self.parse_u32()?;
|
||||
'*' => {
|
||||
self.advance_one();
|
||||
Expr::Eraser
|
||||
}
|
||||
c if is_num_char(c) => {
|
||||
let val = self.parse_number()?;
|
||||
Expr::Num { val }
|
||||
}
|
||||
// var or postfix
|
||||
@ -284,9 +294,9 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
let mut lhs = self.parse_infix_expr(prec + 1, inline)?;
|
||||
self.skip_trivia_maybe_inline(inline);
|
||||
while let Some(op) = self.get_op() {
|
||||
while let Some(op) = self.peek_oper() {
|
||||
if PREC[prec].iter().any(|r| *r == op) {
|
||||
let op = self.parse_oper()?;
|
||||
self.parse_oper()?;
|
||||
let rhs = self.parse_infix_expr(prec + 1, inline)?;
|
||||
lhs = Expr::Bin { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
|
||||
self.advance_trivia_inline();
|
||||
@ -298,31 +308,6 @@ impl<'a> PyParser<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_op(&mut self) -> Option<Op> {
|
||||
let ret = if self.starts_with("+") {
|
||||
Op::ADD
|
||||
} else if self.starts_with("-") {
|
||||
Op::SUB
|
||||
} else if self.starts_with("*") {
|
||||
Op::MUL
|
||||
} else if self.starts_with("/") {
|
||||
Op::DIV
|
||||
} else if self.starts_with("%") {
|
||||
Op::REM
|
||||
} else if self.starts_with("<") {
|
||||
Op::LTN
|
||||
} else if self.starts_with(">") {
|
||||
Op::GTN
|
||||
} else if self.starts_with("==") {
|
||||
Op::EQL
|
||||
} else if self.starts_with("!=") {
|
||||
Op::NEQ
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn consume_indent_at_most(&mut self, expected: Indent) -> ParseResult<Indent> {
|
||||
let got = self.advance_newlines();
|
||||
match (expected, got) {
|
||||
@ -390,6 +375,9 @@ impl<'a> PyParser<'a> {
|
||||
let val = self.parse_expr(true)?;
|
||||
self.skip_trivia_inline();
|
||||
self.try_consume_exactly(";");
|
||||
if !self.is_eof() {
|
||||
self.consume_new_line()?;
|
||||
}
|
||||
let nxt_indent = self.advance_newlines();
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
@ -460,6 +448,9 @@ impl<'a> PyParser<'a> {
|
||||
let term = self.parse_expr(true)?;
|
||||
self.skip_trivia_inline();
|
||||
self.try_consume_exactly(";");
|
||||
if !self.is_eof() {
|
||||
self.consume_new_line()?;
|
||||
}
|
||||
let indent = self.advance_newlines();
|
||||
Ok((Stmt::Return { term: Box::new(term) }, indent))
|
||||
}
|
||||
@ -766,6 +757,11 @@ impl<'a> PyParser<'a> {
|
||||
/// | <nam> "[" <expr> "]"
|
||||
/// | <nam>
|
||||
fn parse_assign_pattern(&mut self) -> ParseResult<AssignPattern> {
|
||||
// Eraser pattern
|
||||
if self.starts_with("*") {
|
||||
self.advance_one();
|
||||
return Ok(AssignPattern::Eraser);
|
||||
}
|
||||
// Tup pattern
|
||||
if self.starts_with("(") {
|
||||
let binds = self.list_like(|p| p.parse_assign_pattern(), "(", ")", ",", true, 1)?;
|
||||
|
@ -22,6 +22,7 @@ impl Definition {
|
||||
impl AssignPattern {
|
||||
pub fn into_fun(self) -> fun::Pattern {
|
||||
match self {
|
||||
AssignPattern::Eraser => fun::Pattern::Var(None),
|
||||
AssignPattern::Var(name) => fun::Pattern::Var(Some(name)),
|
||||
AssignPattern::Chn(name) => fun::Pattern::Chn(name),
|
||||
AssignPattern::Tup(names) => fun::Pattern::Fan(
|
||||
@ -273,10 +274,10 @@ impl Stmt {
|
||||
impl Expr {
|
||||
pub fn to_fun(self) -> fun::Term {
|
||||
match self {
|
||||
Expr::None => fun::Term::Era,
|
||||
Expr::Eraser => fun::Term::Era,
|
||||
Expr::Var { nam } => fun::Term::Var { nam },
|
||||
Expr::Chn { nam } => fun::Term::Link { nam },
|
||||
Expr::Num { val } => fun::Term::Num { val: fun::Num::U24(val) },
|
||||
Expr::Num { val } => fun::Term::Num { val },
|
||||
Expr::Call { fun, args, kwargs } => {
|
||||
assert!(kwargs.is_empty());
|
||||
let args = args.into_iter().map(Self::to_fun);
|
||||
|
@ -237,6 +237,7 @@ fn parse_file() {
|
||||
ctx.book.encode_adts();
|
||||
ctx.book.encode_builtins();
|
||||
ctx.resolve_refs().expect("Resolve refs");
|
||||
ctx.desugar_match_defs().expect("Desugar match defs");
|
||||
ctx.prune(false);
|
||||
Ok(book.to_string())
|
||||
})
|
||||
|
@ -65,7 +65,12 @@ def bnd():
|
||||
else:
|
||||
return List/nil();
|
||||
|
||||
def era():
|
||||
* = (2 + 3)
|
||||
the_expr_killer = *
|
||||
return the_expr_killer(9)
|
||||
|
||||
def main():
|
||||
do IO:
|
||||
x <- IO.read();
|
||||
return x;
|
||||
return x;
|
||||
|
@ -2,4 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: examples/bitonic_sort.bend
|
||||
---
|
||||
523776
|
||||
16515072
|
||||
|
File diff suppressed because one or more lines are too long
@ -4,9 +4,9 @@ input_file: tests/golden_tests/parse_file/imp_map.bend
|
||||
---
|
||||
(Map/empty) = Map/leaf
|
||||
|
||||
(Map/get map key) = match map = map { Map/leaf: (*, map); Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/node map.value map.left rest)); }; _ _-1: (map.value, map); }; }
|
||||
(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/leaf: (*, map); Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/node map.value map.left rest)); }; _ _-1: (map.value, map); }; }
|
||||
|
||||
(Map/set map key value) = match map = map { Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/node value map.left map.right); }; Map/leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node * (Map/set Map/leaf (/ key 2) value) Map/leaf); _ _-1: (Map/node * Map/leaf (Map/set Map/leaf (/ key 2) value)); }; _ _-1: (Map/node value Map/leaf Map/leaf); }; }
|
||||
(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/node value map.left map.right); }; Map/leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node * (Map/set Map/leaf (/ key 2) value) Map/leaf); _ _-1: (Map/node * Map/leaf (Map/set Map/leaf (/ key 2) value)); }; _ _-1: (Map/node value Map/leaf Map/leaf); }; }
|
||||
|
||||
(main) = let x = (Map/set (Map/set Map/empty 2 1) 3 2); let (map/get%1, x) = (Map/get x 2); let y = (id map/get%1); let z = 4; let x = (Map/set x z 4); let (map/get%0, x) = (Map/get x z); (+ y map/get%0)
|
||||
|
||||
|
@ -2,28 +2,36 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/parse_file/imp_program.bend
|
||||
---
|
||||
(Map/empty) = Map/leaf
|
||||
|
||||
(Map/get) = λ%arg0 λ%arg1 use key = %arg1; use map = %arg0; match map = map { Map/leaf: (*, map); Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: let (got, rest) = (Map/get map.left (/ key 2)); (got, (Map/node map.value rest map.right)); _ _-1: let (got, rest) = (Map/get map.right (/ key 2)); (got, (Map/node map.value map.left rest)); }; _ _-1: (map.value, map); }; }
|
||||
|
||||
(Map/set) = λ%arg0 λ%arg1 λ%arg2 use value = %arg2; use key = %arg1; use map = %arg0; match map = map { Map/node: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node map.value (Map/set map.left (/ key 2) value) map.right); _ _-1: (Map/node map.value map.left (Map/set map.right (/ key 2) value)); }; _ _-1: (Map/node value map.left map.right); }; Map/leaf: switch _ = (== 0 key) { 0: switch _ = (% key 2) { 0: (Map/node * (Map/set Map/leaf (/ key 2) value) Map/leaf); _ _-1: (Map/node * Map/leaf (Map/set Map/leaf (/ key 2) value)); }; _ _-1: (Map/node value Map/leaf Map/leaf); }; }
|
||||
|
||||
(symbols) = let x = (Map/set (Map/set Map/empty 49 5) 2 3); let x = (Map/set x 49 2); let x = (Map/set x 2 3); let (map/get%0, x) = (Map/get x 49); (+ map/get%0 8293490)
|
||||
|
||||
(mk_point) = (Point/Point 1 2)
|
||||
|
||||
(identity x) = x
|
||||
(identity) = λ%arg0 use x = %arg0; x
|
||||
|
||||
(inc n) = let n = (+ n 1); n
|
||||
(inc) = λ%arg0 use n = %arg0; let n = (+ n 1); n
|
||||
|
||||
(lam) = λx λy x
|
||||
|
||||
(do_match b) = match b = b { Bool/True: 1; Bool/False: 0; }
|
||||
(do_match) = λ%arg0 use b = %arg0; match b = b { Bool/True: 1; Bool/False: 0; }
|
||||
|
||||
(true) = Bool/True
|
||||
|
||||
(fib n) = switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; }
|
||||
(fib) = λ%arg0 use n = %arg0; switch %pred = (< n 2) { 0: (+ (fib (- n 1)) (fib (- n 2))); _ %pred-1: n; }
|
||||
|
||||
(swt n) = switch n = n { 0: 42; _ n-1: 1; }
|
||||
(swt) = λ%arg0 use n = %arg0; switch n = n { 0: 42; _ n-1: 1; }
|
||||
|
||||
(fld list) = fold list = list { List/cons: 1; List/nil: 2; }
|
||||
(fld) = λ%arg0 use list = %arg0; fold list = list { List/cons: 1; List/nil: 2; }
|
||||
|
||||
(bnd) = bend x = 0, while (< x 10) { (List/cons x (go (+ x 1))) } then { List/nil }
|
||||
|
||||
(era) = let * = (+ 2 3); let the_expr_killer = *; (the_expr_killer 9)
|
||||
|
||||
(main) = do IO { ask x = IO.read; x }
|
||||
|
||||
(List/cons) = λhead λtail λList/cons λList/nil (List/cons head tail)
|
||||
|
@ -2,4 +2,30 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/recursive_bind.bend
|
||||
---
|
||||
λa (a 0)
|
||||
[4m[1m[31mErrors:[0m
|
||||
[1mThe following functions contain recursive cycles incompatible with HVM's strict evaluation:[0m
|
||||
* Foo -> Foo
|
||||
|
||||
The greedy eager evaluation of HVM may cause infinite loops.
|
||||
[1mRefactor these functions to use lazy references instead of direct function calls.[0m
|
||||
A reference is strict when it's being called ('(Foo x)') or when it's used non-linearly ('let x = Foo; (x x)').
|
||||
It is lazy when it's an argument ('(x Foo)') or when it's used linearly ('let x = Foo; (x 0)').
|
||||
|
||||
[1mTry one of these strategies:[0m
|
||||
- Use pattern matching with 'match', 'fold', and 'bend' to automatically lift expressions to lazy references.
|
||||
- Replace direct calls with combinators. For example, change:
|
||||
'Foo = λa λb (b (λc (Foo a c)) a)'
|
||||
to:
|
||||
'Foo = λa λb (b (λc λa (Foo a c)) (λa a) a)'
|
||||
which is lifted to:
|
||||
'Foo = λa λb (b Foo__C1 Foo_C2 a)'
|
||||
- Replace non-linear 'let' expressions with 'use' expressions. For example, change:
|
||||
'Foo = λf let x = Foo; (f x x)'
|
||||
to:
|
||||
'Foo = λf use x = Foo; (f x x)'
|
||||
which inlines to:
|
||||
'Foo = λf (f Foo Foo)'
|
||||
- If disabled, re-enable the default 'float-combinators' and 'linearize-matches' compiler options.
|
||||
|
||||
For more information, visit: https://github.com/HigherOrderCO/hvm-lang/blob/main/docs/lazy-definitions.md.
|
||||
To disable this check, use the "-Arecursion-cycle" compiler option.
|
||||
|
Loading…
Reference in New Issue
Block a user