This PR implements a proof of concept for handling branches in the Liquid VM.
How
OP Codes
To implement branches i've added 3 OP codes:
OP_EVAL_CONDITION: This operation has a constant which is a Liquid Condition object. When executed it evaluates the Condition and pushes the RTEST of the result onto the stack
OP_BRANCH_UNLESS: Pops a value off the stack, if its not true then jump to the offset located in the lower 16bits of the instruction.
OP_BRANCH: This is an unconditional branch, it's used to exit out of a block to the "endif".
Process
When an if tag is encountered we add an OP_EVAL_CONDITION for the if statement and add an OP_BRANCH_UNLESS operation that jumps to the next clause (or endif)
Parsing then resumes as normal by calling internal_block_body_parse, when internal_block_body_parse returns with an unknown tag we check if its one of the clauses we can handle like else if so we add an unconditional OP_BRANCH which exits to the next endif and continue parsing the tokens for the else block.
Nesting is handled through recursion.
Heres an example of a liquid if block and its corresponding bytecode.
Theres a more complete example in test.rb with elsif and nesting.
Limitations of the POC
I'm sure theres a lot thats missing, heres the stuff im aware of:
Currently i've only parsed conditions with a single binary comparison, this needs to be extended to handle more comparisons
In order to support multiple elsif clauses i've added a small array that can handle 10 clauses, this needs to be dynamically sized.
Theres no validation of the structure of the tag. right now its possible to have an else before an elsif
What
This PR implements a proof of concept for handling branches in the Liquid VM.
How
OP Codes
To implement branches i've added 3 OP codes:
Condition
object. When executed it evaluates theCondition
and pushes the RTEST of the result onto the stackProcess
When an if tag is encountered we add an
OP_EVAL_CONDITION
for theif
statement and add anOP_BRANCH_UNLESS
operation that jumps to the next clause (or endif)Parsing then resumes as normal by calling
internal_block_body_parse
, wheninternal_block_body_parse
returns with an unknown tag we check if its one of the clauses we can handle likeelse
if so we add an unconditionalOP_BRANCH
which exits to the next endif and continue parsing the tokens for theelse
block.Nesting is handled through recursion.
Heres an example of a liquid if block and its corresponding bytecode.
Theres a more complete example in
test.rb
with elsif and nesting.Limitations of the POC
I'm sure theres a lot thats missing, heres the stuff im aware of: