Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for nested if and foreach #960

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9bb3faf
feat: support nested if separated by other statements and a way to en…
matteo-cristino Nov 8, 2024
7436222
fix: first update to parser to support nested if
matteo-cristino Nov 11, 2024
14300a1
fix: typos in error message
matteo-cristino Nov 11, 2024
f8d5e5e
test: add first tests for nested branching
matteo-cristino Nov 11, 2024
e47087e
test(array): close unclosed if branches
matteo-cristino Nov 11, 2024
e68e42b
fix: parser detect wrong if branching exit
matteo-cristino Nov 11, 2024
82534b5
Merge branch 'master' into fix/if
matteo-cristino Nov 11, 2024
998b82e
Merge branch 'master' into fix/if
matteo-cristino Nov 11, 2024
ece7b32
feat: support also nested foreach loops
matteo-cristino Nov 13, 2024
89bdcfd
fix: some fixes on transitions table
matteo-cristino Nov 13, 2024
484291d
fix: improve checks on branch and loop ending
matteo-cristino Nov 14, 2024
275c2c9
refactor: remove code duplication
matteo-cristino Nov 14, 2024
0a4ed67
refactor: remove more duplicated code
matteo-cristino Nov 14, 2024
e402a21
fix: small optimization to manage_foreach
matteo-cristino Nov 14, 2024
c57eed4
feat: manage_foreach optimization
matteo-cristino Nov 14, 2024
b048d51
Merge branch 'master' into fix/if
matteo-cristino Nov 14, 2024
b8c5848
feat: endif ends only the latest opend if
matteo-cristino Nov 14, 2024
7d49438
fix: manage loops over empty array
matteo-cristino Nov 14, 2024
065421c
test: one more test
matteo-cristino Nov 14, 2024
d2d274e
test: fix comment char
matteo-cristino Nov 14, 2024
b1c455b
fix: correctly propagate errors in if branching, only the errors in t…
matteo-cristino Nov 15, 2024
7f14b98
test: add some educational for loop examples found around (like rever…
matteo-cristino Nov 15, 2024
1e651cb
fix: improve foreach performance
matteo-cristino Nov 17, 2024
99732cc
fix: loop over integers was doing one more step
matteo-cristino Nov 18, 2024
30984e1
refactor: in foreach do not throw an error when reaching maxiter, but…
matteo-cristino Nov 18, 2024
ad0c782
refactor: create a AST_iterator
matteo-cristino Nov 18, 2024
039b3de
fix: cover case in which contract ends with endif or endforeach
matteo-cristino Nov 18, 2024
c70a736
refactor: simplify a bit manage_branching and use more local varibale…
matteo-cristino Nov 18, 2024
75eb7a7
feat: improve general performance by reducing the total numbers of ca…
matteo-cristino Nov 20, 2024
e1e91f4
docs: add foreach documentation and update if documentation (#973)
matteo-cristino Nov 25, 2024
63b9067
Merge branch 'master' into fix/if
matteo-cristino Nov 25, 2024
55174bf
Merge branch 'master' into fix/if
matteo-cristino Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ tests = [
'bitcoin', 'branching', 'cookbook_debug', 'cookbook_dictionaries',
'cookbook_ecdh', 'cookbook_ecdh_encrypt_json', 'cookbook_given',
'cookbook_intro', 'cookbook_then', 'cookbook_when', 'credential',
'dictionary', 'dp3t', 'ecdh', 'float', 'foreach', 'fsp',
'dictionary', 'dp3t', 'ecdh', 'educational', 'float', 'foreach', 'fsp',
'generic_bbs', 'generic_dilithium', 'generic_ecdh', 'generic_eddsa',
'generic_es256', 'generic_mldsa44', 'generic_schnorr', 'given',
'hash', 'http', 'keys', 'kyber', 'mlkem512', 'ntrup', 'numbers',
Expand Down
13 changes: 7 additions & 6 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

- Zencode Basics
- [Quickstart](/pages/zencode-cookbook-intro.md "Zencode Quickstart")
- [Zencode: "Given" phase](/pages/zencode-cookbook-given.md "Zencode cookbook Given")
- [Zencode: "When phase](/pages/zencode-cookbook-when.md "Zencode cookbook When")
- [Zencode: "Then" phase](/pages/zencode-cookbook-then.md "Zencode cookbook Then")
- [Zencode: "If/Endif" branching](/pages/zencode-if-endif.md "Zencode branching")
- [Zencode: debug](/pages/zencode-debug.md "Zencode debug")
- [Given phase](/pages/zencode-cookbook-given.md "Zencode cookbook Given")
- [When phase](/pages/zencode-cookbook-when.md "Zencode cookbook When")
- [Then phase](/pages/zencode-cookbook-then.md "Zencode cookbook Then")
- [If/Endif branching](/pages/zencode-if-endif.md "Zencode branching")
- [Foreach/EndForeach looping](/pages/zencode-foreach-endforeach.md "Zencode looping")
- [Debug](/pages/zencode-debug.md "Zencode debug")

- Zencode Advanced
- [Cryptography](/pages/zencode-crypto.md "Zencode")
Expand All @@ -37,7 +38,7 @@
- [Zencode command list](/pages/zencode-list.md "Zencode command list")

- Bindings
- [ Use Zenroom as a library](/pages/how-to-embed.md "Embed")
- [Use Zenroom as a library](/pages/how-to-embed.md "Embed")
- [JavaScript ](/pages/javascript.md "Use Zenroom in JavaScript")
- [Python](/pages/python.md "Use Zenroom in JavaScript")
- [Java](/pages/java.md "Use Zenroom in Java")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"my_array": [
{
"data": {
"key": "value"
}
},
{
"other_data": {
"other_key": [
"other_value_1",
"other_value_2"
]
}
}
],
"one": 1,
"filter": "other_value_2"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"output":["long_array"],"res":["value"],"res_foreach":["other_value_2"]}
46 changes: 46 additions & 0 deletions docs/examples/zencode_cookbook/branching/nested_if_in_foreach.zen
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Given I have a 'string array' named 'my_array'
Given I have a 'number' named 'one'
Given I have a 'string' named 'filter'
When I create the 'string array' named 'res'
When I create the 'string array' named 'res_foreach'
If I verify 'my_array' is found
If I verify size of 'my_array' is more than 'one'
Then print the string 'long array'
EndIf
Foreach 'el' in 'my_array'
If I verify 'data' is found in 'el'
When I pickup from path 'el.data'
If I verify 'other_key' is found in 'data'
When I pickup from path 'data.other_key'
Foreach 'e' in 'other_key'
When I copy 'e' in 'res_foreach'
EndForeach
When I remove 'other_key'
EndIf
If I verify 'key' is found in 'data'
When I move 'key' from 'data' in 'res'
EndIf
When I remove 'data'
EndIf
If I verify 'other_data' is found in 'el'
When I pickup from path 'el.other_data'
If I verify 'other_key' is found in 'other_data'
When I pickup from path 'other_data.other_key'
Foreach 'e' in 'other_key'
If I verify 'e' is equal to 'filter'
When I copy 'e' in 'res_foreach'
EndIf
When done
EndForeach
When I remove 'other_key'
EndIf
If I verify 'key' is found in 'other_data'
When I move 'key' from 'other_data' in 'res'
EndIf
When I remove 'other_data'
EndIf
EndForeach
EndIf

Then print 'res'
Then print 'res_foreach'
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ If I verify 'number_lower' is equal to 'number_higher'
When I create the random 'random_this_is_impossible'
Then print string 'the conditions can never be satisfied'
Endif
EndIf

# You can also check if an object exists at a certain point of the execution, with the statement:
# If I verify 'objectName' is found
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numbers": [12, 75, 150, 180, 145, 525, 50]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"0": 0,
"5": 5,
"150": 150,
"500": 500
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"res":[75,150,145]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Problem:
# display only those numbers from a list that satisfy the following conditions
# 1. The number must be divisible by five
# 2. If the number is greater than 150, then skip it and move to the following number
# 3. If the number is greater than 500, then stop the loop

Given I have a 'number array' named 'numbers'
Given I have a 'number' named '0'
Given I have a 'number' named '5'
Given I have a 'number' named '150'
Given I have a 'number' named '500'

When I create the 'number array' named 'res'

Foreach 'num' in 'numbers'
# point 3
If I verify number 'num' is more than '500'
When I break foreach
EndIf
# point 2
If I verify number 'num' is less or equal than '150'
When I create the result of 'num' % '5'
# point 1
If I verify 'result' is equal to '0'
When I copy 'num' in 'res'
EndIf
When I remove 'result'
EndIf
EndForeach

Then print the 'res'
3 changes: 3 additions & 0 deletions docs/examples/zencode_cookbook/foreach/verysimple.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"xs": ["a", "b"]
}
1 change: 1 addition & 0 deletions docs/examples/zencode_cookbook/foreach/verysimple.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"new_array":["a","b"]}
6 changes: 6 additions & 0 deletions docs/examples/zencode_cookbook/foreach/verysimple.zen
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Given I have a 'string array' named 'xs'
When I create the 'string array' named 'new array'
Foreach 'x' in 'xs'
When I move 'x' in 'new array'
EndForeach
Then print 'new array'
95 changes: 95 additions & 0 deletions docs/pages/zencode-foreach-endforeach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

# Looping: Foreach/EndForeach statements

Zenroom allows looping, done using the **Foreach** and **EndForeach** statements.
There are two possible way of looping that are, over an array
```gherkin
Foreach 'element' in 'array'
```
or from a number to another with certain step
```gherkin
Foreach 'i' in sequence from 'zero' to 'ten' with step 'one'
```
As can be seen the keyword **Foreach** is the one that indicates the start of a loop, while
the statement **EndForeach** indicates the ends of loop.
When reaching it if the condition of the foreach is still valid the loop is performed again.

Inside a **Foreach** you can use:
* **Foreach** statements (any of them must have a corresponding **EndForeach**)
* [**If**](zencode-if-endif) statements that must be closed before the loop end. Morever, even if the basic **If** support
the use of **Then** in it, when it is inside a loop this last property is not true anymore, only **When** statements can be used.
* [**When**](zencode-cookbook-when) statements

## Simple Foreach/EndForeach example

In the following script we are looping over all the elements of an array and simply copying them into a new one.

[](../_media/examples/zencode_cookbook/foreach/verysimple.zen ':include :type=code gherkin')

Indeed if run with data

[](../_media/examples/zencode_cookbook/foreach/verysimple.data ':include :type=code json')

the result will be

[](../_media/examples/zencode_cookbook/foreach/verysimple.out ':include :type=code json')


## Break from a Foreach loop

The
```gherkin
When I break the foreach
# or equivalently
When I exit the foreach
```
statements allow you to exit a foreach loop prematurely. When executed, it immediately terminates the loop's iteration, skipping
any remaining items in the collection or sequence. The program then continues with the first statement following the loop.

The break statement is typically used when a specific condition is met within the loop,
and further iteration is unnecessary or undesirable. An example to understand the foreach and the break can be the following:

Display only those numbers from the list:

[](../_media/examples/zencode_cookbook/educational/foreach_divisible_by_five.data.json ':include :type=code json')

that satisfy the following conditions:
1. The number must be divisible by five
1. If the number is greater than 150, then skip it and move to the following number
1. If the number is greater than 500, then stop the loop

We start by defineing some usefull variables in a file that we will use as keys file

[](../_media/examples/zencode_cookbook/educational/foreach_divisible_by_five.keys.json ':include :type=code json')

then the code will be

[](../_media/examples/zencode_cookbook/educational/foreach_divisible_by_five.zen ':include :type=code gherkin')

resulting in

[](../_media/examples/zencode_cookbook/educational/foreach_divisible_by_five.out.json ':include :type=code json')

## More complex Foreach/EndForeach example

You can play with them as much as you want, like:

[](../_media/examples/zencode_cookbook/branching/nested_if_in_foreach.zen ':include :type=code gherkin')

that with input data

[](../_media/examples/zencode_cookbook/branching/nested_if_in_foreach.data.json ':include :type=code json')

will result in

[](../_media/examples/zencode_cookbook/branching/nested_if_in_foreach.out.json ':include :type=code json')

# The script used to create the material in this page

All the smart contracts and the data you see in this page are generated by the scripts [branching.bats](https://github.com/dyne/Zenroom/blob/master/test/zencode/branching.bats),
[foreach.bats](https://github.com/dyne/Zenroom/blob/master/test/zencode/foreach.bats) and
[branching.bats](https://github.com/dyne/Zenroom/blob/master/test/zencode/educational.bats).
If you want to run the scripts (on Linux) you should:
- *git clone https://github.com/dyne/Zenroom.git*
- install **jq**
- download a [zenroom binary](https://zenroom.org/#downloads) and place it */bin* or */usr/bin* or in *./Zenroom/src*
82 changes: 40 additions & 42 deletions docs/pages/zencode-if-endif.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@

# Branching: If/Endif statements

Zenroom allows branching, done using the ***If*** and ***Endif*** Statements.

The **first** ***If*** statement creates a *conditional branch* and can be *stacked*: you can have multiple conditions following each other, something like:

* If (condition1)
* If (condition2)
* If (condition3)
* When (do something)
* Then (print something)
* Endif

The statement ***Endif*** ends the conditional branch, so takes the execution back to the *main* execution branch, independently on the amount of conditions (on how many ***If*** you have stacked together).

You can not use a ***When*** or ***Then*** statement, inside a stack of ***If***, so something like this will return an error:

* If (condition1)
* <span style="color: red">When (do something)</span>
* If (condition2)
* <span style="color: red">Then (print something)</span>
* If (condition3)
* Then (print something)
* Endif



## Simple If/Endif example
Zenroom allows branching, done using the **If** and **Endif** Statements.

The *conditional branch* starts with a **If** statement and ends when the corresponding **EndIf** is found,
the **EndIf** close always only the latest opened **If**. For example

```gherking
If (condition1)
When (do something)
If (condition2)
If (condition3)
When (do something else)
Then (print something)
EndIf # ends conditional branching of condition 3
Then (print something else)
EndIf # ends conditional branching of condition 2
Endif # ends conditional branching of condition 1
```

Inside a **If** you can use:
* **If** statements
* [**When**](zencode-cookbook-when) statements
* [**Then**](zencode-cookbook-then) statements
* [**Foreach**](zencode-foreach-endforeach) statementes (any of them must have a corresponding **EndForeach**
inside the conditional branch), pay attention that inside a **Foreach** you can not use the **Then** statements,
even if it is inside a **If** branching.

## Simple If/Endif example

We'll use a simple JSON file with two numbers as input:

Expand All @@ -38,7 +39,7 @@ And in this script we can see some simple comparisons between numbers:
* two stacked conditions, where the second one will fail
* a simple condition, based on output from a the previous branch, that will succeed

The script goes:
The script goes:

[](../_media/examples/zencode_cookbook/branching/number_comparison.zen ':include :type=code gherkin')

Expand All @@ -64,28 +65,25 @@ You should expect an output like this:

[](../_media/examples/zencode_cookbook/branching/complex_comparison_output.json ':include :type=code json')

## Statements that can be used as a condition
## Statements that can be used as a condition

Only some statements can be used as a condition, typically the ones that **verify** a value or check do cryptographic checks, specifically:
Only a subset of the **When** statements can be used to create a conditional branch, that are the one that,
after the `When I` part, start with the **verify** keyword. They can be both cryptographic statements
like a singature verification

* all the **verify** statements
* all the **is less than** and **is more than** statements
* all the **is less or equal than** and **is more or equal than** statements
* all the **is equal to** and **is not equal to** statements
* all the **is found** and **is not found** statements
```gherkin
When I verify '' has a eddsa signature in '' by ''
```

or, as seen above, more simpler check, like number comparison

```gherkin
When I verify number '' is less than ''
```

# The script used to create the material in this page

All the smart contracts and the data you see in this page are generated by the scripts [branching.bats](https://github.com/dyne/Zenroom/blob/master/test/zencode/branching.bats). If you want to run the scripts (on Linux) you should:
All the smart contracts and the data you see in this page are generated by the scripts [branching.bats](https://github.com/dyne/Zenroom/blob/master/test/zencode/branching.bats). If you want to run the scripts (on Linux) you should:
- *git clone https://github.com/dyne/Zenroom.git*
- install **jq**
- download a [zenroom binary](https://zenroom.org/#downloads) and place it */bin* or */usr/bin* or in *./Zenroom/src*



<!-- Temp removed,


-->
Loading
Loading