Merge branch 'main' into netsize

This commit is contained in:
Kunal Singh 2024-08-07 22:11:44 +05:30
commit 8a394fc4e9
27 changed files with 514 additions and 200 deletions

View File

@ -14,6 +14,7 @@ and this project does not currently adhere to a particular versioning scheme.
- Fix local definitions not being desugared properly. ([#623][gh-623])
- Expand references to functions generated by the `float_combinators` pass inside the main function. ([#642][gh-642])
- Expand references inside constructors in the main function. ([#643][gh-643])
- Fix readback when hvm net has `a{n}` or `x{n}` vars. ([#659][gh-659])
### Added
@ -27,6 +28,8 @@ and this project does not currently adhere to a particular versioning scheme.
- Change tuple syntax to not require parentheses in some cases. ([#554][gh-554])
- Improve error messages in branching statements. ([#464][gh-464])
- Change branches to support ending with ask statements. ([#629][gh-629])
- Improve hexadecimal and binary floating numbers. ([#648][gh-648])
## [0.2.36] - 2024-07-04
@ -410,6 +413,9 @@ and this project does not currently adhere to a particular versioning scheme.
[gh-620]: https://github.com/HigherOrderCO/Bend/issues/620
[gh-621]: https://github.com/HigherOrderCO/Bend/issues/621
[gh-623]: https://github.com/HigherOrderCO/Bend/issues/623
[gh-629]: https://github.com/HigherOrderCO/Bend/issues/629
[gh-642]: https://github.com/HigherOrderCO/Bend/issues/642
[gh-643]: https://github.com/HigherOrderCO/Bend/issues/643
[gh-648]: https://github.com/HigherOrderCO/Bend/issues/648
[gh-659]: https://github.com/HigherOrderCO/Bend/pull/659
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD

View File

@ -580,6 +580,18 @@ Currently, the 3 number types cannot be mixed.
| Bitwise Or | x \| y | int, uint |
| Bitwise Xor | x ^ y | int, uint |
Hexadecimal and binary floating-point literals are also supported.
In these representations, each digit after the point is divided according to the bases power of the digit's position.
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the power of the digit's depth.
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the power of the digit's depth.
```python
0xA.A == 10.625
0b111.111 == 7.875
```
### Constructor Literals
Constructors are just functions.
@ -1093,7 +1105,7 @@ Result/wrap x = (Result/Ok x)
with Result {
ask x = (some_operation ...)
ask y = (some_operation ...)
wrap(x * y)
(wrap (* x y))
}
```
@ -1141,6 +1153,18 @@ Currently, the 3 number types cannot be mixed.
| Bitwise Or | (\| x y) | int, uint |
| Bitwise Xor | (^ x y) | int, uint |
Hexadecimal and binary floating-point literals are also supported.
In these representations, each digit after the point is divided according to the bases power of the digit's position.
Specifically, for hexadecimal floating-point numbers, each place after the dot represents a fraction of 16 to the negative power of the digit's depth.
Similarly, for binary floating-point numbers, each place after the dot represents a fraction of 2 to the negative power of the digit's depth.
```python
(== 0xA.A 10.625)
(== 0b111.111 7.875)
```
### Character Literal
```rust
@ -1240,14 +1264,17 @@ changed or optimized by the compiler.
# Import Syntax
### Import Relative to the File
Paths starting with `./` or `../` are imported relative to the file.
### Import Relative to the Main Folder
Paths that do not start with `./` or `../` are relative to the folder of the main file.
## Syntax
### Import Specific Names from a File, or Files from a Folder
```py
from path import name
from path import (name1, name2)
@ -1255,11 +1282,13 @@ import (path/name1, path/name2)
```
### Import All Names from a File, or All Files from a Folder
```py
from path import *
```
### Aliasing Imports
```py
from path import name as alias
from path import (name1 as Alias1, name2 as Alias2)

View File

@ -76,10 +76,20 @@ String/split s delim = (String/split.go s delim (List/Cons String/Nil List/Nil))
# Create a new difference list
DiffList/new = λx x
# DiffList/wrap(head: T) -> (List(T) -> List(T)
# Creates a new difference list with just the given value.
def DiffList/wrap(head):
return lambda tail: List/Cons(head, tail)
# DiffList/append(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T))
# Append a value to the end of the difference list
DiffList/append diff val = λx (diff (List/Cons val x))
# DiffList/append(left: List(T) -> List(T), right: List(T) -> List(T)) -> (List(T) -> List(T))
# Concatenates two difference lists.
def DiffList/concat(left, right):
return lambda x: left(right(x))
# DiffList/cons(diff: List(T) -> List(T), val: T) -> (List(T) -> List(T))
# Append a value to the beginning of the difference list
DiffList/cons diff val = λx (List/Cons val (diff x))
@ -96,6 +106,22 @@ type Tree:
Node { ~left, ~right }
Leaf { value }
# Returns a List converted from a Tree.
def Tree/to_list(tree):
fold tree:
case Tree/Leaf:
list = DiffList/wrap(tree.value)
case Tree/Node:
list = DiffList/concat(tree.left, tree.right)
return DiffList/to_list(list)
# Reverses a tree swapping right and left leaves.
def Tree/reverse(tree):
fold tree:
case Tree/Leaf:
return !tree.value
case Tree/Node:
return ![tree.right, tree.left]
# MAP Impl
@ -217,11 +243,11 @@ IO/FS/STDOUT = 1
IO/FS/STDERR = 2
### Seek modes
# Seek from start of file
# Seek from start of file.
IO/FS/SEEK_SET = +0
# Seek from current position
# Seek from current position.
IO/FS/SEEK_CUR = +1
# Seek from end of file
# Seek from end of file.
IO/FS/SEEK_END = +2
### File utilities

View File

@ -326,24 +326,28 @@ impl<'a> TermParser<'a> {
Ok(vec![import])
}
fn parse_rule(&mut self) -> ParseResult<(Name, Rule)> {
// (name pat*) = term
// name pat* = term
let (name, pats) = if self.try_consume_exactly("(") {
fn parse_rule_lhs(&mut self) -> ParseResult<(Name, Vec<Pattern>)> {
if self.try_consume_exactly("(") {
self.skip_trivia();
let name = self.labelled(|p| p.parse_top_level_name(), "function name")?;
let pats = self.list_like(|p| p.parse_pattern(false), "", ")", "", false, 0)?;
self.consume("=")?;
(name, pats)
Ok((name, pats))
} else {
let name = self.labelled(|p| p.parse_top_level_name(), "top-level definition")?;
let mut pats = vec![];
while !self.try_consume("=") {
self.skip_trivia();
while !self.starts_with("=") {
pats.push(self.parse_pattern(false)?);
self.skip_trivia();
}
(name, pats)
};
Ok((name, pats))
}
}
fn parse_rule(&mut self) -> ParseResult<(Name, Rule)> {
let (name, pats) = self.parse_rule_lhs()?;
self.consume("=")?;
let body = self.parse_term()?;
@ -425,9 +429,23 @@ impl<'a> TermParser<'a> {
}
// Var
unexpected_tag(self)?;
let nam = self.labelled(|p| p.parse_name_or_era(), "pattern-matching pattern")?;
Ok(Pattern::Var(nam))
if self.starts_with("*")
|| self
.peek_one()
.is_some_and(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/')
{
unexpected_tag(self)?;
let nam = self.parse_name_or_era()?;
return Ok(Pattern::Var(nam));
}
let ini_idx = *self.index();
while !(self.is_eof() || self.starts_with("=")) {
self.advance_one();
}
let cur_idx = *self.index();
self.expected_spanned("pattern or '='", ini_idx..cur_idx)
})
}
@ -1500,80 +1518,85 @@ pub trait ParserCommons<'a>: Parser<'a> {
let radix = match self.peek_many(2) {
Some("0x") => {
self.advance_many(2);
16
Radix::Hex
}
Some("0b") => {
self.advance_many(2);
2
Radix::Bin
}
_ => 10,
_ => Radix::Dec,
};
let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
let num_str = self.take_while(move |c| c.is_digit(radix as u32) || c == '_');
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
let next_is_hex = self.peek_one().map_or(false, |c| "0123456789abcdefABCDEF".contains(c));
if next_is_hex || num_str.is_empty() {
let base = match radix {
16 => "hexadecimal",
10 => "decimal",
2 => "binary",
_ => unreachable!(),
};
self.expected(format!("valid {base} digit").as_str())
self.expected(format!("valid {radix} digit").as_str())
} else {
u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string())
}
}
fn u32_with_radix(&mut self, radix: Radix) -> ParseResult<u32> {
let num_str = self.take_while(move |c| c.is_digit(radix as u32) || c == '_');
let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
let next_is_hex = self.peek_one().map_or(false, |c| "0123456789abcdefABCDEF".contains(c));
if next_is_hex || num_str.is_empty() {
self.expected(format!("valid {radix} digit").as_str())
} else {
u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string())
}
}
fn parse_number(&mut self) -> ParseResult<Num> {
let ini_idx = *self.index();
// Parses sign
let sgn = if self.try_consume_exactly("+") {
let sign = if self.try_consume_exactly("+") {
Some(1)
} else if self.try_consume_exactly("-") {
Some(-1)
} else {
None
};
// Parses main value
let num = self.parse_u32()?;
// Parses frac value (Float type)
// TODO: Will lead to some rounding errors
// TODO: Doesn't cover very large/small numbers
let fra = if let Some('.') = self.peek_one() {
let radix = match self.peek_many(2) {
Some("0x") => {
self.advance_many(2);
Radix::Hex
}
Some("0b") => {
self.advance_many(2);
Radix::Bin
}
_ => Radix::Dec,
};
let num = self.u32_with_radix(radix)?;
let frac = if let Some('.') = self.peek_one() {
self.advance_one();
let ini_idx = *self.index();
let fra = self.parse_u32()? as f32;
let end_idx = *self.index();
let fra = fra / 10f32.powi((end_idx - ini_idx) as i32);
let fra_str = self.take_while(|c| c.is_digit(radix as u32) || c == '_');
let fra_str = fra_str.chars().filter(|c| *c != '_').collect::<String>();
let fra = u32::from_str_radix(&fra_str, radix as u32).map_err(|e| e.to_string())?;
let fra = fra as f32 / (radix.to_f32()).powi(fra_str.len() as i32);
Some(fra)
} else {
None
};
// F24
if let Some(fra) = fra {
let sgn = sgn.unwrap_or(1);
return Ok(Num::F24(sgn as f32 * (num as f32 + fra)));
if let Some(frac) = frac {
let sign = sign.unwrap_or(1);
return Ok(Num::F24(sign as f32 * (num as f32 + frac)));
}
// I24
if let Some(sgn) = sgn {
let num = sgn * num as i32;
if let Some(sign) = sign {
let num = sign * num as i32;
if !(-0x00800000..=0x007fffff).contains(&num) {
return self.num_range_err(ini_idx, "I24");
}
return Ok(Num::I24(num));
Ok(Num::I24(num))
} else {
if num >= 1 << 24 {
return self.num_range_err(ini_idx, "U24");
}
Ok(Num::U24(num))
}
// U24
if num >= 1 << 24 {
return self.num_range_err(ini_idx, "U24");
}
Ok(Num::U24(num))
}
fn num_range_err<T>(&mut self, ini_idx: usize, typ: &str) -> ParseResult<T> {
@ -1641,3 +1664,30 @@ pub trait ParserCommons<'a>: Parser<'a> {
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Radix {
Bin = 2,
Dec = 10,
Hex = 16,
}
impl Radix {
fn to_f32(self) -> f32 {
match self {
Radix::Bin => 2.,
Radix::Dec => 10.,
Radix::Hex => 16.,
}
}
}
impl std::fmt::Display for Radix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Radix::Bin => write!(f, "binary"),
Radix::Dec => write!(f, "decimal"),
Radix::Hex => write!(f, "hexadecimal"),
}
}
}

View File

@ -37,7 +37,9 @@ impl Stmt {
}
}
Stmt::Ask { pat: _, val, nxt } => {
nxt.gen_map_get(id);
if let Some(nxt) = nxt {
nxt.gen_map_get(id);
}
let substitutions = val.substitute_map_gets(id);
if !substitutions.is_empty() {
*self = gen_get(self, substitutions);

View File

@ -174,7 +174,7 @@ pub enum Stmt {
Ask {
pat: AssignPattern,
val: Box<Expr>,
nxt: Box<Stmt>,
nxt: Option<Box<Stmt>>,
},
// "return" {expr} ";"?
Return {

View File

@ -28,7 +28,9 @@ impl Stmt {
}
Stmt::Ask { val, nxt, .. } => {
val.order_kwargs(book, use_map)?;
nxt.order_kwargs(book, use_map)?;
if let Some(nxt) = nxt {
nxt.order_kwargs(book, use_map)?;
}
}
Stmt::InPlace { val, nxt, .. } => {
val.order_kwargs(book, use_map)?;

View File

@ -496,10 +496,15 @@ impl<'a> PyParser<'a> {
let val = self.parse_expr(true, true)?;
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_indent_exactly(*indent)?;
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: Box::new(nxt) };
return Ok((stmt, nxt_indent));
let nxt_indent = self.advance_newlines()?;
if nxt_indent == *indent {
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: Some(Box::new(nxt)) };
return Ok((stmt, nxt_indent));
} else {
let stmt = Stmt::Ask { pat, val: Box::new(val), nxt: None };
return Ok((stmt, nxt_indent));
}
}
// In-place

View File

@ -61,9 +61,25 @@ impl AssignPattern {
}
}
#[derive(Debug)]
enum StmtToFun {
Return(fun::Term),
Assign(fun::Pattern, fun::Term),
Assign(bool, fun::Pattern, fun::Term),
}
fn take(t: Stmt) -> Result<(bool, Option<fun::Pattern>, fun::Term), String> {
match t.into_fun()? {
StmtToFun::Return(ret) => Ok((false, None, ret)),
StmtToFun::Assign(x, pat, val) => Ok((x, Some(pat), val)),
}
}
fn wrap(nxt: Option<fun::Pattern>, term: fun::Term, ask: bool) -> StmtToFun {
if let Some(pat) = nxt {
StmtToFun::Assign(ask, pat, term)
} else {
StmtToFun::Return(term)
}
}
impl Stmt {
@ -72,10 +88,7 @@ impl Stmt {
// TODO: When we have an error with an assignment, we should show the offending assignment (eg. "{pat} = ...").
let stmt_to_fun = match self {
Stmt::Assign { pat: AssignPattern::MapSet(map, key), val, nxt: Some(nxt) } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(map.clone()))),
val: Box::new(fun::Term::call(
@ -84,11 +97,7 @@ impl Stmt {
)),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Assign { pat: AssignPattern::MapSet(..), val: _, nxt: None } => {
return Err("Branch ends with map assignment.".to_string());
@ -96,28 +105,17 @@ impl Stmt {
Stmt::Assign { pat, val, nxt: Some(nxt) } => {
let pat = pat.into_fun();
let val = val.to_fun();
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Let { pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Assign { pat, val, nxt: None } => {
let pat = pat.into_fun();
let val = val.to_fun();
StmtToFun::Assign(pat, val)
StmtToFun::Assign(false, pat, val)
}
Stmt::InPlace { op, pat, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
// if it is a mapper operation
if let InPlaceOp::Map = op {
let term = match &*pat {
@ -138,11 +136,7 @@ impl Stmt {
}
};
if let Some(pat) = nxt_pat {
return Ok(StmtToFun::Assign(pat, term));
} else {
return Ok(StmtToFun::Return(term));
}
return Ok(wrap(nxt_pat, term, ask));
}
// otherwise
@ -157,11 +151,7 @@ impl Stmt {
}),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
AssignPattern::MapSet(map, key) => {
let temp = Name::new("%0");
@ -177,19 +167,17 @@ impl Stmt {
val: Box::new(map_term),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
_ => unreachable!(),
}
}
Stmt::If { cond, then, otherwise, nxt } => {
let (pat, then, else_) = match (then.into_fun()?, otherwise.into_fun()?) {
(StmtToFun::Return(t), StmtToFun::Return(e)) => (None, t, e),
(StmtToFun::Assign(tp, t), StmtToFun::Assign(ep, e)) if tp == ep => (Some(tp), t, e),
let (ask, pat, then, else_) = match (then.into_fun()?, otherwise.into_fun()?) {
(StmtToFun::Return(t), StmtToFun::Return(e)) => (false, None, t, e),
(StmtToFun::Assign(ask, tp, t), StmtToFun::Assign(ask_, ep, e)) if tp == ep => {
(ask && ask_, Some(tp), t, e)
}
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
return Err("'if' branches end with different assignments.".to_string());
}
@ -213,26 +201,20 @@ impl Stmt {
pred: Some(Name::new("%pred-1")),
arms,
};
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::Match { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst_rgt) = match fst.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst_rgt) = take(fst.rgt)?;
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
fun_arms.push((fst.lft, vec![], fst_rgt));
for arm in arms {
let (arm_pat, arm_rgt) = match arm.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'match' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -246,26 +228,20 @@ impl Stmt {
}
}
let term = fun::Term::Mat { arg: Box::new(arg), bnd, with_bnd, with_arg, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Switch { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst) = match fst.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst) = take(fst)?;
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
fun_arms.push(fst);
for arm in arms {
let (arm_pat, arm) = match arm.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm) = take(arm)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'switch' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -280,26 +256,20 @@ impl Stmt {
}
let pred = Some(Name::new(format!("{}-{}", bnd.clone().unwrap(), fun_arms.len() - 1)));
let term = fun::Term::Swt { arg: Box::new(arg), bnd, with_bnd, with_arg, pred, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Fold { arg, bnd, with_bnd, with_arg, arms, nxt } => {
let arg = arg.to_fun();
let mut fun_arms = vec![];
let mut arms = arms.into_iter();
let fst = arms.next().unwrap();
let (fst_pat, fst_rgt) = match fst.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (fst_ask, fst_pat, fst_rgt) = take(fst.rgt)?;
fun_arms.push((fst.lft, vec![], fst_rgt));
let with_arg = with_arg.into_iter().map(Expr::to_fun).collect();
for arm in arms {
let (arm_pat, arm_rgt) = match arm.rgt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?;
match (&arm_pat, &fst_pat) {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => {
return Err("'fold' arms end with different assignments.".to_string());
}
(Some(_), None) => {
@ -313,14 +283,16 @@ impl Stmt {
}
}
let term = fun::Term::Fold { arg: Box::new(arg), bnd, with_bnd, with_arg, arms: fun_arms };
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
wrap_nxt_assign_stmt(term, nxt, fst_pat, fst_ask)?
}
Stmt::Bend { bnd, arg, cond, step, base, nxt } => {
let arg = arg.into_iter().map(Expr::to_fun).collect();
let cond = cond.to_fun();
let (pat, step, base) = match (step.into_fun()?, base.into_fun()?) {
(StmtToFun::Return(s), StmtToFun::Return(b)) => (None, s, b),
(StmtToFun::Assign(sp, s), StmtToFun::Assign(bp, b)) if sp == bp => (Some(sp), s, b),
let (ask, pat, step, base) = match (step.into_fun()?, base.into_fun()?) {
(StmtToFun::Return(s), StmtToFun::Return(b)) => (false, None, s, b),
(StmtToFun::Assign(aa, sp, s), StmtToFun::Assign(ba, bp, b)) if sp == bp => {
(aa && ba, Some(sp), s, b)
}
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
return Err("'bend' branches end with different assignments.".to_string());
}
@ -337,66 +309,40 @@ impl Stmt {
};
let term =
fun::Term::Bend { bnd, arg, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) };
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::With { typ, bod, nxt } => {
let (pat, bod) = match bod.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, pat, bod) = take(*bod)?;
let term = fun::Term::With { typ, bod: Box::new(bod) };
wrap_nxt_assign_stmt(term, nxt, pat)?
wrap_nxt_assign_stmt(term, nxt, pat, ask)?
}
Stmt::Ask { pat, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
Stmt::Ask { pat, val, nxt: Some(nxt) } => {
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term =
fun::Term::Ask { pat: Box::new(pat.into_fun()), val: Box::new(val.to_fun()), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Ask { pat, val, nxt: None } => {
let pat = pat.into_fun();
let val = val.to_fun();
StmtToFun::Assign(true, pat, val)
}
Stmt::Open { typ, var, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Open { typ, var, bod: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Use { nam, val, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let term = fun::Term::Use { nam: Some(nam), val: Box::new(val.to_fun()), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Return { term } => StmtToFun::Return(term.to_fun()),
Stmt::LocalDef { def, nxt } => {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let (ask, nxt_pat, nxt) = take(*nxt)?;
let def = def.to_fun()?;
let term = fun::Term::Def { def, nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
wrap(nxt_pat, term, ask)
}
Stmt::Err => unreachable!(),
};
@ -508,24 +454,22 @@ fn wrap_nxt_assign_stmt(
term: fun::Term,
nxt: Option<Box<Stmt>>,
pat: Option<fun::Pattern>,
ask: bool,
) -> Result<StmtToFun, String> {
if let Some(nxt) = nxt {
if let Some(pat) = pat {
let (nxt_pat, nxt) = match nxt.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let term = fun::Term::Let { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) };
if let Some(pat) = nxt_pat {
Ok(StmtToFun::Assign(pat, term))
let (ask_nxt, nxt_pat, nxt) = take(*nxt)?;
let term = if ask {
fun::Term::Ask { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) }
} else {
Ok(StmtToFun::Return(term))
}
fun::Term::Let { pat: Box::new(pat), val: Box::new(term), nxt: Box::new(nxt) }
};
Ok(wrap(nxt_pat, term, ask_nxt))
} else {
Err("Statement ends with return but is not at end of function.".to_string())
}
} else if let Some(pat) = pat {
Ok(StmtToFun::Assign(pat, term))
Ok(StmtToFun::Assign(ask, pat, term))
} else {
Ok(StmtToFun::Return(term))
}

View File

@ -114,6 +114,7 @@ impl ParseBook {
let main_imports = main_imports.unwrap_or(&self.import_ctx.map);
let mut local_imports = BindMap::new();
let mut adt_imports = BindMap::new();
// Collect local imports binds, starting with `__` if not imported by the main book.
'outer: for (bind, src) in self.import_ctx.map.binds.iter().rev() {
@ -128,7 +129,6 @@ impl ParseBook {
// Checks if the bind is an loaded ADT name,
// If so, add the constructors binds as `bind/ctr` instead.
// As ADTs names are not used in the syntax, we don't bind their names.
for pkg in self.import_ctx.sources() {
if let Some(book) = pkgs.loaded_adts.get(pkg) {
if let Some(ctrs) = book.get(&nam) {
@ -138,6 +138,8 @@ impl ParseBook {
let bind = Name::new(format!("{}{}", bind, ctr_name));
local_imports.insert(bind, ctr.clone());
}
// Add a mapping of the ADT name
adt_imports.insert(bind.clone(), nam.clone());
continue 'outer;
}
}
@ -149,6 +151,7 @@ impl ParseBook {
for (_, def) in self.local_defs_mut() {
def.apply_binds(true, &local_imports);
def.apply_types(&adt_imports);
}
}
@ -306,6 +309,8 @@ trait Def {
/// and if there are possible constructor names on it, rename rule patterns.
fn apply_binds(&mut self, maybe_constructor: bool, binds: &BindMap);
fn apply_types(&mut self, types: &BindMap);
fn source(&self) -> &Source;
fn source_mut(&mut self) -> &mut Source;
fn name_mut(&mut self) -> &mut Name;
@ -332,6 +337,22 @@ impl Def for Definition {
}
}
fn apply_types(&mut self, types: &BindMap) {
fn rename_with_type(bod: &mut Term, types: &BindMap) {
if let Term::With { typ, .. } = bod {
if let Some(alias) = types.get(typ).cloned() {
*typ = alias;
}
}
for child in bod.children_mut() {
rename_with_type(child, types);
}
}
for rule in self.rules.iter_mut() {
rename_with_type(&mut rule.body, types);
}
}
fn source(&self) -> &Source {
&self.source
}
@ -351,6 +372,86 @@ impl Def for imp::Definition {
self.body = bod.fold_uses(binds.iter().rev());
}
fn apply_types(&mut self, types: &BindMap) {
fn rename_with_type(bod: &mut Stmt, types: &BindMap) {
match bod {
Stmt::With { typ, bod, nxt } => {
if let Some(alias) = types.get(typ).cloned() {
*typ = alias
}
rename_with_type(bod, types);
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Assign { nxt, .. } => {
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::InPlace { nxt, .. } => {
rename_with_type(nxt, types);
}
Stmt::If { then, otherwise, nxt, .. } => {
rename_with_type(then, types);
rename_with_type(otherwise, types);
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Match { arms, nxt, .. } => {
for arm in arms {
rename_with_type(&mut arm.rgt, types);
}
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Switch { arms, nxt, .. } => {
for arm in arms {
rename_with_type(arm, types);
}
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Bend { step, base, nxt, .. } => {
rename_with_type(step, types);
rename_with_type(base, types);
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Fold { arms, nxt, .. } => {
for arm in arms {
rename_with_type(&mut arm.rgt, types);
}
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Ask { nxt, .. } => {
if let Some(nxt) = nxt {
rename_with_type(nxt, types);
}
}
Stmt::Return { .. } => {}
Stmt::Open { nxt, .. } => {
rename_with_type(nxt, types);
}
Stmt::Use { nxt, .. } => {
rename_with_type(nxt, types);
}
Stmt::LocalDef { def, nxt } => {
def.apply_types(types);
rename_with_type(nxt, types);
}
Stmt::Err => {}
}
}
rename_with_type(&mut self.body, types);
}
fn source(&self) -> &Source {
&self.source
}
@ -368,6 +469,8 @@ impl Def for HvmDefinition {
/// Do nothing, can not apply binds to a HvmDefinition.
fn apply_binds(&mut self, _maybe_constructor: bool, _binds: &BindMap) {}
fn apply_types(&mut self, _types: &BindMap) {}
fn source(&self) -> &Source {
&self.source
}
@ -379,6 +482,18 @@ impl Def for HvmDefinition {
fn name_mut(&mut self) -> &mut Name {
&mut self.name
}
fn canonicalize_name(&mut self, src: &Name, main_imports: &ImportsMap, binds: &mut BindMap) {
let def_name = self.name_mut();
let mut new_name = Name::new(std::format!("{}/{}", src, def_name));
if !main_imports.contains_source(&new_name) {
new_name = Name::new(std::format!("__{}", new_name));
}
binds.insert(def_name.clone(), new_name.clone());
*def_name = new_name;
}
}
impl Term {

View File

@ -23,7 +23,8 @@ fn hvm_to_inodes(net: &Net) -> INodes {
// Convert all the trees forming active pairs.
for (i, (_, tree1, tree2)) in net.rbag.iter().enumerate() {
let tree_root = format!("a{i}");
// This name cannot appear anywhere in the original net
let tree_root = format!("%a{i}");
let mut tree1 = tree_to_inodes(tree1, tree_root.clone(), net_root, &mut n_vars);
inodes.append(&mut tree1);
let mut tree2 = tree_to_inodes(tree2, tree_root, net_root, &mut n_vars);
@ -33,7 +34,8 @@ fn hvm_to_inodes(net: &Net) -> INodes {
}
fn new_var(n_vars: &mut NodeId) -> String {
let new_var = format!("x{n_vars}");
// This name cannot appear anywhere in the original net
let new_var = format!("%x{n_vars}");
*n_vars += 1;
new_var
}
@ -46,7 +48,6 @@ fn tree_to_inodes(tree: &Tree, tree_root: String, net_root: &str, n_vars: &mut N
n_vars: &mut NodeId,
) -> String {
if let Tree::Var { nam } = subtree {
//
if nam == net_root {
"_".to_string()
} else {

View File

@ -0,0 +1,12 @@
type Bool:
T
F
def main:
with IO:
match _ = Bool/T:
case Bool/T:
x <- wrap(0)
case Bool/F:
x = wrap(0)
return wrap(x)

View File

@ -0,0 +1,12 @@
type Bool:
T
F
def main:
with IO:
match _ = Bool/T:
case Bool/T:
x <- wrap(0)
case Bool/F:
x <- wrap(0)
return wrap(x)

View File

@ -0,0 +1,7 @@
from lib/MyOption import (MyOption, MyOption/bind, MyOption/wrap)
def main():
with MyOption:
a <- MyOption/Some(1)
b <- MyOption/Some(2)
return wrap((a, b))

View File

@ -0,0 +1,12 @@
type MyOption = (Some val) | (None)
def MyOption/bind(val, nxt):
match val:
case MyOption/Some:
nxt = undefer(nxt)
return nxt(val.val)
case MyOption/None:
return MyOption/None
def MyOption/wrap(val):
return MyOption/Some(val)

View File

@ -0,0 +1,2 @@
def main:
return 0xA.0xA

View File

@ -0,0 +1 @@
main & = (a b c)

View File

@ -0,0 +1,2 @@
def main:
return [0x12.129, 0x0.2, 0b101.101, 0xAAAAAAAA.AAAAAAAA, 0xA.__A__]

View File

@ -0,0 +1,4 @@
def main:
var = Tree/reverse(![![!1, !2],![!3, !4]])
return Tree/to_list(var)

View File

@ -4,6 +4,6 @@ input_file: tests/golden_tests/compile_file/just_a_name.bend
---
Errors:
In tests/golden_tests/compile_file/just_a_name.bend :
- expected: pattern-matching pattern
- expected: pattern or '='
- detected: end of input
 1 | asdf 
 1 | asdf

View File

@ -0,0 +1,6 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/compile_file/mismatched_ask_statements.bend
---
Errors:
In function 'main': 'match' arms end with different assignments.

View File

@ -0,0 +1,35 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/desugar_file/ask_branch.bend
---
(undefer) = λa (a λb b)
(IO/MAGIC) = (13683217, 16719857)
(IO/wrap) = λa (IO/Done IO/MAGIC a)
(IO/bind) = λa λb (a IO/bind__C2 b)
(main) = (IO/bind (Bool/T λa switch a { 0: (IO/wrap 0); _: λ* (IO/wrap 0); }) λb (b λc λd (c d) IO/wrap))
(IO/Done) = λa λb λc (c IO/Done/tag a b)
(IO/Call) = λa λb λc λd λe (e IO/Call/tag a b c d)
(Bool/T) = λa (a Bool/T/tag)
(Bool/F) = λa (a Bool/F/tag)
(IO/Done/tag) = 0
(IO/Call/tag) = 1
(Bool/T/tag) = 0
(Bool/F/tag) = 1
(IO/bind__C0) = λ* λa λb (undefer b a)
(IO/bind__C1) = λ* λ* λa λb λc λd (IO/Call IO/MAGIC a b λe (IO/bind (c e) d))
(IO/bind__C2) = λa switch a { 0: IO/bind__C0; _: IO/bind__C1; }

View File

@ -0,0 +1,5 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/import_system/import_type.bend
---
λa (a lib/MyOption/MyOption/Some/tag (1, 2))

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/bad_floating.bend
---
Errors:
In tests/golden_tests/parse_file/bad_floating.bend :
- expected: newline
- detected:
 2 | return 0xA.0xA

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/strange_pattern.bend
---
Errors:
In tests/golden_tests/parse_file/strange_pattern.bend :
- expected: pattern or '='
- detected:
 1 | main & = (a b c)

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/floating_numbers.bend
---
NumScott:
[18.072, 0.125, 5.625, 2863333376.000, 10.625]
Scott:
[18.072, 0.125, 5.625, 2863333376.000, 10.625]

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/tree_to_list.bend
---
NumScott:
[4, 3, 2, 1]
Scott:
[4, 3, 2, 1]