mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-17 14:47:21 +03:00
Merge pull request #529 from HigherOrderCO/465-add-mapper-statements
#465 Add mapper statements
This commit is contained in:
commit
3c9a532df1
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -62,7 +62,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bend-lang"
|
||||
version = "0.2.27"
|
||||
version = "0.2.28"
|
||||
dependencies = [
|
||||
"TSPL",
|
||||
"clap",
|
||||
|
@ -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/"]
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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 } => {
|
||||
|
7
tests/golden_tests/desugar_file/mapper_syntax.bend
Normal file
7
tests/golden_tests/desugar_file/mapper_syntax.bend
Normal 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])
|
7
tests/golden_tests/run_file/mapper_syntax.bend
Normal file
7
tests/golden_tests/run_file/mapper_syntax.bend
Normal 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])
|
85
tests/snapshots/desugar_file__mapper_syntax.bend.snap
Normal file
85
tests/snapshots/desugar_file__mapper_syntax.bend.snap
Normal 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)
|
9
tests/snapshots/run_file__mapper_syntax.bend.snap
Normal file
9
tests/snapshots/run_file__mapper_syntax.bend.snap
Normal 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))
|
Loading…
Reference in New Issue
Block a user