Merge pull request #546 from HigherOrderCO/528-add-syntax-for-a-builtin-tree-type

528 Add syntax for a builtin Tree type
This commit is contained in:
Nicolas Abril 2024-06-05 16:02:05 +00:00 committed by GitHub
commit c97e47107d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 469 additions and 302 deletions

11
CHANGELOG.md Normal file
View File

@ -0,0 +1,11 @@
# Changelog
## 0.2.32
- Added the built-in `Tree` datatype.
- Added `![]` and `!` syntax for `Tree` literals.
- Moved the builtins documentation to `/docs`.
- Created the changelog.
## 0.2.0
- Initial public release of Bend.

2
Cargo.lock generated
View File

@ -62,7 +62,7 @@ dependencies = [
[[package]]
name = "bend-lang"
version = "0.2.31"
version = "0.2.32"
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.31"
version = "0.2.32"
edition = "2021"
rust-version = "1.74"
exclude = ["tests/"]

View File

@ -414,7 +414,7 @@ Other features are described in the following documentation files:
- 📗 Data types: [Defining data types](docs/defining-data-types.md)
- 📗 Pattern matching: [Pattern matching](docs/pattern-matching.md)
- 📗 Native numbers and operations: [Native numbers](docs/native-numbers.md)
- 📗 Builtin definitions: _Documentation coming soon_
- 📗 Builtin definitions: [Builtins](docs/builtins.md)
- 📗 CLI arguments: [CLI arguments](docs/cli-arguments.md)
- 📙 Duplications and superpositions: [Dups and sups](docs/dups-and-sups.md)
- 📙 Scopeless lambdas: [Using scopeless lambdas](docs/using-scopeless-lambdas.md)

View File

@ -309,8 +309,8 @@ Let's start by implementing a recursive datatype in Bend:
```python
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
Node { ~left, ~right }
Leaf { value }
```
This defines a binary tree, with elements on leaves. Here, `~` flags a field as
@ -326,18 +326,18 @@ Could be represented as:
```
tree = Tree/Node {
lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 } },
rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 } }
lft: Tree/Node { left: Tree/Leaf { val: 1 }, right: Tree/Leaf { val: 2 } },
rgt: Tree/Node { left: Tree/Leaf { val: 3 }, right: Tree/Leaf { val: 4 } }
}
```
That's ugly. Very soon, we'll add a syntax sugar to make this shorter:
Binary trees are so useful in Bend that this type is already pre-defined in the
language and has its own dedicated syntax:
```py
# ![a, b] => Equivalent to Tree/Node { left: a, right: b }
# !x => Equivalent to Tree/Leaf { value: x }
tree = ![![!1, !2],![!3, !4]]
```
tree = ![![1,2],![3,4]]
```
As usual, for now, we'll live with the longer version.
### Fold: consuming recursive datatypes
@ -348,27 +348,20 @@ and replace* for datatypes. For example, consider the code below:
```python
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def sum(tree):
fold tree:
case Tree/Node:
return tree.lft + tree.rgt
return tree.left + tree.right
case Tree/Leaf:
return tree.val
return tree.value
def main:
tree = Tree/Node {
lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 } },
rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 } }
}
tree = ![![!1, !2],![!3, !4]]
return sum(tree)
```
It accomplishes the task by replacing every `Tree/Node { lft, rgt }` by `lft +
rgt`, and replacing every `Tree/Leaf` by `val`. As a result, the entire "tree of
It accomplishes the task by replacing every `Tree/Node { left, right }` by `left +
right`, and replacing every `Tree/Leaf` by `value`. As a result, the entire "tree of
values" is turned into a "tree of additions", and it evaluates as follows:
```python
@ -384,26 +377,16 @@ implemented with a fold*. So, we can do much more than just compute an
into a tuple of `(index,value)`, returning a new tree. Here's how to do it:
```python
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def enum(tree):
idx = 0
fold tree with idx:
case Tree/Node:
return Tree/Node {
lft: tree.lft(idx * 2 + 0),
rgt: tree.rgt(idx * 2 + 1),
}
return ![tree.left(idx * 2 + 0), tree.right(idx * 2 + 1)]
case Tree/Leaf:
return Tree/Leaf { val: (idx, tree.val) }
return !(idx, tree.value)
def main:
tree = Tree/Node {
lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 }, },
rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 }, }
}
tree = ![![!1, !2],![!3, !4]]
return enum(tree)
```
@ -440,16 +423,12 @@ can "grow" a recursive structure, layer by layer, until the condition is met.
For example, consider the code below:
```python
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def main():
bend x = 0:
when x < 3:
tree = Tree/Node { lft: fork(x + 1), rgt: fork(x + 1) }
tree = ![fork(x + 1), fork(x + 1)]
else:
tree = Tree/Leaf { val: 7 }
tree = !7
return tree
```
@ -463,7 +442,7 @@ tree = fork(0)
tree = ![fork(1), fork(1)]
tree = ![![fork(2),fork(2)], ![fork(2),fork(2)]]
tree = ![![![fork(3),fork(3)], ![fork(3),fork(3)]], ![![fork(3),fork(3)], ![fork(3),fork(3)]]]
tree = ![![![7,7], ![7,7]], ![![7,7], ![7,7]]]
tree = ![![![!7, !7], ![!7, !7]], ![![!7, !7], ![!7, !7]]]
```
With some imagination, we can easily see that, by recursively unrolling a state

View File

@ -19,8 +19,6 @@ A String literal is surrounded with `"`. Accepts the same values as characters l
```
## List
```python
type List = (Cons head ~tail) | (Nil)
@ -37,46 +35,64 @@ A List of values can be written using `[ ]`, it can have multiple values inside,
```
## Nat
## Tree
```python
type Nat = (Succ ~pred) | (Zero)
```
- **Succ ~pred**: Represents a natural number successor.
- **Zero**: Represents the natural number zero.
### Syntax
A Natural Number can be written with literals with a `#` before the literal number.
```
#1337
type Tree:
Node { ~left, ~right }
Leaf { value }
```
**`Tree`** represents a tree with values stored in the leaves.
Trees are a structure that naturally lends itself to parallel recursion, so writing your problem in terms of trees is a good first approach to parallelize your code.
- **Node { ~left ~right }**: Represents a tree node with `left` and `right` subtrees.
- **Leaf { value }**: Represents one of the ends of the tree, storing `value`.
## Result
```python
type Result = (Ok val) | (Err val)
#### Syntax
**Bend** provides the `![]` operator to create tree branches and the `!` operator to create a tree leaf.
```py
# ![a, b] => Equivalent to Tree/Node { left: a, right: b }
# !x => Equivalent to Tree/Leaf { value: x }
tree = ![![!1, !2],![!3, !4]]
```
- **Ok val**: Represents a successful result with value `val`.
- **Err val**: Represents an error with value `val`.
Technically your trees don't need to end with leaves, but if you don't, your program will be very hard to reason about.
## Map
```python
type Map = (Node value ~left ~right) | (Leaf)
type Map:
Node { value ~left ~right }
Leaf
```
- **Node value ~left ~right**: Represents a map node with a `value` and `left` and `right` subtrees.
- **Leaf**: Represents an empty map.
**`Map`** represents a tree with values stored in the branches.
It is meant to be used as an efficient map data structure with integer keys and O(log n) read and write operations.
- **Node { value ~left ~right }**: Represents a map node with a `value` and `left` and `right` subtrees. Empty nodes have `*` stored in the `value` field.
- **Leaf**: Represents an unwritten, empty portion of the map.
#### Syntax
**Bend** has a built-in binary tree map data structure where the key is a `u24`, meaning you can use numbers, characters, and symbols as keys.
Here's how you create a new `Map` with some initial values.:
```python
{ 0: 4, `hi`: "bye", 'c': 2 + 3 }
```
The keys must be `U24` numbers, and can be given as literals or any other expression that evaluates to a `U24`.
The values can be anything, but storing data of different types in a `Map` will make it harder for you to reason about it.
You can read and write a value of a map with the `[]` operator:
```python
map = { 0: "zero", 1: "one", 2: "two", 3: "three" }
map[0] = "not zero"
map[1] = 2
map[2] = 3
map[3] = map[1] + map[map[1]]
```
Here, `map` must be the name of the `Map` variable, and the keys inside `[]` can be any expression that evaluates to a `U24`.
## Map functions
### Map/empty
Initializes an empty map.
@ -86,6 +102,7 @@ Map/empty = Map/Leaf
### Map/get
Retrieves a `value` from the `map` based on the `key`.
Returns a tuple with the value and the `map` unchanged.
```rust
Map/get map key =
match map {
@ -105,9 +122,8 @@ Map/get map key =
}
```
#### Syntax
Considering the following tree
Considering the following map
```python
{ 0: "hello", 1: "bye", 2: "maybe", 3: "yes"}
```
@ -122,6 +138,7 @@ And the value resultant from the get function would be:
### Map/set
Sets a `value` in the `map` at the specified `key`.
Returns the map with the new value.
```rust
Map/set map key value =
match map {
@ -149,22 +166,63 @@ Considering the following tree
{ 0: "hello", 1: "bye", 2: "maybe", 3: "yes"}
```
The `set` function can be written as
```
```py
x[0] = "swapped" # Assigns the key 0 to the value "swapped"
```
And the value resultant from the get function would be:
```
```py
{ 0: "swapped", 1: "bye", 2: "maybe", 3: "yes"}
```
If there's no matching `key` in the tree, it would add a new branch to that tree with the value `set`
```
```py
x[4] = "added" # Assigns the key 4 to the value "added"
```
The new tree
```
```py
{ 0: "swapped", 1: "bye", 2: "maybe", 3: "yes", 4: "added"}
```
### Map/map
Applies a function to a value in the map.
Returns the map with the value mapped.
```rust
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)
}
```
#### Syntax
With the same map that we `set` in the previous section, we can map it's values with `@=`:
```py
x[0] @= lambda y: String/concat(y, " and mapped")
# x[0] now contains "swapped and mapped"
```
## Nat
```python
type Nat = (Succ ~pred) | (Zero)
```
- **Succ ~pred**: Represents a natural number successor.
- **Zero**: Represents the natural number zero.
### Syntax
A Natural Number can be written with literals with a `#` before the literal number.
```
#1337
```
## IO
IO Functions are in the **next milestone**!

