mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-19 07:37:56 +03:00
Merge remote-tracking branch 'origin/main' into feature/sc-678/add-run-c-and-run-cuda-commands-to-bend
This commit is contained in:
commit
46ec41141e
249
docs/syntax.md
249
docs/syntax.md
@ -2,13 +2,13 @@
|
||||
|
||||
This file provides a reference of each possible syntax of bend programming language.
|
||||
|
||||
Click [here](#bend-syntax) to see the bend syntax.
|
||||
Click [here](#imp-syntax) to see the syntax for "imp", the variant of bend that looks like an imperative language like python.
|
||||
|
||||
Click [here](#core-syntax) to see the core syntax.
|
||||
Click [here](#fun-syntax) to see the syntax for "fun", the variant of bend that looks like a functional language like Haskell or ML.
|
||||
|
||||
<div id="bend-syntax"></div>
|
||||
<div id="imp-syntax"></div>
|
||||
|
||||
# Bend Syntax
|
||||
# Imp Syntax
|
||||
|
||||
## Top-level definitions
|
||||
|
||||
@ -25,10 +25,12 @@ def main:
|
||||
return add(40, 2)
|
||||
```
|
||||
|
||||
A definition is composed by a name, a sequence of parameters and a body.
|
||||
A function definition is composed by a name, a sequence of parameters and a body.
|
||||
|
||||
A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except it can't have `__` (used for generated names).
|
||||
|
||||
The last statement of each branch of the function must be a `return`.
|
||||
|
||||
### Type
|
||||
|
||||
Defines an algebraic data type.
|
||||
@ -59,22 +61,28 @@ Read [defining data types](./defining-data-types.md) to know more.
|
||||
### Assignment
|
||||
|
||||
```python
|
||||
value = 2;
|
||||
return value;
|
||||
value = 2
|
||||
return value
|
||||
|
||||
(first, second) = (1, 2);
|
||||
return second;
|
||||
(first, second) = (1, 2)
|
||||
return second
|
||||
|
||||
{x y} = {2 3};
|
||||
{x y} = {2 3}
|
||||
```
|
||||
|
||||
Assigns a value to a variable, it's possible to pattern match match tuples and superpositions.
|
||||
Assigns a value to a variable.
|
||||
|
||||
It's possible to assign to a pattern, like a tuple or superposition, which will destructure the value returned by the expression.
|
||||
|
||||
```python
|
||||
(first, second) = (1, 2)
|
||||
```
|
||||
|
||||
### In-Place Operation
|
||||
|
||||
```python
|
||||
x += 1;
|
||||
return x;
|
||||
x += 1
|
||||
return x
|
||||
```
|
||||
|
||||
The in-place operation does an infix operation and re-assigns a variable.
|
||||
@ -89,23 +97,52 @@ The operations are:
|
||||
### Return
|
||||
|
||||
```python
|
||||
return "hello";
|
||||
return "hello"
|
||||
```
|
||||
|
||||
Returns the following expression. All paths or branches should end with a return.
|
||||
Returns the expression that follows. The last statement of each branch of a function must be a `return`.
|
||||
|
||||
```py
|
||||
// Allowed, all branches return
|
||||
def max(a, b):
|
||||
if a > b:
|
||||
return a
|
||||
else:
|
||||
return b
|
||||
```
|
||||
```py
|
||||
// Not allowed, early return
|
||||
def Foo(x):
|
||||
if test_condition(x):
|
||||
return "err"
|
||||
else:
|
||||
y = map(x)
|
||||
|
||||
return y
|
||||
```
|
||||
```py
|
||||
// Not allowed, one of the branches doesn't return
|
||||
def Foo(a, b):
|
||||
if a < b:
|
||||
return a
|
||||
else:
|
||||
c = a + b
|
||||
```
|
||||
|
||||
### If
|
||||
|
||||
```python
|
||||
if condition:
|
||||
return 0;
|
||||
return 0
|
||||
else:
|
||||
return 1;
|
||||
return 1
|
||||
```
|
||||
|
||||
A branching statement where `else` is mandatory.
|
||||
|
||||
The condition must return an `u24` and it is equivalent to a switch statement:
|
||||
The condition must return a `u24` number, where 0 will run the `else` branch and any other value will return the first one.
|
||||
|
||||
It is equivalent to a switch statement:
|
||||
|
||||
```
|
||||
switch _ = condition:
|
||||
@ -120,25 +157,25 @@ switch _ = condition:
|
||||
```python
|
||||
switch x = 4:
|
||||
case 0:
|
||||
return "Zero";
|
||||
return 0
|
||||
case 1:
|
||||
return "One";
|
||||
return 0
|
||||
case _:
|
||||
return "Not zero or one";
|
||||
return x-2
|
||||
```
|
||||
|
||||
A switch for native numbers, the pattern matching cases must start from `0` up to `_` sequentially.
|
||||
|
||||
It is possible to bind a variable name to the matching value, it allows the access to the predecessor `x-2` (in this case) or `bound_var-next_num` (in the general case).
|
||||
In the last arm, the predecessor value is available with the name `x-2` (in this case) or `bound_var-next_num` (in the general case).
|
||||
|
||||
### Match
|
||||
|
||||
```python
|
||||
match x = Option/none:
|
||||
case Option/some:
|
||||
return x.value;
|
||||
y = x.value
|
||||
case Option/none:
|
||||
return 0;
|
||||
y = 0
|
||||
```
|
||||
|
||||
A pattern matching statement, the cases must be the constructor names of the matching value.
|
||||
@ -150,9 +187,9 @@ It is possible to bind a variable name to the matching value. The fields of the
|
||||
```python
|
||||
fold x = Tree/leaf:
|
||||
case Tree/node:
|
||||
return x.value + x.left + x.right;
|
||||
return x.value + x.left + x.right
|
||||
case Tree/leaf:
|
||||
return 0;
|
||||
return 0
|
||||
```
|
||||
|
||||
A fold statement. Reduces the given value with the given match cases.
|
||||
@ -179,23 +216,28 @@ fold(Tree/Leaf)
|
||||
Bend can be used to create recursive data structures:
|
||||
|
||||
```rust
|
||||
bend x = 0 while x < 10:
|
||||
left = go(x + 1);
|
||||
right = go(x + 1);
|
||||
return Tree/Node(left, right);
|
||||
then:
|
||||
return Tree/Leaf(x);
|
||||
bend x = 0:
|
||||
when x < 10:
|
||||
left = go(x + 1)
|
||||
right = go(x + 1)
|
||||
y = Tree/Node(left, right)
|
||||
else:
|
||||
y = Tree/Leaf(x)
|
||||
```
|
||||
|
||||
Which binds a variable to the return of an inline recursive function.
|
||||
The function `go` is available inside the bend body and calls it recursively.
|
||||
The function `go` is available inside the `when` arm of the `bend` and calls it recursively.
|
||||
|
||||
It is possible to initialize multiple variables:
|
||||
It is possible to pass multiple state variables, which can be initialized:
|
||||
|
||||
```python
|
||||
bend x = 1, y = 2 ... while condition(x, y, ...):
|
||||
bend x = 1, y = 2 ...:
|
||||
when condition(x, y, ...):
|
||||
...
|
||||
```
|
||||
|
||||
When calling `go`, the function must receive the same number of arguments as the number of state variables.
|
||||
|
||||
It is equivalent to this inline recursive function:
|
||||
|
||||
```python
|
||||
@ -210,16 +252,34 @@ def bend(x, y, ...):
|
||||
### Do
|
||||
|
||||
```python
|
||||
do Result.bind:
|
||||
x <- safe_div(2, 0);
|
||||
return x;
|
||||
do Result:
|
||||
x <- safe_div(2, 0)
|
||||
return x
|
||||
```
|
||||
|
||||
A monadic do block.
|
||||
|
||||
Where `x <- ...` performs a monadic operation.
|
||||
|
||||
Other statements are allowed inside the `do` block.
|
||||
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:
|
||||
case Result/ok:
|
||||
return nxt(res.value)
|
||||
case Result/err:
|
||||
return res
|
||||
```
|
||||
|
||||
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:
|
||||
x <- safe_div(2, 0);
|
||||
y = x
|
||||
return y
|
||||
```
|
||||
|
||||
## Expressions
|
||||
|
||||
@ -236,10 +296,12 @@ A variable can be anything matching the regex `[A-Za-z0-9_.-/]+`.
|
||||
A variable is a name for some immutable expression. It is possible to rebind variables with the same name.
|
||||
|
||||
```python
|
||||
x = 1;
|
||||
x = x + 1;
|
||||
x = 1
|
||||
x = x + 1
|
||||
```
|
||||
|
||||
Note that `-` is also used for negative numbers and as the numeric operator. Bend's grammar is greedily parsed from left to right, meaning that `x-3` always represents a name and not `x - 3` or a sequence of expressions like in `[x -3]`.
|
||||
|
||||
### Lambdas
|
||||
|
||||
```python
|
||||
@ -266,7 +328,7 @@ Like lambdas, with the exception that the variable starts with a `$` sign. Every
|
||||
|
||||
Unscoped variables are not transformed and linearized like normal scoped variables.
|
||||
|
||||
Read [using scopeless lambdas](/docs/using-scopeless-lambdas.md) to know more about.
|
||||
Read [using scopeless lambdas](/docs/using-scopeless-lambdas.md) to know more about their behavior.
|
||||
|
||||
### Function Call
|
||||
|
||||
@ -274,11 +336,19 @@ Read [using scopeless lambdas](/docs/using-scopeless-lambdas.md) to know more ab
|
||||
callee(arg_1, arg_2, arg_n)
|
||||
```
|
||||
|
||||
A call is written with a callee followed by a list of arguments.
|
||||
A call is written with a callee followed by a list of arguments. Arguments can be optionally separated by `,`.
|
||||
|
||||
The effect of a function call is to substitute the callee with it's body and replace the arguments by the passed variables.
|
||||
|
||||
Accepts partial applications.
|
||||
The called function can be any expression and it supports partial applications.
|
||||
|
||||
Optionally, if you call a function by its name, you can used named arguments:
|
||||
|
||||
```python
|
||||
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.
|
||||
|
||||
### Tuple
|
||||
|
||||
@ -294,7 +364,7 @@ A Tuple is surrounded by `(` `)` and should contain 2 or more elements. Elements
|
||||
{1 2 3}
|
||||
```
|
||||
|
||||
A superposition of values is defined using `{` `}` with at least 2 expressions inside.
|
||||
A superposition of values is defined using `{` `}` with at least 2 expressions inside. Elements can be optionally separated by `,`.
|
||||
|
||||
Read [sups and dups](./dups-and-sups.md) to know more.
|
||||
|
||||
@ -344,6 +414,17 @@ A Character is surrounded with `'`. Accepts unicode characters, unicode escapes
|
||||
|
||||
Only supports unicode codepoints up to `0xFFFFFF`.
|
||||
|
||||
### Symbol Literal
|
||||
|
||||
```python
|
||||
// Becomes 2146 (33 << 6 + 34)
|
||||
`hi`
|
||||
```
|
||||
|
||||
A Symbol encodes a up to 4 base64 characters as a `u24` number. It is surrounded by `\``.
|
||||
|
||||
Empty characters are interpreted as `A` which has value 0, meaning that `B` is the same as `AAAB`.
|
||||
|
||||
### String Literal
|
||||
|
||||
```python
|
||||
@ -397,7 +478,7 @@ fold list:
|
||||
|
||||
<div id="core-syntax"></div>
|
||||
|
||||
# Core Syntax
|
||||
# Fun Syntax
|
||||
|
||||
## Top-level definitions
|
||||
|
||||
@ -410,15 +491,18 @@ Name (Ctr1 sub_arg1 sub_arg2) arg3 = rule0_body
|
||||
Name Ctr2 arg3 = rule1_body
|
||||
```
|
||||
|
||||
### Definitions
|
||||
A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except it can't have `__` (used for generated names).
|
||||
|
||||
Definitions can have multiple rules pattern matching each defined argument.
|
||||
### Function Definitions
|
||||
|
||||
A function definition is composed of a sequence of pattern matching equations.
|
||||
Each rule is the name of the function, a sequence of patterns and then the body.
|
||||
|
||||
```rust
|
||||
identity x = x
|
||||
|
||||
Bool.neg True = False
|
||||
Bool.neg False = True
|
||||
(Bool.neg True) = False
|
||||
(Bool.neg False) = True
|
||||
|
||||
MapMaybe (Some val) f = (Some (f val))
|
||||
MapMaybe None f = None
|
||||
@ -426,10 +510,6 @@ MapMaybe None f = None
|
||||
Pair.get (fst, snd) f = (f fst snd)
|
||||
```
|
||||
|
||||
A top-level name can be anything matching the regex `[A-Za-z0-9_.-/]+`, except it can't have `__` (used for generated names).
|
||||
|
||||
A function definition is composed of a sequence of rules, where a rule is the name of the function, a sequence of patterns and then the body.
|
||||
|
||||
A rule pattern can be:
|
||||
|
||||
- A variable.
|
||||
@ -437,8 +517,19 @@ A rule pattern can be:
|
||||
- A constructor.
|
||||
- A tuple.
|
||||
- A superposition.
|
||||
- A wildcard `*`.
|
||||
|
||||
The rule body is a `term`, there is no statements in the language.
|
||||
And the builtin types that desugar to one of the above:
|
||||
|
||||
- A list (becomes a constructor).
|
||||
- A string (becomes a constructor).
|
||||
- A natural number (becomes a constructor).
|
||||
- A character (becomes a number).
|
||||
- A symbol (becomes a number);
|
||||
|
||||
Unscoped variables can't be defined in a rule pattern.
|
||||
|
||||
The rule body is a term, there are no statements in the Fun variant of Bend.
|
||||
|
||||
Read [pattern matching](./pattern-matching.md) to learn about what exactly the rules for pattern matching equations are.
|
||||
|
||||
@ -457,6 +548,8 @@ data Tree
|
||||
|
||||
Each constructor is defined by a name followed by its fields. The `~` notation describes a recursive field.
|
||||
|
||||
The constructors inherit the name of their types and become functions (`Tree/Node` and `Tree/Leaf` in this case).
|
||||
|
||||
## Terms
|
||||
|
||||
### Variables
|
||||
@ -484,21 +577,24 @@ let x = (+ x 1)
|
||||
|
||||
Lambdas represents anonymous inline functions, it can be written with `λ` or `@` followed by a pattern and a term.
|
||||
|
||||
A pattern is equivalent to a let:
|
||||
A tuple or duplication pattern is equivalent to a lambda followed by a `let`.
|
||||
|
||||
```rust
|
||||
λ(fst, snd) snd
|
||||
λa let (fst, snd) = a; snd
|
||||
|
||||
λa let {x y} = a; x
|
||||
λ{x y} (x y)
|
||||
λa let {x y} = a; (x y)
|
||||
```
|
||||
|
||||
### Unscoped Lambdas and Variables
|
||||
### Unscoped Variables
|
||||
|
||||
```
|
||||
λ$x $x
|
||||
```
|
||||
|
||||
Same as lambdas, with the exception that the variable starts with a `$` sign. Every unscoped variable in a function must have a unique name and must be used exactly once.
|
||||
Like a normal scoped variable, but starts with a `$` sign. Every unscoped variable in a function must have a unique name and must be used exactly once.
|
||||
They can be defined anywhere a scoped variable would be defined in a term, like in a lambda or a `let`.
|
||||
|
||||
Unscoped variables are not transformed and linearized like normal scoped variables.
|
||||
|
||||
@ -598,11 +694,11 @@ It is desugared according to the chosen encoding. Read [pattern matching](./patt
|
||||
|
||||
Using `;` is optional.
|
||||
|
||||
### Monadic bind
|
||||
### Monadic bind blocks
|
||||
|
||||
```rust
|
||||
Result.bind (Result.ok val) f = (f val)
|
||||
Result.bind err _ = err
|
||||
Result/bind (Result.ok val) f = (f val)
|
||||
Result/bind err _ = err
|
||||
|
||||
div a b = switch b {
|
||||
0: (Result.err "Div by 0")
|
||||
@ -614,18 +710,28 @@ rem a b = switch b {
|
||||
_: (Result.ok (% a b))
|
||||
}
|
||||
|
||||
Main = do Result.bind {
|
||||
Main = do Result {
|
||||
ask y = (div 3 2);
|
||||
ask x = (rem y 0);
|
||||
x
|
||||
}
|
||||
```
|
||||
|
||||
Receives a monadic bind function and then expects a block.
|
||||
Receives a type defined with `data` or `type` and expects `Result/bind` to be defined as a monadic bind function.
|
||||
It should be of type `(Result a) -> (a -> Result b) -> Result b`, like in the example above.
|
||||
|
||||
The block is followed by `ask` binds and ends with a return term.
|
||||
Inside a `do` block, you can use `ask`, to access the continuation value of the monadic operation.
|
||||
|
||||
The monad bind function should be of type `(Monad a) -> (a -> (Monad b)) -> (Monad b)`.
|
||||
```rust
|
||||
ask y = (div 3 2)
|
||||
ask x = (rem y 0)
|
||||
x
|
||||
|
||||
// Becomes
|
||||
(Result/bind (div 3 2) λy (Result/bind (rem y 0) λx x))
|
||||
```
|
||||
|
||||
It can be used to force a sequence of operations. Since the continuation receives the result through a lambda, it is only fully evaluated after something is applied to it.
|
||||
|
||||
### Numbers and operations
|
||||
|
||||
@ -664,6 +770,17 @@ A Character is surrounded with `'`. Accepts unicode characters, unicode escapes
|
||||
|
||||
Only supports unicode codepoints up to `0xFFFFFF`.
|
||||
|
||||
### Symbol Literal
|
||||
|
||||
```python
|
||||
// Becomes 2146 (33 << 6 + 34)
|
||||
`hi`
|
||||
```
|
||||
|
||||
A Symbol encodes a up to 4 base64 characters as a `u24` number. It is surrounded by `\``.
|
||||
|
||||
Empty characters are interpreted as `A` which has value 0, meaning that `B` is the same as `AAAB`.
|
||||
|
||||
### String Literal
|
||||
|
||||
```rust
|
||||
|
@ -468,19 +468,21 @@ impl<'a> TermParser<'a> {
|
||||
Ok((bind, init))
|
||||
},
|
||||
"",
|
||||
"when",
|
||||
"{",
|
||||
",",
|
||||
false,
|
||||
0,
|
||||
)?;
|
||||
let (bind, init): (Vec<_>, Vec<_>) = args.into_iter().unzip();
|
||||
let bind = bind.into_iter().map(Some).collect::<Vec<_>>();
|
||||
self.skip_trivia();
|
||||
self.parse_keyword("when")?;
|
||||
let cond = self.parse_term()?;
|
||||
self.consume("{")?;
|
||||
self.consume(":")?;
|
||||
let step = self.parse_term()?;
|
||||
self.consume("}")?;
|
||||
self.consume("else")?;
|
||||
self.consume("{")?;
|
||||
self.skip_trivia();
|
||||
self.parse_keyword("else")?;
|
||||
self.consume(":")?;
|
||||
let base = self.parse_term()?;
|
||||
self.consume("}")?;
|
||||
return Ok(Term::Bend {
|
||||
@ -1049,7 +1051,7 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
||||
/// Joins the characters into a u24 and returns it.
|
||||
fn parse_quoted_symbol(&mut self) -> ParseResult<u32> {
|
||||
self.consume_exactly("`")?;
|
||||
let mut result = u32::MAX;
|
||||
let mut result = 0;
|
||||
let mut count = 0;
|
||||
while count < 4 {
|
||||
if self.starts_with("`") {
|
||||
|
@ -17,49 +17,70 @@ impl Definition {
|
||||
impl Stmt {
|
||||
fn gen_map_get(&mut self, id: &mut usize) {
|
||||
match self {
|
||||
Stmt::Assign { val, nxt, .. } | Stmt::Ask { val, nxt, .. } => {
|
||||
Stmt::Assign { pat: _, val, nxt } => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Stmt::Ask { pat: _, val, nxt } => {
|
||||
nxt.gen_map_get(id);
|
||||
let substitutions = val.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::InPlace { val, nxt, .. } => {
|
||||
Stmt::InPlace { op: _, var: _, val, nxt } => {
|
||||
nxt.gen_map_get(id);
|
||||
let substitutions = val.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::If { cond, then, otherwise } => {
|
||||
Stmt::If { cond, then, otherwise, nxt } => {
|
||||
then.gen_map_get(id);
|
||||
otherwise.gen_map_get(id);
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
let substitutions = cond.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::Match { arg, arms, .. } | Stmt::Fold { arg, arms, .. } => {
|
||||
Stmt::Match { bind: _, arg, arms, nxt } | Stmt::Fold { bind: _, arg, arms, nxt } => {
|
||||
for arm in arms.iter_mut() {
|
||||
arm.rgt.gen_map_get(id);
|
||||
}
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
let substitutions = arg.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::Switch { arg, arms, .. } => {
|
||||
Stmt::Switch { bind: _, arg, arms, nxt } => {
|
||||
for arm in arms.iter_mut() {
|
||||
arm.gen_map_get(id);
|
||||
}
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
let substitutions = arg.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::Bend { bind: _, init, cond, step, base } => {
|
||||
Stmt::Bend { bind: _, init, cond, step, base, nxt } => {
|
||||
step.gen_map_get(id);
|
||||
base.gen_map_get(id);
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
let mut substitutions = cond.substitute_map_gets(id);
|
||||
for init in init {
|
||||
substitutions.extend(init.substitute_map_gets(id));
|
||||
@ -68,7 +89,12 @@ impl Stmt {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::Do { bod, .. } => bod.gen_map_get(id),
|
||||
Stmt::Do { typ: _, bod, nxt } => {
|
||||
bod.gen_map_get(id);
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
}
|
||||
Stmt::Return { term } => {
|
||||
let substitutions = term.substitute_map_gets(id);
|
||||
if !substitutions.is_empty() {
|
||||
@ -149,7 +175,7 @@ fn gen_get(current: &mut Stmt, substitutions: Substitutions) -> Stmt {
|
||||
};
|
||||
let pat = AssignPattern::Tup(vec![AssignPattern::Var(var), AssignPattern::Var(map_var)]);
|
||||
|
||||
Stmt::Assign { pat, val: Box::new(map_get_call), nxt: Box::new(acc) }
|
||||
Stmt::Assign { pat, val: Box::new(map_get_call), nxt: Some(Box::new(acc)) }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -73,13 +73,13 @@ pub enum InPlaceOp {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum Stmt {
|
||||
// {pat} = {val} ";" {nxt}
|
||||
// {pat} = {val} ";"? {nxt}
|
||||
Assign {
|
||||
pat: AssignPattern,
|
||||
val: Box<Expr>,
|
||||
nxt: Box<Stmt>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// {var} += {val} ";" {nxt}
|
||||
// {var} += {val} ";"? {nxt}
|
||||
InPlace {
|
||||
op: InPlaceOp,
|
||||
var: Name,
|
||||
@ -90,55 +90,71 @@ pub enum Stmt {
|
||||
// {then}
|
||||
// "else" ":"
|
||||
// {otherwise}
|
||||
// <nxt>?
|
||||
If {
|
||||
cond: Box<Expr>,
|
||||
then: Box<Stmt>,
|
||||
otherwise: Box<Stmt>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// "match" {arg} ":" ("as" {bind})?
|
||||
// case {lft} ":" {rgt}
|
||||
// ...
|
||||
// <nxt>?
|
||||
Match {
|
||||
arg: Box<Expr>,
|
||||
bind: Option<Name>,
|
||||
arms: Vec<MatchArm>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// "switch" {arg} ("as" {bind})?
|
||||
// case 0..wildcard ":" {rgt}
|
||||
// ...
|
||||
// <nxt>?
|
||||
Switch {
|
||||
arg: Box<Expr>,
|
||||
bind: Option<Name>,
|
||||
arms: Vec<Stmt>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// "bend" ({bind} ("="" {init})?)* "while" {cond} ":"
|
||||
// {step}
|
||||
// {step}
|
||||
// "then" ":"
|
||||
// {base}
|
||||
// {base}
|
||||
// <nxt>?
|
||||
Bend {
|
||||
bind: Vec<Option<Name>>,
|
||||
init: Vec<Expr>,
|
||||
cond: Box<Expr>,
|
||||
step: Box<Stmt>,
|
||||
base: Box<Stmt>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// "fold" {arg} ("as" {bind})? ":" {arms}
|
||||
// case {lft} ":" {rgt}
|
||||
// ...
|
||||
// <nxt>?
|
||||
Fold {
|
||||
arg: Box<Expr>,
|
||||
bind: Option<Name>,
|
||||
arms: Vec<MatchArm>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// "do" {fun} ":" {block}
|
||||
// "do" {fun} ":"
|
||||
// {block}
|
||||
// <nxt>?
|
||||
Do {
|
||||
typ: Name,
|
||||
bod: Box<Stmt>,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
},
|
||||
// {pat} <- {val} ";" {nxt}
|
||||
// {pat} <- {val} ";"? {nxt}
|
||||
Ask {
|
||||
pat: AssignPattern,
|
||||
val: Box<Expr>,
|
||||
nxt: Box<Stmt>,
|
||||
},
|
||||
// "return" {expr} ";"
|
||||
// "return" {expr} ";"?
|
||||
Return {
|
||||
term: Box<Expr>,
|
||||
},
|
||||
|
@ -15,7 +15,13 @@ impl Definition {
|
||||
impl Stmt {
|
||||
fn order_kwargs(&mut self, book: &Book) -> Result<(), String> {
|
||||
match self {
|
||||
Stmt::Assign { val, nxt, .. } | Stmt::Ask { val, nxt, .. } => {
|
||||
Stmt::Assign { val, nxt, .. } => {
|
||||
val.order_kwargs(book)?;
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Ask { val, nxt, .. } => {
|
||||
val.order_kwargs(book)?;
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
@ -23,38 +29,58 @@ impl Stmt {
|
||||
val.order_kwargs(book)?;
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
Stmt::If { cond, then, otherwise } => {
|
||||
Stmt::If { cond, then, otherwise, nxt } => {
|
||||
cond.order_kwargs(book)?;
|
||||
then.order_kwargs(book)?;
|
||||
otherwise.order_kwargs(book)?;
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Match { arg, arms, .. } => {
|
||||
Stmt::Match { arg, arms, nxt, .. } => {
|
||||
arg.order_kwargs(book)?;
|
||||
for arm in arms {
|
||||
arm.rgt.order_kwargs(book)?;
|
||||
}
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Switch { arg, arms, .. } => {
|
||||
Stmt::Switch { arg, arms, nxt, .. } => {
|
||||
arg.order_kwargs(book)?;
|
||||
for arm in arms {
|
||||
arm.order_kwargs(book)?;
|
||||
}
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Fold { arg, arms, .. } => {
|
||||
Stmt::Fold { arg, arms, nxt, .. } => {
|
||||
arg.order_kwargs(book)?;
|
||||
for arm in arms {
|
||||
arm.rgt.order_kwargs(book)?;
|
||||
}
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Bend { bind: _, init, cond, step, base } => {
|
||||
Stmt::Bend { bind: _, init, cond, step, base, nxt } => {
|
||||
for init in init {
|
||||
init.order_kwargs(book)?;
|
||||
}
|
||||
cond.order_kwargs(book)?;
|
||||
step.order_kwargs(book)?;
|
||||
base.order_kwargs(book)?;
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Do { typ: _, bod, nxt } => {
|
||||
bod.order_kwargs(book)?;
|
||||
if let Some(nxt) = nxt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Do { bod, .. } => bod.order_kwargs(book)?,
|
||||
Stmt::Return { term } => term.order_kwargs(book)?,
|
||||
Stmt::Err => {}
|
||||
}
|
||||
|
@ -390,10 +390,15 @@ impl<'a> PyParser<'a> {
|
||||
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::Assign { 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::Assign { pat, val: Box::new(val), nxt: Some(Box::new(nxt)) };
|
||||
return Ok((stmt, nxt_indent));
|
||||
} else {
|
||||
let stmt = Stmt::Assign { pat, val: Box::new(val), nxt: None };
|
||||
return Ok((stmt, nxt_indent));
|
||||
}
|
||||
}
|
||||
// Ask
|
||||
if self.starts_with("<-") {
|
||||
@ -480,9 +485,20 @@ impl<'a> PyParser<'a> {
|
||||
self.consume_indent_exactly(*indent)?;
|
||||
let (otherwise, nxt_indent) = self.parse_statement(indent)?;
|
||||
indent.exit_level();
|
||||
|
||||
let stmt = Stmt::If { cond: Box::new(cond), then: Box::new(then), otherwise: Box::new(otherwise) };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::If {
|
||||
cond: Box::new(cond),
|
||||
then: Box::new(then),
|
||||
otherwise: Box::new(otherwise),
|
||||
nxt: Some(Box::new(nxt)),
|
||||
};
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt =
|
||||
Stmt::If { cond: Box::new(cond), then: Box::new(then), otherwise: Box::new(otherwise), nxt: None };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_match(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
|
||||
@ -501,8 +517,14 @@ impl<'a> PyParser<'a> {
|
||||
arms.push(case);
|
||||
}
|
||||
indent.exit_level();
|
||||
let stmt = Stmt::Match { arg: Box::new(arg), bind, arms };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Match { arg: Box::new(arg), bind, arms, nxt: Some(Box::new(nxt)) };
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt = Stmt::Match { arg: Box::new(arg), bind, arms, nxt: None };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_match_arg(&mut self) -> ParseResult<(Option<Name>, Expr)> {
|
||||
@ -586,8 +608,14 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
}
|
||||
indent.exit_level();
|
||||
let stmt = Stmt::Switch { arg: Box::new(arg), bind, arms };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Switch { arg: Box::new(arg), bind, arms, nxt: Some(Box::new(nxt)) };
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt = Stmt::Switch { arg: Box::new(arg), bind, arms, nxt: None };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_switch_case(&mut self, indent: &mut Indent) -> ParseResult<(Option<u32>, Stmt, Indent)> {
|
||||
@ -637,8 +665,14 @@ impl<'a> PyParser<'a> {
|
||||
arms.push(case);
|
||||
}
|
||||
indent.exit_level();
|
||||
let stmt = Stmt::Fold { arg: Box::new(arg), bind, arms };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Fold { arg: Box::new(arg), bind, arms, nxt: Some(Box::new(nxt)) };
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt = Stmt::Fold { arg: Box::new(arg), bind, arms, nxt: None };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
/// "bend" (<bind> "=" <init> ","?)* ":"
|
||||
@ -678,12 +712,33 @@ impl<'a> PyParser<'a> {
|
||||
indent.exit_level();
|
||||
|
||||
indent.exit_level();
|
||||
let stmt = Stmt::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Bend {
|
||||
bind,
|
||||
init,
|
||||
cond: Box::new(cond),
|
||||
step: Box::new(step),
|
||||
base: Box::new(base),
|
||||
nxt: Some(Box::new(nxt)),
|
||||
};
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt = Stmt::Bend {
|
||||
bind,
|
||||
init,
|
||||
cond: Box::new(cond),
|
||||
step: Box::new(step),
|
||||
base: Box::new(base),
|
||||
nxt: None,
|
||||
};
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
/// "do" <typ> ":"
|
||||
/// <bod>
|
||||
/// <nxt>?
|
||||
fn parse_do(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
|
||||
self.skip_trivia_inline();
|
||||
let typ = self.parse_bend_name()?;
|
||||
@ -696,8 +751,14 @@ impl<'a> PyParser<'a> {
|
||||
let (bod, nxt_indent) = self.parse_statement(indent)?;
|
||||
indent.exit_level();
|
||||
|
||||
let stmt = Stmt::Do { typ, bod: Box::new(bod) };
|
||||
Ok((stmt, nxt_indent))
|
||||
if nxt_indent == *indent {
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Do { typ, bod: Box::new(bod), nxt: Some(Box::new(nxt)) };
|
||||
Ok((stmt, nxt_indent))
|
||||
} else {
|
||||
let stmt = Stmt::Do { typ, bod: Box::new(bod), nxt: None };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
}
|
||||
|
||||
/// "("<nam> ("," <nam>)* ")"
|
||||
@ -828,7 +889,7 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
def.order_kwargs(book)?;
|
||||
def.gen_map_get();
|
||||
let def = def.to_fun();
|
||||
let def = def.to_fun()?;
|
||||
book.defs.insert(def.name.clone(), def);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2,101 +2,271 @@ use super::{AssignPattern, Definition, Expr, Stmt};
|
||||
use crate::fun::{self, Name};
|
||||
|
||||
impl Definition {
|
||||
pub fn to_fun(self) -> fun::Definition {
|
||||
let rule = fun::Rule {
|
||||
pats: self.params.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(),
|
||||
body: self.body.to_fun(),
|
||||
pub fn to_fun(self) -> Result<fun::Definition, String> {
|
||||
let body = self.body.into_fun().map_err(|e| format!("In function '{}': {}", self.name, e))?;
|
||||
let body = match body {
|
||||
StmtToFun::Return(term) => term,
|
||||
StmtToFun::Assign(..) => {
|
||||
return Err(format!("Function '{}' doesn't end with a return statement", self.name));
|
||||
}
|
||||
};
|
||||
|
||||
fun::Definition { name: self.name, rules: vec![rule], builtin: false }
|
||||
let rule =
|
||||
fun::Rule { pats: self.params.into_iter().map(|param| fun::Pattern::Var(Some(param))).collect(), body };
|
||||
|
||||
let def = fun::Definition { name: self.name, rules: vec![rule], builtin: false };
|
||||
Ok(def)
|
||||
}
|
||||
}
|
||||
|
||||
impl AssignPattern {
|
||||
pub fn to_fun(self) -> fun::Pattern {
|
||||
pub fn into_fun(self) -> fun::Pattern {
|
||||
match self {
|
||||
AssignPattern::Var(name) => fun::Pattern::Var(Some(name)),
|
||||
AssignPattern::Chn(name) => fun::Pattern::Chn(name),
|
||||
AssignPattern::Tup(names) => {
|
||||
fun::Pattern::Fan(fun::FanKind::Tup, fun::Tag::Static, names.into_iter().map(Self::to_fun).collect())
|
||||
}
|
||||
AssignPattern::Tup(names) => fun::Pattern::Fan(
|
||||
fun::FanKind::Tup,
|
||||
fun::Tag::Static,
|
||||
names.into_iter().map(Self::into_fun).collect(),
|
||||
),
|
||||
AssignPattern::Sup(names) => {
|
||||
fun::Pattern::Fan(fun::FanKind::Dup, fun::Tag::Auto, names.into_iter().map(Self::to_fun).collect())
|
||||
fun::Pattern::Fan(fun::FanKind::Dup, fun::Tag::Auto, names.into_iter().map(Self::into_fun).collect())
|
||||
}
|
||||
AssignPattern::MapSet(..) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum StmtToFun {
|
||||
Return(fun::Term),
|
||||
Assign(fun::Pattern, fun::Term),
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
pub fn to_fun(self) -> fun::Term {
|
||||
match self {
|
||||
Stmt::Assign { pat: AssignPattern::MapSet(map, key), val, nxt } => fun::Term::Let {
|
||||
pat: Box::new(fun::Pattern::Var(Some(map.clone()))),
|
||||
val: Box::new(fun::Term::call(fun::Term::Ref { nam: fun::Name::new("Map/set") }, [
|
||||
fun::Term::Var { nam: map },
|
||||
key.to_fun(),
|
||||
val.to_fun(),
|
||||
])),
|
||||
nxt: Box::new(nxt.to_fun()),
|
||||
},
|
||||
Stmt::Assign { pat, val, nxt } => {
|
||||
let pat = pat.to_fun();
|
||||
let val = val.to_fun();
|
||||
let nxt = nxt.to_fun();
|
||||
fun::Term::Let { pat: Box::new(pat), val: Box::new(val), nxt: Box::new(nxt) }
|
||||
fn into_fun(self) -> Result<StmtToFun, String> {
|
||||
// TODO: Refactor this to not repeat everything.
|
||||
// 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 term = fun::Term::Let {
|
||||
pat: Box::new(fun::Pattern::Var(Some(map.clone()))),
|
||||
val: Box::new(fun::Term::call(fun::Term::Ref { nam: fun::Name::new("Map/set") }, [
|
||||
fun::Term::Var { nam: map },
|
||||
key.to_fun(),
|
||||
val.to_fun(),
|
||||
])),
|
||||
nxt: Box::new(nxt),
|
||||
};
|
||||
if let Some(pat) = nxt_pat { StmtToFun::Assign(pat, term) } else { StmtToFun::Return(term) }
|
||||
}
|
||||
Stmt::InPlace { op, var: nam, val, nxt } => fun::Term::Let {
|
||||
pat: Box::new(fun::Pattern::Var(Some(nam.clone()))),
|
||||
val: Box::new(fun::Term::Oper {
|
||||
opr: op.to_lang_op(),
|
||||
fst: Box::new(fun::Term::Var { nam }),
|
||||
snd: Box::new(val.to_fun()),
|
||||
}),
|
||||
nxt: Box::new(nxt.to_fun()),
|
||||
},
|
||||
Stmt::If { cond, then, otherwise } => {
|
||||
let arms = vec![otherwise.to_fun(), then.to_fun()];
|
||||
fun::Term::Swt {
|
||||
Stmt::Assign { pat: AssignPattern::MapSet(..), val: _, nxt: None } => {
|
||||
return Err("Branch ends with map assignment.".to_string());
|
||||
}
|
||||
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 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) }
|
||||
}
|
||||
Stmt::Assign { pat, val, nxt: None } => {
|
||||
let pat = pat.into_fun();
|
||||
let val = val.to_fun();
|
||||
StmtToFun::Assign(pat, val)
|
||||
}
|
||||
Stmt::InPlace { op, var, 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) }
|
||||
}
|
||||
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),
|
||||
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
|
||||
return Err("'if' branches end with different assignments.".to_string());
|
||||
}
|
||||
(StmtToFun::Return(..), StmtToFun::Assign(..)) => {
|
||||
return Err(
|
||||
"Expected 'else' branch from 'if' to return, but it ends with assignment.".to_string(),
|
||||
);
|
||||
}
|
||||
(StmtToFun::Assign(..), StmtToFun::Return(..)) => {
|
||||
return Err(
|
||||
"Expected 'else' branch from 'if' to end with assignment, but it returns.".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let arms = vec![else_, then];
|
||||
let term = fun::Term::Swt {
|
||||
arg: Box::new(cond.to_fun()),
|
||||
bnd: Some(Name::new("%pred")),
|
||||
with: Vec::new(),
|
||||
pred: Some(Name::new("%pred-1")),
|
||||
arms,
|
||||
};
|
||||
wrap_nxt_assign_stmt(term, nxt, pat)?
|
||||
}
|
||||
Stmt::Match { arg, bind, 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),
|
||||
};
|
||||
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),
|
||||
};
|
||||
match (&arm_pat, &fst_pat) {
|
||||
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
|
||||
return Err("'match' arms end with different assignments.".to_string());
|
||||
}
|
||||
(Some(_), None) => {
|
||||
return Err("Expected 'match' arms to end with assignment, but it returns.".to_string());
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
return Err("Expected 'match' arms to return, but it ends with assignment.".to_string());
|
||||
}
|
||||
(Some(_), Some(_)) => fun_arms.push((arm.lft, vec![], arm_rgt)),
|
||||
(None, None) => fun_arms.push((arm.lft, vec![], arm_rgt)),
|
||||
}
|
||||
}
|
||||
let term = fun::Term::Mat { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms: fun_arms };
|
||||
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
|
||||
}
|
||||
Stmt::Match { arg, bind, arms } => {
|
||||
Stmt::Switch { arg, bind, arms, nxt } => {
|
||||
let arg = arg.to_fun();
|
||||
let arms = arms.into_iter().map(|arm| (arm.lft, Vec::new(), arm.rgt.to_fun())).collect();
|
||||
fun::Term::Mat { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms }
|
||||
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),
|
||||
};
|
||||
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),
|
||||
};
|
||||
match (&arm_pat, &fst_pat) {
|
||||
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
|
||||
return Err("'switch' arms end with different assignments.".to_string());
|
||||
}
|
||||
(Some(_), None) => {
|
||||
return Err("Expected 'switch' arms to end with assignment, but it returns.".to_string());
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
return Err("Expected 'switch' arms to return, but it ends with assignment.".to_string());
|
||||
}
|
||||
(Some(_), Some(_)) => fun_arms.push(arm),
|
||||
(None, None) => fun_arms.push(arm),
|
||||
}
|
||||
}
|
||||
let pred = Some(Name::new(format!("{}-{}", bind.clone().unwrap(), fun_arms.len() - 1)));
|
||||
let term = fun::Term::Swt { arg: Box::new(arg), bnd: bind, with: Vec::new(), pred, arms: fun_arms };
|
||||
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
|
||||
}
|
||||
Stmt::Switch { arg, bind, arms } => {
|
||||
Stmt::Fold { arg, bind, arms, nxt } => {
|
||||
let arg = arg.to_fun();
|
||||
let pred = Some(Name::new(format!("{}-{}", bind.clone().unwrap(), arms.len() - 1)));
|
||||
let arms = arms.into_iter().map(Stmt::to_fun).collect();
|
||||
fun::Term::Swt { arg: Box::new(arg), bnd: bind, with: Vec::new(), pred, arms }
|
||||
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),
|
||||
};
|
||||
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),
|
||||
};
|
||||
match (&arm_pat, &fst_pat) {
|
||||
(Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat => {
|
||||
return Err("'fold' arms end with different assignments.".to_string());
|
||||
}
|
||||
(Some(_), None) => {
|
||||
return Err("Expected 'fold' arms to end with assignment, but it returns.".to_string());
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
return Err("Expected 'fold' arms to return, but it ends with assignment.".to_string());
|
||||
}
|
||||
(Some(_), Some(_)) => fun_arms.push((arm.lft, vec![], arm_rgt)),
|
||||
(None, None) => fun_arms.push((arm.lft, vec![], arm_rgt)),
|
||||
}
|
||||
}
|
||||
let term = fun::Term::Fold { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms: fun_arms };
|
||||
wrap_nxt_assign_stmt(term, nxt, fst_pat)?
|
||||
}
|
||||
Stmt::Fold { arg, bind, arms } => {
|
||||
let arg = arg.to_fun();
|
||||
let arms = arms.into_iter().map(|arm| (arm.lft, Vec::new(), arm.rgt.to_fun())).collect();
|
||||
fun::Term::Fold { arg: Box::new(arg), bnd: bind, with: Vec::new(), arms }
|
||||
}
|
||||
Stmt::Bend { bind, init, cond, step, base } => {
|
||||
Stmt::Bend { bind, init, cond, step, base, nxt } => {
|
||||
let init = init.into_iter().map(Expr::to_fun).collect();
|
||||
let cond = cond.to_fun();
|
||||
let step = step.to_fun();
|
||||
let base = base.to_fun();
|
||||
fun::Term::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) }
|
||||
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),
|
||||
(StmtToFun::Assign(..), StmtToFun::Assign(..)) => {
|
||||
return Err("'bend' branches end with different assignments.".to_string());
|
||||
}
|
||||
(StmtToFun::Return(..), StmtToFun::Assign(..)) => {
|
||||
return Err(
|
||||
"Expected 'else' branch from 'bend' to return, but it ends with assignment.".to_string(),
|
||||
);
|
||||
}
|
||||
(StmtToFun::Assign(..), StmtToFun::Return(..)) => {
|
||||
return Err(
|
||||
"Expected 'else' branch from 'bend' to end with assignment, but it returns.".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let term =
|
||||
fun::Term::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) };
|
||||
wrap_nxt_assign_stmt(term, nxt, pat)?
|
||||
}
|
||||
Stmt::Do { typ, bod } => fun::Term::Do { typ, bod: Box::new(bod.to_fun()) },
|
||||
Self::Ask { pat, val, nxt } => fun::Term::Ask {
|
||||
pat: Box::new(pat.to_fun()),
|
||||
val: Box::new(val.to_fun()),
|
||||
nxt: Box::new(nxt.to_fun()),
|
||||
},
|
||||
Stmt::Return { term } => term.to_fun(),
|
||||
Stmt::Err => fun::Term::Err,
|
||||
}
|
||||
Stmt::Do { typ, bod, nxt } => {
|
||||
let (pat, bod) = match bod.into_fun()? {
|
||||
StmtToFun::Return(term) => (None, term),
|
||||
StmtToFun::Assign(pat, term) => (Some(pat), term),
|
||||
};
|
||||
let term = fun::Term::Do { typ, bod: Box::new(bod) };
|
||||
wrap_nxt_assign_stmt(term, nxt, pat)?
|
||||
}
|
||||
Self::Ask { 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::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) }
|
||||
}
|
||||
Stmt::Return { term } => StmtToFun::Return(term.to_fun()),
|
||||
Stmt::Err => unreachable!(),
|
||||
};
|
||||
Ok(stmt_to_fun)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,3 +322,28 @@ fn map_init(entries: Vec<(Expr, Expr)>) -> fun::Term {
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
/// If the statement was a return, returns it, erroring if there is another after it.
|
||||
/// Otherwise, turns it into a 'let' and returns the next statement.
|
||||
fn wrap_nxt_assign_stmt(
|
||||
term: fun::Term,
|
||||
nxt: Option<Box<Stmt>>,
|
||||
pat: Option<fun::Pattern>,
|
||||
) -> 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)) } else { Ok(StmtToFun::Return(term)) }
|
||||
} 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))
|
||||
} else {
|
||||
Ok(StmtToFun::Return(term))
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,10 @@ main =
|
||||
let b = 2;
|
||||
let c = 3;
|
||||
let n = 0;
|
||||
let tree = bend n when (< n depth) {
|
||||
let tree = bend n{
|
||||
when (< n depth):
|
||||
(Tree/node (go (+ n 1)) (go (+ n 1)))
|
||||
} else {
|
||||
else:
|
||||
(Tree/leaf c)
|
||||
}
|
||||
fold tree with a {
|
||||
|
26
tests/golden_tests/run_file/branch_statements_assignment.hvm
Normal file
26
tests/golden_tests/run_file/branch_statements_assignment.hvm
Normal file
@ -0,0 +1,26 @@
|
||||
// Test that branching statements can end with an assignment
|
||||
type Tree:
|
||||
node { val, ~lft, ~rgt }
|
||||
leaf
|
||||
|
||||
def main:
|
||||
deep = 0
|
||||
|
||||
if deep:
|
||||
n = 20
|
||||
else:
|
||||
n = 2
|
||||
|
||||
bend x = 1, h = 0:
|
||||
when h < n:
|
||||
y = Tree/node { val: x, lft: go(2 * x, h + 1), rgt: go(2 * x + 1, h + 1) }
|
||||
else:
|
||||
y = Tree/leaf
|
||||
|
||||
fold y:
|
||||
case Tree/node:
|
||||
z = y.val + y.lft + y.rgt
|
||||
case Tree/leaf:
|
||||
z = 0
|
||||
|
||||
return z
|
@ -17,9 +17,10 @@ def tree_xor(tree):
|
||||
|
||||
main =
|
||||
let depth = 10
|
||||
let tree = bend n=0 when (< n depth) {
|
||||
(Tree/node (go (+ n 1)) (go (+ n 1)))
|
||||
} else {
|
||||
if (% n 2) { (Tree/leaf Bool/True) } else { (Tree/leaf Bool/False) }
|
||||
let tree = bend n = 0 {
|
||||
when (< n depth):
|
||||
(Tree/node (go (+ n 1)) (go (+ n 1)))
|
||||
else:
|
||||
if (% n 2) { (Tree/leaf Bool/True) } else { (Tree/leaf Bool/False) }
|
||||
}
|
||||
(tree_xor tree)
|
||||
|
@ -2,7 +2,7 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/parse_file/imp_program.hvm
|
||||
---
|
||||
(symbols) = let x = (Map/set (Map/set Map/empty 4294967281 5) 2 3); let x = (Map/set x 4294967281 2); let x = (Map/set x 2 3); let (map/get%0, x) = (Map/get x 4294967281); (+ map/get%0 4286483570)
|
||||
(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)
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/branch_statements_assignment.hvm
|
||||
---
|
||||
6
|
Loading…
Reference in New Issue
Block a user