Merge pull request #529 from HigherOrderCO/465-add-mapper-statements

#465 Add mapper statements
This commit is contained in:
Nicolas Abril 2024-05-30 20:03:25 +00:00 committed by GitHub
commit 3c9a532df1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 235 additions and 29 deletions

2
Cargo.lock generated
View File

@ -62,7 +62,7 @@ dependencies = [
[[package]]
name = "bend-lang"
version = "0.2.27"
version = "0.2.28"
dependencies = [
"TSPL",
"clap",

View File

@ -2,7 +2,7 @@
name = "bend-lang"
description = "A high-level, massively parallel programming language"
license = "Apache-2.0"
version = "0.2.27"
version = "0.2.28"
edition = "2021"
rust-version = "1.74"
exclude = ["tests/snapshots/"]

View File

@ -142,6 +142,17 @@ The operations are:
- Subtraction `-=`
- Multiplication `*=`
- Division `/=`
- Bit And `&=`
- Bit Or `|=`
- Bit Xor `^=`
- Mapper `@=`
The mapper in-place operation applies a function and re-assigns the variable:
```python
x = "hello"
x @= String/uppercase
```
### Return

View File

@ -45,6 +45,19 @@ Map/set map key value =
}
}
Map/map (Map/Leaf) key f = Map/Leaf
Map/map (Map/Node value left right) key f =
switch _ = (== 0 key) {
0: switch _ = (% key 2) {
0:
(Map/Node value (Map/map left (/ key 2) f) right)
_:
(Map/Node value left (Map/map right (/ key 2) f))
}
_: (Map/Node (f value) left right)
}
# IO Impl
type IO:

View File

@ -39,12 +39,23 @@ impl Stmt {
*self = gen_get(self, substitutions);
}
}
Stmt::InPlace { op: _, var: _, val, nxt } => {
Stmt::InPlace { op: _, pat, val, nxt } => {
let key_substitutions = if let AssignPattern::MapSet(_, key) = &mut **pat {
key.substitute_map_gets(id)
} else {
Vec::new()
};
nxt.gen_map_get(id);
let substitutions = val.substitute_map_gets(id);
if !substitutions.is_empty() {
*self = gen_get(self, substitutions);
}
if !key_substitutions.is_empty() {
*self = gen_get(self, key_substitutions);
}
}
Stmt::If { cond, then, otherwise, nxt } => {
then.gen_map_get(id);

View File

@ -72,6 +72,7 @@ pub enum InPlaceOp {
And,
Or,
Xor,
Map,
}
#[derive(Clone, Debug, Default)]
@ -85,7 +86,7 @@ pub enum Stmt {
// {var} += {val} ";"? {nxt}
InPlace {
op: InPlaceOp,
var: Name,
pat: Box<AssignPattern>,
val: Box<Expr>,
nxt: Box<Stmt>,
},
@ -215,6 +216,7 @@ impl InPlaceOp {
InPlaceOp::And => Op::AND,
InPlaceOp::Or => Op::OR,
InPlaceOp::Xor => Op::XOR,
InPlaceOp::Map => unreachable!(),
}
}
}

View File

@ -464,16 +464,20 @@ impl<'a> PyParser<'a> {
return Ok((stmt, nxt_indent));
}
// In-place
if let AssignPattern::Var(name) = pat {
if let Some(op) = self.parse_in_place_op()? {
let val = self.parse_expr(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::InPlace { op, var: name, val: Box::new(val), nxt: Box::new(nxt) };
return Ok((stmt, nxt_indent));
}
match &pat {
AssignPattern::Var(..) => {}
AssignPattern::MapSet(..) => {}
_ => 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)?;
self.skip_trivia_inline();
self.try_consume_exactly(";");
self.consume_indent_exactly(*indent)?;
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::InPlace { op, pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) };
return Ok((stmt, nxt_indent));
}
self.expected_spanned("statement", ini_idx, end_idx)
@ -502,6 +506,9 @@ impl<'a> PyParser<'a> {
} else if self.starts_with("^=") {
self.consume("^=")?;
Some(InPlaceOp::Xor)
} else if self.starts_with("@=") {
self.consume("@=")?;
Some(InPlaceOp::Map)
} else {
None
};

View File

@ -1,4 +1,4 @@
use super::{AssignPattern, Definition, Expr, Stmt};
use super::{AssignPattern, Definition, Expr, InPlaceOp, Stmt};
use crate::fun::{
self,
builtins::{LCONS, LNIL},
@ -93,24 +93,78 @@ impl Stmt {
let val = val.to_fun();
StmtToFun::Assign(pat, val)
}
Stmt::InPlace { op, var, val, nxt } => {
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 term = fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(var.clone()))),
val: Box::new(fun::Term::Oper {
opr: op.to_lang_op(),
fst: Box::new(fun::Term::Var { nam: var }),
snd: Box::new(val.to_fun()),
}),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
// if it is a mapper operation
if let InPlaceOp::Map = op {
let term = match &*pat {
AssignPattern::MapSet(map, key) => {
let rhs = fun::Term::call(
fun::Term::r#ref("Map/map"),
[fun::Term::Var { nam: map.clone() }, key.clone().to_fun(), val.clone().to_fun()],
);
fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(map.clone()))),
val: Box::new(rhs),
nxt: Box::new(nxt),
}
}
_ => {
let rhs = fun::Term::call(val.to_fun(), [pat.clone().into_fun().to_term()]);
fun::Term::Let { pat: Box::new(pat.into_fun()), val: Box::new(rhs), nxt: Box::new(nxt) }
}
};
if let Some(pat) = nxt_pat {
return Ok(StmtToFun::Assign(pat, term));
} else {
return Ok(StmtToFun::Return(term));
}
}
// otherwise
match *pat {
AssignPattern::Var(var) => {
let term = fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(var.clone()))),
val: Box::new(fun::Term::Oper {
opr: op.to_lang_op(),
fst: Box::new(fun::Term::Var { nam: var }),
snd: Box::new(val.to_fun()),
}),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
}
AssignPattern::MapSet(map, key) => {
let temp = Name::new("%0");
let partial =
Expr::Opr { op: op.to_lang_op(), lhs: Box::new(Expr::Var { nam: temp.clone() }), rhs: val };
let map_fn = Expr::Lam { names: vec![(temp, false)], bod: Box::new(partial) };
let map_term = fun::Term::call(
fun::Term::r#ref("Map/map"),
[fun::Term::Var { nam: map.clone() }, key.to_fun(), map_fn.to_fun()],
);
let term = fun::Term::Let {
pat: Box::new(fun::Pattern::Var(Some(map))),
val: Box::new(map_term),
nxt: Box::new(nxt),
};
if let Some(pat) = nxt_pat {
StmtToFun::Assign(pat, term)
} else {
StmtToFun::Return(term)
}
}
_ => unreachable!(),
}
}
Stmt::If { cond, then, otherwise, nxt } => {

View File

@ -0,0 +1,7 @@
def main:
x = 1
x @= lambda x: x + 1
map = { 0: 3, 1: 4 }
map[1] += 1
map[1] @= lambda x: x * 2
return (x, map[1], map[0])

View File

@ -0,0 +1,7 @@
def main:
x = 1
x @= lambda x: x + 1
map = { 0: 3, 1: 4 }
map[1] += 1
map[1] @= lambda x: x * 2
return (x, map[1], map[0])

View File

@ -0,0 +1,85 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/desugar_file/mapper_syntax.bend
---
(Map/empty) = Map/Leaf
(Map/get) = λa λb (a Map/get__C5 b)
(Map/set) = λa λb λc (a Map/set__C10 c b)
(Map/map) = λa λb λc (a Map/map__C5 b c)
(main) = let (a, b) = main__C8; let (c, *) = (Map/get b 0); (main__C7, a, c)
(Map/Node) = λa λb λc λd (d Map/Node/tag a b c)
(Map/Leaf) = λa (a Map/Leaf/tag)
(Map/Node/tag) = 0
(Map/Leaf/tag) = 1
(Map/get__C0) = λa λb λc λd let (e, f) = (Map/get c (/ a 2)); (e, (Map/Node b f d))
(Map/get__C1) = λ* λa λb λc λd let (e, f) = (Map/get d (/ a 2)); (e, (Map/Node b c f))
(Map/get__C2) = λa let {b c} = a; λd λe λf λ* (switch (% b 2) { 0: Map/get__C0; _: Map/get__C1; } c d e f)
(Map/get__C3) = λ* λ* λa λ* λ* λb (a, b)
(Map/get__C4) = λa let {b c} = a; λd let {e f} = d; λg let {h i} = g; λj let {k l} = j; (switch (== 0 k) { 0: Map/get__C2; _: Map/get__C3; } l b e h (Map/Node c f i))
(Map/get__C5) = λa switch a { 0: Map/get__C4; _: λ* λ* (*, Map/Leaf); }
(Map/map__C0) = λa λb λc λd λe (Map/Node e (Map/map d (/ b 2) a) c)
(Map/map__C1) = λ* λa λb λc λd λe (Map/Node e d (Map/map c (/ b 2) a))
(Map/map__C2) = λa λb let {c d} = b; λe λf λg (switch (% c 2) { 0: Map/map__C0; _: Map/map__C1; } a d e f g)
(Map/map__C3) = λ* λa λ* λb λc λd (Map/Node (a d) c b)
(Map/map__C4) = λa λb λc λd let {e f} = d; λg (switch (== 0 e) { 0: Map/map__C2; _: Map/map__C3; } g f c b a)
(Map/map__C5) = λa switch a { 0: Map/map__C4; _: λ* λ* λ* Map/Leaf; }
(Map/set__C0) = λa λb λc λd λe (Map/Node c (Map/set d (/ b 2) a) e)
(Map/set__C1) = λ* λa λb λc λd λe (Map/Node c d (Map/set e (/ b 2) a))
(Map/set__C10) = λa switch a { 0: Map/set__C8; _: Map/set__C9; }
(Map/set__C2) = λa λb let {c d} = b; λe λf λg (switch (% c 2) { 0: Map/set__C0; _: Map/set__C1; } a d e f g)
(Map/set__C3) = λ* λa λ* λ* λb λc (Map/Node a b c)
(Map/set__C4) = λa λb (Map/Node * (Map/set Map/Leaf (/ b 2) a) Map/Leaf)
(Map/set__C5) = λ* λa λb (Map/Node * Map/Leaf (Map/set Map/Leaf (/ b 2) a))
(Map/set__C6) = λa λb let {c d} = b; (switch (% c 2) { 0: Map/set__C4; _: Map/set__C5; } a d)
(Map/set__C7) = λ* λa λ* (Map/Node a Map/Leaf Map/Leaf)
(Map/set__C8) = λa λb λc λd λe let {f g} = e; (switch (== 0 f) { 0: Map/set__C2; _: Map/set__C3; } d g a b c)
(Map/set__C9) = λ* λa λb let {c d} = b; (switch (== 0 c) { 0: Map/set__C6; _: Map/set__C7; } a d)
(main__C0) = (Map/set Map/empty 0 3)
(main__C1) = λa (+ a 1)
(main__C2) = (Map/set main__C0 1 4)
(main__C3) = λa (* a 2)
(main__C4) = (Map/map main__C2 1 main__C1)
(main__C5) = (Map/map main__C4 1 main__C3)
(main__C6) = λa (+ a 1)
(main__C7) = (main__C6 1)
(main__C8) = (Map/get main__C5 1)

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/run_file/mapper_syntax.bend
---
NumScott:
((λa (+ a 1) 1), (10, 3))
Scott:
((λa (+ a 1) 1), (10, 3))