View File

@ -11,7 +11,7 @@ Both syntaxes can be mixed in the same file like the example below:
```python
object Point { x, y }
type Tree = (Node ~left ~right) | (Leaf value)
type MyTree = (Node ~left ~right) | (Leaf value)
type Bool:
True
@ -622,6 +622,18 @@ A List literal is surrounded by `[` `]`. The elements must be separated by `,`.
It is desugared to constructor calls of the built-in type List, `List/cons(head, ~tail)` and `List/nil` .
### Tree Literals
```python
![![1, 2], ![3, 4]]
```
The Tree literals `![]` and `!` are used to create values of the built-in type `Tree`.
`![a b]` is equivalent to `Tree/Node(a, b)`.
`!x` is equivalent to `Tree/Leaf(x)`.
### Map Literals
```python
@ -1131,6 +1143,18 @@ The syntax above is desugared to:
Using `,` is optional.
### Tree Literals
```python
![![1, 2], ![3, 4]]
```
The Tree literals `![]` and `!` are used to create values of the built-in type `Tree`.
`![a b]` is equivalent to `Tree/Node(a, b)`.
`!x` is equivalent to `Tree/Leaf(x)`.
### Nat Literal
```rust

View File

@ -1,14 +1,14 @@
# Generates a tree with numbers in the nodes using 'bend'
type Tree:
type MyTree:
Node { val, ~left, ~right }
Leaf
def tree_gen(n, x):
bend n, x:
when n != 0:
return Tree/Node(x, fork(n - 1, x*2+1), fork(n - 1, x*2+2))
return MyTree/Node(x, fork(n - 1, x*2+1), fork(n - 1, x*2+2))
else:
return Tree/Leaf
return MyTree/Leaf
def main:
depth = 4

View File

@ -3,10 +3,6 @@ type Bool:
True
False
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def and(a, b):
match a:
case Bool/True:
@ -17,16 +13,16 @@ def and(a, b):
def all(tree):
fold tree:
case Tree/Node:
return and(tree.lft, tree.rgt)
return and(tree.left, tree.right)
case Tree/Leaf:
return tree.val
return tree.value
def gen(n):
switch n:
case 0:
return Tree/Leaf(Bool/True)
case _:
return Tree/Node { lft: gen(n-1), rgt: gen(n-1) }
return Tree/Node { left: gen(n-1), right: gen(n-1) }
def main():
return all(gen(8))

View File

@ -1,32 +1,32 @@
type Tree = Leaf | (Node l m r)
type MyTree = Leaf | (Node lft val rgt)
# Parallel QuickSort
(Sort List/Nil) = Tree/Leaf
(Sort (List/Cons x xs)) =
((Part x xs) λmin λmax
(Sort List/Nil) = MyTree/Leaf
(Sort (List/Cons head tail)) =
((Part head tail) λmin λmax
let lft = (Sort min)
let rgt = (Sort max)
(Tree/Node lft x rgt))
(MyTree/Node lft head rgt))
# Partitions a list in two halves, less-than-p and greater-than-p
(Part p List/Nil) = λt (t List/Nil List/Nil)
(Part p (List/Cons x xs)) = (Push (> x p) x (Part p xs))
(Part p List/Nil) = λt (t List/Nil List/Nil)
(Part p (List/Cons head tail)) = (Push (> head p) head (Part p tail))
# Pushes a value to the first or second list of a pair
(Push 0 x pair) = (pair λmin λmax λp (p (List/Cons x min) max))
(Push _ x pair) = (pair λmin λmax λp (p min (List/Cons x max)))
# Generates a random list
(Rnd 0 s) = List/Nil
(Rnd n s) =
let s = (^ s (* s 0b10000000000000))
let s = (^ s (/ s 0b100000000000000000))
let s = (^ s (* s 0b100000))
(List/Cons s (Rnd (- n 1) s))
# Generates a random list with xorshift
(Rnd 0 state) = List/Nil
(Rnd n state) =
let state = (^ state (<< state 13))
let state = (^ state (>> state 17))
let state = (^ state (<< state 5))
(List/Cons state (Rnd (- n 1) state))
# Sums all elements in a concatenation tree
(Sum Tree/Leaf) = 0
(Sum (Tree/Node l m r)) = (+ m (+ (Sum l) (Sum r)))
(Sum MyTree/Leaf) = 0
(Sum (MyTree/Node lft val rgt)) = (+ val (+ (Sum lft) (Sum rgt)))
# Sorts and sums n random numbers
(Main) =

View File

@ -1,7 +1,14 @@
type String = (Nil) | (Cons head ~tail)
type List = (Nil) | (Cons head ~tail)
type Nat = (Succ ~pred) | (Zero)
type Tree:
Node { ~left, ~right }
Leaf { value }
# MAP Impl
type Map = (Node value ~left ~right) | (Leaf)

View File

@ -316,12 +316,10 @@ impl<'a> TermParser<'a> {
// Opr but maybe a tup
self.skip_trivia();
let starts_with_oper = self.peek_one().map_or(false, |c| "+-*/%&|<>^=!".contains(c));
if starts_with_oper {
let opr = self.parse_oper()?;
if let Some(opr) = self.try_parse_oper() {
self.skip_trivia();
// jk, actually a tuple
self.skip_trivia();
if self.starts_with(",") && opr == Op::MUL {
let mut els = vec![Term::Era];
while self.try_consume(",") {
@ -370,6 +368,26 @@ impl<'a> TermParser<'a> {
return Ok(Term::List { els });
}
// Tree Node
if self.starts_with("![") {
self.advance_one();
self.advance_one();
unexpected_tag(self)?;
let lft = self.parse_term()?;
self.try_consume(",");
let rgt = self.parse_term()?;
self.labelled(|p| p.consume("]"), "Only two children in a Tree/Node")?;
return Ok(Term::call(Term::r#ref("Tree/Node"), [lft, rgt]));
}
// Tree Leaf
if self.starts_with("!") {
self.advance_one();
unexpected_tag(self)?;
let val = self.parse_term()?;
return Ok(Term::app(Term::r#ref("Tree/Leaf"), val));
}
// Sup
if self.starts_with("{") {
let els = self.list_like(|p| p.parse_term(), "{", "}", ",", false, 2)?;
@ -1022,7 +1040,7 @@ pub trait ParserCommons<'a>: Parser<'a> {
Ok(els)
}
fn parse_oper(&mut self) -> ParseResult<Op> {
fn try_parse_oper(&mut self) -> Option<Op> {
let opr = if self.try_consume_exactly("+") {
Op::ADD
} else if self.try_consume_exactly("-") {
@ -1054,9 +1072,9 @@ pub trait ParserCommons<'a>: Parser<'a> {
} else if self.try_consume_exactly("^") {
Op::XOR
} else {
return self.expected("numeric operator");
return None;
};
Ok(opr)
Some(opr)
}
fn peek_oper(&mut self) -> Option<Op> {

View File

@ -190,6 +190,13 @@ impl Expr {
go(entry, substitutions, id);
}
}
Expr::TreeNode { left, right } => {
go(left, substitutions, id);
go(right, substitutions, id);
}
Expr::TreeLeaf { val } => {
go(val, substitutions, id);
}
Expr::Era | Expr::Str { .. } | Expr::Var { .. } | Expr::Chn { .. } | Expr::Num { .. } => {}
}
}

View File

@ -38,6 +38,10 @@ pub enum Expr {
Map { entries: Vec<(Expr, Expr)> },
// {map} "[" {key} "]"
MapGet { nam: Name, key: Box<Expr> },
// "![" {left} "," {right} "]"
TreeNode { left: Box<Expr>, right: Box<Expr> },
// "!" {val}
TreeLeaf { val: Box<Expr> },
}
#[derive(Clone, Debug)]

View File

@ -156,12 +156,17 @@ impl Expr {
entry.1.order_kwargs(book)?;
}
}
Expr::MapGet { .. }
| Expr::Era
| Expr::Var { .. }
| Expr::Chn { .. }
| Expr::Num { .. }
| Expr::Str { .. } => {}
Expr::MapGet { nam: _, key } => {
key.order_kwargs(book)?;
}
Expr::TreeNode { left, right } => {
left.order_kwargs(book)?;
right.order_kwargs(book)?;
}
Expr::TreeLeaf { val } => {
val.order_kwargs(book)?;
}
Expr::Era | Expr::Var { .. } | Expr::Chn { .. } | Expr::Num { .. } | Expr::Str { .. } => {}
}
Ok(())
}

View File

@ -83,87 +83,54 @@ impl<'a> PyParser<'a> {
} else {
self.skip_trivia();
}
let Some(head) = self.peek_one() else { return self.expected("expression")? };
let ini_idx = *self.index();
let base = match head {
let base = if self.starts_with("(") {
// Tuple or parenthesized expression
'(' => {
self.advance_one();
let head = self.parse_expr(false)?;
self.skip_trivia();
if self.starts_with(",") {
// A Tuple
let mut els = vec![head];
while self.try_consume(",") {
els.push(self.parse_expr(false)?);
}
self.consume(")")?;
Expr::Tup { els }
} else {
self.consume(")")?;
// A parenthesized expression
head
}
}
self.parse_tuple_or_parens()?
} else if self.starts_with("{") {
// Map or Sup
'{' => {
self.advance_one();
// Empty map
if self.try_consume("}") {
return Ok(Expr::Map { entries: vec![] });
}
let head = self.parse_expr(false)?;
self.skip_trivia();
if self.try_consume(",") {
self.parse_sup(head)?
} else if self.try_consume(":") {
self.parse_map_init(head)?
} else {
self.expected("',' or ':'")?
}
}
self.parse_map_or_sup()?
} else if self.starts_with("[") {
// List or Comprehension
'[' => self.parse_list_or_comprehension()?,
self.parse_list_or_comprehension()?
} else if self.starts_with("![") {
// Tree Node
self.parse_tree_node()?
} else if self.starts_with("!") {
// Tree Leaf
self.parse_tree_leaf(inline)?
} else if self.starts_with("`") {
// Symbol
'`' => Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) },
Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) }
} else if self.starts_with("\"") {
// String
'\"' => {
let str = self.parse_quoted_string()?;
let val = STRINGS.get(str);
Expr::Str { val }
}
Expr::Str { val: STRINGS.get(self.parse_quoted_string()?) }
} else if self.starts_with("'") {
// Char
'\'' => {
let chr = self.parse_quoted_char()?;
Expr::Num { val: Num::U24(chr as u32 & 0x00ff_ffff) }
}
Expr::Num { val: Num::U24(self.parse_quoted_char()? as u32 & 0x00ff_ffff) }
} else if self.starts_with("$") {
// Unscoped var
'$' => {
self.advance_one();
let nam = self.parse_bend_name()?;
Expr::Chn { nam }
}
self.advance_one();
Expr::Chn { nam: self.parse_bend_name()? }
} else if self.starts_with("*") {
// Era
'*' => {
self.advance_one();
Expr::Era
}
// Number
c if is_num_char(c) => {
let val = self.parse_number()?;
Expr::Num { val }
}
// var
_ => {
self.advance_one();
Expr::Era
} else if let Some(c) = self.peek_one() {
if is_num_char(c) {
// Number
Expr::Num { val: self.parse_number()? }
} else {
// Var
let nam = self.labelled(|p| p.parse_bend_name(), "expression")?;
Expr::Var { nam }
}
} else {
self.expected("expression")?
};
let end_idx = *self.index();
// postfixes
if inline {
self.skip_trivia_inline();
@ -202,12 +169,13 @@ impl<'a> PyParser<'a> {
// map get
if self.starts_with("[") {
self.advance_one();
if let Expr::Var { nam } = base {
self.advance_one();
let key = self.parse_expr(false)?;
self.consume("]")?;
return Ok(Expr::MapGet { nam, key: Box::new(key) });
} else {
let end_idx = *self.index();
return self.expected_spanned("Map variable name", ini_idx, end_idx);
}
}
@ -218,6 +186,7 @@ impl<'a> PyParser<'a> {
let kwargs = self.list_like(|p| p.data_kwarg(), "{", "}", ",", true, 0)?;
return Ok(Expr::Ctr { name: nam, args: Vec::new(), kwargs });
} else {
let end_idx = *self.index();
return self.expected_spanned("Constructor name", ini_idx, end_idx);
}
}
@ -226,6 +195,43 @@ impl<'a> PyParser<'a> {
Ok(base)
}
fn parse_tuple_or_parens(&mut self) -> ParseResult<Expr> {
self.advance_one();
let head = self.parse_expr(false)?;
self.skip_trivia();
let term = if self.starts_with(",") {
// A Tuple
let mut els = vec![head];
while self.try_consume(",") {
els.push(self.parse_expr(false)?);
}
self.consume(")")?;
Expr::Tup { els }
} else {
self.consume(")")?;
// A parenthesized expression
head
};
Ok(term)
}
fn parse_map_or_sup(&mut self) -> ParseResult<Expr> {
self.advance_one();
// Empty map
if self.try_consume("}") {
return Ok(Expr::Map { entries: vec![] });
}
let head = self.parse_expr(false)?;
self.skip_trivia();
if self.try_consume(",") {
self.parse_sup(head)
} else if self.try_consume(":") {
self.parse_map_init(head)
} else {
self.expected("',' or ':'")
}
}
fn parse_map_init(&mut self, head: Expr) -> ParseResult<Expr> {
let mut entries = Vec::new();
let val = self.parse_expr(false)?;
@ -246,6 +252,22 @@ impl<'a> PyParser<'a> {
Ok(Expr::Sup { els })
}
fn parse_tree_node(&mut self) -> ParseResult<Expr> {
self.advance_one();
self.advance_one();
let left = self.parse_expr(false)?;
self.consume(",")?;
let right = self.parse_expr(false)?;
self.consume("]")?;
Ok(Expr::TreeNode { left: Box::new(left), right: Box::new(right) })
}
fn parse_tree_leaf(&mut self, inline: bool) -> ParseResult<Expr> {
self.advance_one();
let val = self.parse_expr(inline)?;
Ok(Expr::TreeLeaf { val: Box::new(val) })
}
fn data_kwarg(&mut self) -> ParseResult<(Name, Expr)> {
self.skip_trivia();
let nam = self.parse_bend_name()?;
@ -366,7 +388,7 @@ impl<'a> PyParser<'a> {
}
while let Some(op) = self.peek_oper() {
if op.precedence() == prec {
self.parse_oper()?;
self.try_parse_oper().unwrap();
let rhs = self.parse_infix_expr(prec + 1, inline)?;
lhs = Expr::Opr { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
self.skip_trivia_inline();

View File

@ -448,6 +448,15 @@ impl Expr {
}
Expr::Map { entries } => map_init(entries),
Expr::MapGet { .. } => unreachable!(),
Expr::TreeNode { left, right } => {
let left = left.to_fun();
let right = right.to_fun();
fun::Term::call(fun::Term::r#ref("Tree/Node"), [left, right])
}
Expr::TreeLeaf { val } => {
let val = val.to_fun();
fun::Term::app(fun::Term::r#ref("Tree/Leaf"), val)
}
}
}
}

View File

@ -1,5 +1,3 @@
type Tree = (node lft rgt) | (leaf val)
tail_recursive = @x (x @pred @acc (tail_recursive pred (+ 1 acc)) @acc 0)
fold_ = @bm (bm
@ -60,23 +58,23 @@ List.reverse_over xs ys = match xs {
}
Tree.leaves tree = match tree {
Tree/leaf: 1
Tree/node: (+ (Tree.leaves tree.lft) (Tree.leaves tree.rgt))
Tree/Leaf: 1
Tree/Node: (+ (Tree.leaves tree.left) (Tree.leaves tree.right))
}
Tree.nodes tree = match tree {
Tree/leaf: 0
Tree/node: (+ 1 (+ (Tree.nodes tree.lft) (Tree.nodes tree.rgt)))
Tree/Leaf: 0
Tree/Node: (+ 1 (+ (Tree.nodes tree.left) (Tree.nodes tree.right)))
}
Tree.height tree = match tree {
Tree/leaf: 0
Tree/node: (+ 1 (max (Tree.height tree.lft) (Tree.height tree.rgt)))
Tree/Leaf: 0
Tree/Node: (+ 1 (max (Tree.height tree.left) (Tree.height tree.right)))
}
Tree.map tree f = match tree {
Tree/leaf: (Tree/leaf (f tree.val))
Tree/node: (Tree/node (Tree.map tree.lft f) (Tree.map tree.rgt f))
Tree/Leaf: (Tree/Leaf (f tree.value))
Tree/Node: (Tree/Node (Tree.map tree.left f) (Tree.map tree.right f))
}
max a b = switch (> a b) {
@ -85,8 +83,8 @@ max a b = switch (> a b) {
}
Tree.flip tree = match tree {
Tree/leaf: tree
Tree/node: (Tree/node (Tree.flip tree.rgt) (Tree.flip tree.lft))
Tree/Leaf: tree
Tree/Node: (Tree/Node (Tree.flip tree.right) (Tree.flip tree.left))
}
calc =

View File

@ -1,5 +1,4 @@
type Tree = (Leaf a) | (Both a b)
(Warp s (Tree/Leaf a) (Tree/Leaf b)) = 0
(Warp s (Tree/Both a b) (Tree/Both c d)) = 1
(Warp s (Tree/Node a b) (Tree/Node c d)) = 1
main = *

View File

@ -1,45 +1,44 @@
type Tree = (Leaf a) | (Both a b)
type Error = Err
# Atomic Swapper
(Swap n a b) = switch n {
0: (Tree/Both a b)
_: (Tree/Both b a)
0: (Tree/Node a b)
_: (Tree/Node b a)
}
# Swaps distant values in parallel; corresponds to a Red Box
(Warp s (Tree/Leaf a) (Tree/Leaf b)) = (Swap (^ (> a b) s) (Tree/Leaf a) (Tree/Leaf b))
(Warp s (Tree/Both a b) (Tree/Both c d)) = (Join (Warp s a c) (Warp s b d))
(Warp s (Tree/Node a b) (Tree/Node c d)) = (Join (Warp s a c) (Warp s b d))
(Warp s a b) = Error/Err
# Rebuilds the warped tree in the original order
(Join (Tree/Both a b) (Tree/Both c d)) = (Tree/Both (Tree/Both a c) (Tree/Both b d))
(Join (Tree/Node a b) (Tree/Node c d)) = (Tree/Node (Tree/Node a c) (Tree/Node b d))
(Join a b) = Error/Err
# Recursively warps each sub-tree; corresponds to a Blue/Green Box
(Flow s (Tree/Leaf a)) = (Tree/Leaf a)
(Flow s (Tree/Both a b)) = (Down s (Warp s a b))
(Flow s (Tree/Node a b)) = (Down s (Warp s a b))
# Propagates Flow downwards
(Down s (Tree/Leaf a)) = (Tree/Leaf a)
(Down s (Tree/Both a b)) = (Tree/Both (Flow s a) (Flow s b))
(Down s (Tree/Node a b)) = (Tree/Node (Flow s a) (Flow s b))
# Bitonic Sort
(Sort s (Tree/Leaf a)) = (Tree/Leaf a)
(Sort s (Tree/Both a b)) = (Flow s (Tree/Both (Sort 0 a) (Sort 1 b)))
(Sort s (Tree/Node a b)) = (Flow s (Tree/Node (Sort 0 a) (Sort 1 b)))
# Generates a tree of depth `n`
(Gen n x) = switch n {
0: (Tree/Leaf x)
_: (Tree/Both (Gen n-1 (* x 2)) (Gen n-1 (+ (* x 2) 1)))
_: (Tree/Node (Gen n-1 (* x 2)) (Gen n-1 (+ (* x 2) 1)))
}
# Reverses a tree
(Rev (Tree/Leaf x)) = (Tree/Leaf x)
(Rev (Tree/Both a b)) = (Tree/Both (Rev b) (Rev a))
(Rev (Tree/Node a b)) = (Tree/Node (Rev b) (Rev a))
# Sums a tree
(Sum (Tree/Leaf x)) = x
(Sum (Tree/Both a b)) = (+ (Sum a) (Sum b))
(Sum (Tree/Node a b)) = (+ (Sum a) (Sum b))
Main = (Sum (Sort 0 (Rev (Gen 4 0))))

View File

@ -1,5 +1,3 @@
type Tree = (Leaf x) | (Node x0 x1)
add = λa λb (+ a b)
gen = λn switch n {
@ -9,8 +7,8 @@ gen = λn switch n {
sum = λt
match t {
Tree/Leaf: t.x
Tree/Node: (add (sum t.x0) (sum t.x1))
Tree/Leaf: t.value
Tree/Node: (add (sum t.left) (sum t.right))
}
main = (sum (gen 8))

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
fun0 = ![!0 ![!1 ![!2, !3]]]
fun1 = !(![!*!*])
fun2 = !!!*
fun3 = !1
fun4 = @n switch n {
0: !0
_: ![(fun4 n-1) (fun4 n-1)]
}
def imp0:
return ![!0, ![!1, ![!2, !3]]]
def imp1:
return !(![!*,!*])
def imp2:
return !!!*
def imp3:
return !1
def imp4(n):
switch n:
case 0:
return !0
case _:
return ![imp4(n-1), imp4(n-1)]
main = *

View File

@ -1,6 +1,4 @@
# should return (a+b+c) * 2^depth
type Tree = (node ~lft ~rgt) | (leaf val)
main =
let depth = 2
let a = 1;
@ -9,11 +7,11 @@ main =
let n = 0;
let tree = bend n {
when (< n depth):
(Tree/node (fork (+ n 1)) (fork (+ n 1)))
![(fork (+ n 1)) (fork (+ n 1))]
else:
(Tree/leaf c)
!c
}
fold tree {
Tree/node: (+ tree.lft tree.rgt)
Tree/leaf: (+ tree.val (+ a b))
Tree/Node: (+ tree.left tree.right)
Tree/Leaf: (+ tree.value (+ a b))
}

View File

@ -1,45 +1,44 @@
type Tree = (Leaf a) | (Both a b)
type Error = Err
# Atomic Swapper
(Swap n a b) = switch n {
0: (Tree/Both a b)
_: (Tree/Both b a)
0: (Tree/Node a b)
_: (Tree/Node b a)
}
# Swaps distant values in parallel; corresponds to a Red Box
(Warp s (Tree/Leaf a) (Tree/Leaf b)) = (Swap (^ (> a b) s) (Tree/Leaf a) (Tree/Leaf b))
(Warp s (Tree/Both a b) (Tree/Both c d)) = (Join (Warp s a c) (Warp s b d))
(Warp s (Tree/Node a b) (Tree/Node c d)) = (Join (Warp s a c) (Warp s b d))
(Warp s a b) = Error/Err
# Rebuilds the warped tree in the original order
(Join (Tree/Both a b) (Tree/Both c d)) = (Tree/Both (Tree/Both a c) (Tree/Both b d))
(Join (Tree/Node a b) (Tree/Node c d)) = (Tree/Node (Tree/Node a c) (Tree/Node b d))
(Join a b) = Error/Err
# Recursively warps each sub-tree; corresponds to a Blue/Green Box
(Flow s (Tree/Leaf a)) = (Tree/Leaf a)
(Flow s (Tree/Both a b)) = (Down s (Warp s a b))
(Flow s (Tree/Node a b)) = (Down s (Warp s a b))
# Propagates Flow downwards
(Down s (Tree/Leaf a)) = (Tree/Leaf a)
(Down s (Tree/Both a b)) = (Tree/Both (Flow s a) (Flow s b))
(Down s (Tree/Node a b)) = (Tree/Node (Flow s a) (Flow s b))
# Bitonic Sort
(Sort s (Tree/Leaf a)) = (Tree/Leaf a)
(Sort s (Tree/Both a b)) = (Flow s (Tree/Both (Sort 0 a) (Sort 1 b)))
(Sort s (Tree/Node a b)) = (Flow s (Tree/Node (Sort 0 a) (Sort 1 b)))
# Generates a tree of depth `n`
(Gen n x) = switch n {
0: (Tree/Leaf x)
_: (Tree/Both (Gen n-1 (* x 2)) (Gen n-1 (+ (* x 2) 1)))
_: (Tree/Node (Gen n-1 (* x 2)) (Gen n-1 (+ (* x 2) 1)))
}
# Reverses a tree
(Rev (Tree/Leaf x)) = (Tree/Leaf x)
(Rev (Tree/Both a b)) = (Tree/Both (Rev b) (Rev a))
(Rev (Tree/Node a b)) = (Tree/Node (Rev b) (Rev a))
# Sums a tree
(Sum (Tree/Leaf x)) = x
(Sum (Tree/Both a b)) = (+ (Sum a) (Sum b))
(Sum (Tree/Node a b)) = (+ (Sum a) (Sum b))
Main = (Sum (Sort 0 (Rev (Gen 4 0))))

View File

@ -1,5 +1,5 @@
# Test that branching statements can end with an assignment
type Tree:
type MyTree:
node { val, ~lft, ~rgt }
leaf
@ -13,14 +13,14 @@ def main:
bend x = 1, h = 0:
when h < n:
y = Tree/node { val: x, lft: fork(2 * x, h + 1), rgt: fork(2 * x + 1, h + 1) }
y = MyTree/node { val: x, lft: fork(2 * x, h + 1), rgt: fork(2 * x + 1, h + 1) }
else:
y = Tree/leaf
y = MyTree/leaf
fold y:
case Tree/node:
case MyTree/node:
z = y.val + y.lft + y.rgt
case Tree/leaf:
case MyTree/leaf:
z = 0
return z

View File

@ -1,11 +1,7 @@
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def main():
bend x = 0:
when x < 3:
tree = Tree/Node { lft: fork(x + 1), rgt: fork(x + 1) }
tree = ![fork(x + 1), fork(x + 1)]
else:
tree = Tree/Leaf { val: 7 }
tree = !7
return tree

View File

@ -1,21 +1,11 @@
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def enum(tree):
idx = 0
fold tree with idx:
case Tree/Node:
return Tree/Node {
lft: tree.lft(idx * 2 + 0),
rgt: tree.rgt(idx * 2 + 1),
}
return ![tree.left(idx * 2 + 0), tree.right(idx * 2 + 1)]
case Tree/Leaf:
return Tree/Leaf { val: (idx, tree.val) }
return !(idx, tree.value)
def main:
tree = Tree/Node {
lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 }, },
rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 }, }
}
tree = ![![!1, !2], ![!3, !4]]
return enum(tree)

View File

@ -1,17 +1,10 @@
type Tree:
Node { ~lft, ~rgt }
Leaf { val }
def sum(tree):
fold tree:
case Tree/Node:
return tree.lft + tree.rgt
return tree.left + tree.right
case Tree/Leaf:
return tree.val
return tree.value
def main:
tree = Tree/Node {
lft: Tree/Node { lft: Tree/Leaf { val: 1 }, rgt: Tree/Leaf { val: 2 } },
rgt: Tree/Node { lft: Tree/Leaf { val: 3 }, rgt: Tree/Leaf { val: 4 } }
}
tree = ![![!1, !2], ![!3, !4]]
return sum(tree)

View File

@ -1,23 +1,20 @@
type Tree = (Leaf x) | (Node x0 x1)
type List_ = Nil | (Cons h t)
sort (Tree/Leaf v) = (List_/Cons v List_/Nil)
sort (Tree/Leaf v) = (List/Cons v List/Nil)
sort (Tree/Node a b) = (merge (sort a) (sort b))
merge (List_/Nil) b = b
merge (List_/Cons x xs) (List_/Nil) = (List_/Cons x xs)
merge (List_/Cons x xs) (List_/Cons y ys) =
merge (List/Nil) b = b
merge (List/Cons x xs) (List/Nil) = (List/Cons x xs)
merge (List/Cons x xs) (List/Cons y ys) =
let t = switch _ = (< x y) {
0: λaλbλcλt(t c a b)
_: λaλbλcλt(t a b c)
}
let t = (t (List_/Cons x) λx(x) (List_/Cons y))
let t = (t (List/Cons x) λx(x) (List/Cons y))
(t λa λb λc (a (merge (b xs) (c ys))))
sum List_/Nil = 0
sum (List_/Cons h t) = (+ h (sum t))
sum List/Nil = 0
sum (List/Cons h t) = (+ h (sum t))
range n = switch n {
0: λx (Tree/Leaf x)

View File

@ -2,13 +2,13 @@ type Bool:
True
False
type Tree = (node ~lft ~rgt) | (leaf val)
type MyTree = (node ~lft ~rgt) | (leaf val)
def tree_xor(tree):
fold tree:
case Tree/node:
case MyTree/node:
return xor(tree.lft, tree.rgt);
case Tree/leaf:
case MyTree/leaf:
return tree.val;
(xor Bool/True Bool/False) = Bool/True
@ -19,8 +19,8 @@ main =
let depth = 10
let tree = bend n = 0 {
when (< n depth):
(Tree/node (fork (+ n 1)) (fork (+ n 1)))
(MyTree/node (fork (+ n 1)) (fork (+ n 1)))
else:
if (% n 2) { (Tree/leaf Bool/True) } else { (Tree/leaf Bool/False) }
if (% n 2) { (MyTree/leaf Bool/True) } else { (MyTree/leaf Bool/False) }
}
(tree_xor tree)

View File

@ -1,5 +1,3 @@
type Tree = (Leaf x) | (Node x0 x1)
add = λa λb (+ a b)
gen = λn switch n {
@ -9,8 +7,8 @@ gen = λn switch n {
sum = λt
match t {
Tree/Leaf: t.x
Tree/Node: (add (sum t.x0) (sum t.x1))
Tree/Leaf: t.value
Tree/Node: (add (sum t.left) (sum t.right))
}
main = (sum (gen 8))

View File

@ -1,11 +1,11 @@
type Tree = (Node lt rt rd ld) | (Leaf val)
type Tree_ = (Node lt rt rd ld) | (Leaf val)
(map) =
λarg1 λarg2 use tree = arg2;
use f = arg1;
match tree with f {
Tree/Node: (Tree/Node (map f tree.lt) (map f tree.rt) (map f tree.rd) (map f tree.ld));
Tree/Leaf: (Tree/Leaf (f tree.val));
Tree_/Node: (Tree_/Node (map f tree.lt) (map f tree.rt) (map f tree.rd) (map f tree.ld));
Tree_/Leaf: (Tree_/Leaf (f tree.val));
}
main = map

View File

@ -88,12 +88,12 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
@Tree.flip = ((@Tree.flip__C2 a) a)
@Tree.flip__C0 = (c (a e))
& @Tree/node ~ (b (d e))
& @Tree/Node ~ (b (d e))
&!@Tree.flip ~ (a b)
&!@Tree.flip ~ (c d)
@Tree.flip__C1 = (* a)
& @Tree/leaf ~ a
& @Tree/Leaf ~ a
@Tree.flip__C2 = (?((@Tree.flip__C0 @Tree.flip__C1) a) a)
@ -118,12 +118,12 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
@Tree.map = ((@Tree.map__C2 a) a)
@Tree.map__C0 = (a (d ({b e} g)))
& @Tree/node ~ (c (f g))
& @Tree/Node ~ (c (f g))
&!@Tree.map ~ (a (b c))
&!@Tree.map ~ (d (e f))
@Tree.map__C1 = (* (a ((a b) c)))
& @Tree/leaf ~ (b c)
& @Tree/Leaf ~ (b c)
@Tree.map__C2 = (?((@Tree.map__C0 @Tree.map__C1) a) a)
@ -136,13 +136,13 @@ input_file: tests/golden_tests/compile_file/redex_order_recursive.bend
@Tree.nodes__C1 = (?((@Tree.nodes__C0 (* (* 0))) a) a)
@Tree/leaf = (a ((@Tree/leaf/tag (a b)) b))
@Tree/Leaf = (a ((@Tree/Leaf/tag (a b)) b))
@Tree/leaf/tag = 1
@Tree/Leaf/tag = 1
@Tree/node = (a (b ((@Tree/node/tag (a (b c))) c)))
@Tree/Node = (a (b ((@Tree/Node/tag (a (b c))) c)))
@Tree/node/tag = 0
@Tree/Node/tag = 0
@add = ((@add__C0 ((a a) b)) b)

View File

@ -4,4 +4,4 @@ input_file: tests/golden_tests/compile_file_o_all/non_exhaustive_tree.bend
---
Errors:
In definition 'Warp':
Non-exhaustive pattern matching rule. Constructor 'Tree/Both' of type 'Tree' not covered
Non-exhaustive pattern matching rule. Constructor 'Tree/Leaf' of type 'Tree' not covered

View File

@ -0,0 +1,37 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/desugar_file/tree_syntax.bend
---
(fun0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3))))
(fun1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *)))
(fun2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *)))
(fun3) = (Tree/Leaf 1)
(fun4) = λa switch a { 0: (Tree/Leaf 0); _: fun4__C0; }
(imp0) = (Tree/Node (Tree/Leaf 0) (Tree/Node (Tree/Leaf 1) (Tree/Node (Tree/Leaf 2) (Tree/Leaf 3))))
(imp1) = (Tree/Leaf (Tree/Node (Tree/Leaf *) (Tree/Leaf *)))
(imp2) = (Tree/Leaf (Tree/Leaf (Tree/Leaf *)))
(imp3) = (Tree/Leaf 1)
(imp4) = λa switch a { 0: (Tree/Leaf 0); _: imp4__C0; }
(main) = *
(Tree/Node) = λa λb λc (c Tree/Node/tag a b)
(Tree/Leaf) = λa λb (b Tree/Leaf/tag a)
(Tree/Node/tag) = 0
(Tree/Leaf/tag) = 1
(fun4__C0) = λa let {b c} = a; (Tree/Node (fun4 b) (fun4 c))
(imp4__C0) = λa let {b c} = a; (Tree/Node (imp4 b) (imp4 c))

View File

@ -2,4 +2,4 @@
source: tests/golden_tests.rs
input_file: examples/gen_tree.bend
---
λa (a Tree/Node/tag 1 λb (b Tree/Node/tag 3 λc (c Tree/Node/tag 7 λd (d Tree/Node/tag 15 Tree/Leaf Tree/Leaf) λe (e Tree/Node/tag 16 Tree/Leaf Tree/Leaf)) λf (f Tree/Node/tag 8 λg (g Tree/Node/tag 17 Tree/Leaf Tree/Leaf) λh (h Tree/Node/tag 18 Tree/Leaf Tree/Leaf))) λi (i Tree/Node/tag 4 λj (j Tree/Node/tag 9 λk (k Tree/Node/tag 19 Tree/Leaf Tree/Leaf) λl (l Tree/Node/tag 20 Tree/Leaf Tree/Leaf)) λm (m Tree/Node/tag 10 λn (n Tree/Node/tag 21 Tree/Leaf Tree/Leaf) λo (o Tree/Node/tag 22 Tree/Leaf Tree/Leaf))))
λa (a MyTree/Node/tag 1 λb (b MyTree/Node/tag 3 λc (c MyTree/Node/tag 7 λd (d MyTree/Node/tag 15 MyTree/Leaf MyTree/Leaf) λe (e MyTree/Node/tag 16 MyTree/Leaf MyTree/Leaf)) λf (f MyTree/Node/tag 8 λg (g MyTree/Node/tag 17 MyTree/Leaf MyTree/Leaf) λh (h MyTree/Node/tag 18 MyTree/Leaf MyTree/Leaf))) λi (i MyTree/Node/tag 4 λj (j MyTree/Node/tag 9 λk (k MyTree/Node/tag 19 MyTree/Leaf MyTree/Leaf) λl (l MyTree/Node/tag 20 MyTree/Leaf MyTree/Leaf)) λm (m MyTree/Node/tag 10 λn (n MyTree/Node/tag 21 MyTree/Leaf MyTree/Leaf) λo (o MyTree/Node/tag 22 MyTree/Leaf MyTree/Leaf))))

View File

@ -2,14 +2,14 @@
source: tests/golden_tests.rs
input_file: tests/golden_tests/simplify_matches/complex_with_case.bend
---
(map) = λa λb (match b { Tree/Node c d e f: λg (Tree/Node (map g c) (map g d) (map g e) (map g f)); Tree/Leaf h: λi (Tree/Leaf (i h)); } a)
(map) = λa λb (match b { Tree_/Node c d e f: λg (Tree_/Node (map g c) (map g d) (map g e) (map g f)); Tree_/Leaf h: λi (Tree_/Leaf (i h)); } a)
(main) = map
(Tree/Node) = λa λb λc λd λe (e Tree/Node/tag a b c d)
(Tree_/Node) = λa λb λc λd λe (e Tree_/Node/tag a b c d)
(Tree/Leaf) = λa λb (b Tree/Leaf/tag a)
(Tree_/Leaf) = λa λb (b Tree_/Leaf/tag a)
(Tree/Node/tag) = 0
(Tree_/Node/tag) = 0
(Tree/Leaf/tag) = 1
(Tree_/Leaf/tag) = 1