ethereum/compiler — pull request #9 by dougbtv (2014-03-24 03:02:46+00:00)
Title: Fix for conditional statements, supports if/elif/else

**tl;dr**

If/elif/else logic wasn't jumping to the right points. I needed to enable the parser to mark a block as an `else`. I also enhanced the statement compiler to recognize that distinction sent from the parser, and extended it to keep another index (related to the label index), to use to jump out of the inside of an `if`/`elif` block after it was executed.

I first explain what's fixed, and run through the fixed logic. But I outline examples of the previous logic at the end.

----

**Detail with fixed edition**

What I needed to do was differentiate between an `else` and an `if` in the parser -- which was previously just appended as extra instructions, without marking what was the instructions inside the `else` block. So I also had it send along `elif`, too. This way, once it hits the compiler, it can craft the conditional expression -- if and only if need be. Seeing that `else` isn't specified otherwise

Additionally, I need the statement compiler to remember an extra piece -- specifically the `LABEL_X` at the end of the entire `if` block -- to use after the statements inside an `if` or `elif` block in order to `JUMP` to that label by `REF_X`

----

**Example A: if/elif**

Let's take a look at the byte code, with this fix in place. Here's an example with just `elif`.

if 1 < 0:
stop
elif 1 > 0:
a = 10

AST: ['if', ['<', '1', '0'], 'stop', ['elif', ['>', '1', '0'], ['set', 'a', '10']]]

Output: PUSH 0 PUSH 1 LT NOT PUSH 13 JUMPI STOP PUSH 27 JUMP PUSH 0 PUSH 1 GT NOT PUSH 27 JUMPI PUSH 10 PUSH 0 MSTORE

------------------------------ -
-- EVM3-ASM inspection ------ -
---------------------------- -
0001 PUSH
0002 0
0003 PUSH
0004 1
0005 LT
0006 NOT
0007 PUSH
0008 13
0009 JUMPI
0010 STOP
0011 PUSH
0012 27
0013 JUMP
0014 PUSH
0015 0
0016 PUSH
0017 1
0018 GT
0019 NOT
0020 PUSH
0021 27
0022 JUMPI
0023 PUSH
0024 10
0025 PUSH
0026 0
0027 MSTORE

What you'll see if that:
* the first condition `if 1 < 0:` is false, so moving to the instructions `PUSH 13 JUMPI` (0007-0009)
* we're told the jump will be made to instruction 13 (remember: It's the next instruction *after* that that's executed.)
* Following @ instruction 14, we're presented with the elif, `elif 1 > 0:` (`PUSH 0 PUSH 1 GT NOT PUSH 27 JUMPI`)
* Which evaluates to true, and we continue with the rest of the application normally.
* If you presend that the first condition `if 1 < 0:` evaluates to true, and that there is no stop @ 0010, you'll see that we jump to a point after the else (in this case, the end of the program)

----

**Example B: if/elif/else**

Now let's take the ultimate example, an entire `if/elif/else` portion.

if 1 == 0:
stop
elif 3 == 2:
stop
else:
a = 10

AST: ['if', ['==', '1', '0'], 'stop', ['elif', ['==', '3', '2'], 'stop', ['else', ['set', 'a', '10']]]]

Output: PUSH 0 PUSH 1 EQ NOT PUSH 13 JUMPI STOP PUSH 31 JUMP PUSH 2 PUSH 3 EQ NOT PUSH 26 JUMPI STOP PUSH 31 JUMP PUSH 10 PUSH 0 MSTORE

------------------------------ -
-- EVM3-ASM inspection ------ -
---------------------------- -
0001 PUSH
0002 0
0003 PUSH
0004 1
0005 EQ
0006 NOT
0007 PUSH
0008 13
0009 JUMPI
0010 STOP
0011 PUSH
0012 31
0013 JUMP
0014 PUSH
0015 2
0016 PUSH
0017 3
0018 EQ
0019 NOT
0020 PUSH
0021 26
0022 JUMPI
0023 STOP
0024 PUSH
0025 31
0026 JUMP
0027 PUSH
0028 10
0029 PUSH
0030 0
0031 MSTORE

* After the first condition evulates to false `if 1 == 0:`; At instruction 0009, we jump to instruction 0013
* At 0014, our condition `if 3 == 2:` also evaluates to false, so at instruction 0022, we jump to instruction 0026
* At 0027, we process the block inside the else, and set `a = 10`
* Again, like example A, if we pretend that the first condition evaluates to true, and we don't execute a stop @ instruction 0010, we then are jumped to instruction 0031, which is the end of the program.

----

**Issues from previous logic**

Let's look at an example from the previous version, with just if/else

if 1 < 0:
stop
else:
a = 10

AST: ['if', ['<', '1', '0'], 'stop', ['set', 'a', '10']]

Output: PUSH 0 PUSH 1 LT NOT PUSH 18 JUMPI STOP PUSH 18 JUMP PUSH 10 PUSH 0 MSTORE

------------------------------ -
-- EVM3-ASM inspection ------ -
---------------------------- -
0001 PUSH
0002 0
0003 PUSH
0004 1
0005 LT
0006 NOT
0007 PUSH
0008 18
0009 JUMPI
0010 STOP
0011 PUSH
0012 18
0013 JUMP
0014 PUSH
0015 10
0016 PUSH
0017 0
0018 MSTORE

If you follow the flow in the compilation -- you'll see that after the `if` it jumps to the end of the instructions `PUSH 18 JUMPI` (0007-0009), which is the end of the application. But, because `1 > 0` is false, we should've jumped to the `else` block.

It didn't work with `elif` either, it would do the same and jump to the end of the `if` block, every time.

Here's an example with an else if.

if 1 < 0:
stop
elif 1 > 0:
a = 10

AST: ['if', ['<', '1', '0'], 'stop', ['if', ['>', '1', '0'], ['set', 'a', '10']]]

Output: PUSH 0 PUSH 1 LT NOT PUSH 27 JUMPI STOP PUSH 27 JUMP PUSH 0 PUSH 1 GT NOT PUSH 27 JUMPI PUSH 10 PUSH 0 MSTORE

------------------------------ -
-- EVM3-ASM inspection ------ -
---------------------------- -
0001 PUSH
0002 0
0003 PUSH
0004 1
0005 LT
0006 NOT
0007 PUSH
0008 27
0009 JUMPI
0010 STOP
0011 PUSH
0012 27
0013 JUMP
0014 PUSH
0015 0
0016 PUSH
0017 1
0018 GT
0019 NOT
0020 PUSH
0021 27
0022 JUMPI
0023 PUSH
0024 10
0025 PUSH
0026 0
0027 MSTORE

It suffers the same woes, in that again, we jump to the end of the program, follow along in the compilation and you'll find `PUSH 27 JUMPI` (0007-0009), which is again at the end of the application, even though our `elif` evaluates to true.