Skip to content

Commit

Permalink
Merge pull request jenkinsci#37 from cloudbees/doc
Browse files Browse the repository at this point in the history
  • Loading branch information
kohsuke committed Jul 28, 2016
2 parents 7f0efe8 + ba6a331 commit a86fb03
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
82 changes: 81 additions & 1 deletion doc/block-tree.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,82 @@
# How interpreted program is represented
TBD
In [earlier doc](cps-model.md), we saw that `Block` represents a
program/code to be run.

In groovy-cps, there's `Block` implementations that correspond
1:1 to most Groovy AST nodes that are expressions (such as binary operators,
function calls, variable access) and statements (such as if statement, for loop,
and return statement.) There's no `Block` impls for class/method/field declarations
because those are not executable.

Just like AST nodes, `Block` forms a tree structure so that
any complex program can be represented. For example, representing `!f(3)`
requires 4 block nodes. In pseudo code, it would be the following:

new NotBlock(new FunctionCallBlock(new ConstantBlock("f"),new ConstantBlock(3)))

To make it easier to construct a tree of `Block`s, there's the `Builder`
class. This defines a number of convenience methods as well as handling
details like source location.

## How a Block tree gets executed
Let's see how the above `!f(3)` gets executed by the interpreter
main loop:

Next n = <<program to execute>>
while(true) {
n = <<evaluate n.block with n.continuation>>
}

First, `n` is the whole program and continuation `c` is `Continuation.HALT`
(i.e., "when the program yields a value, end the interpretation")

n = NotBlock(FunctionCallBlock(constantBlock("f"),constantBlock(3)))
c1(x) = halt(x)

Next, `NotBlock` is evaluated, which says "evaluate the nested expression
and when it yields a value, negate the value and pass it to `c`". Thus,

n = FunctionCallBlock(constantBlock("f"),constantBlock(3))
c2(x) = c1(!x) = halt(!x)

Next, `FunctionCallBlock` is evaluated, which first says
"evaluate the expression of the function name and when it yields a value,
hold on to the resulting value and evaluate arguments":

n = constantBlock("f")
c3(x) = <<evaluate rest of functionCall>>, followed by c2(x)

Next, `constantBlock("f")` is evaluated, which just passes `"f"` to `c3`
which gets the rest of `FunctionCallBlock`, which is to evaluate arguments.

constantBlock("f").eval(c3)
-> c3("f")
-> n = constantBlock(3)
c4(x) = <<evaluate rest of functionCall>>, followed by c2(x)

Next, `constantBlock(3)` is evaluated, which just passes `3` to `c4`.
At this point, we are ready to invoke a function.

constantBlock(3).eval(c4)
-> c4(3)
-> <<evaluate f(3)>>, followed by c2(x)

Let's say `f(3)` is a function call defined in JDK like this, which is not interpreted:

public static boolean f(int x) {
return x>0;
}

This function call will happen atomically from the perspective of groovy-cps,
and it yields `true`. This value is passed to `c2`, which eventually evaluate
to `halt(true)` and the program halts with the final value `true`:

...
-> <<evaluate f(3)>>, followed by c2(x)
-> c2(true)
-> c1(!true)
-> halt(false)


## How does generated code look like?
TBD
2 changes: 1 addition & 1 deletion src/main/java/com/cloudbees/groovy/cps/LValueBlock.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.cloudbees.groovy.cps;

/**
* Base class for {@link Block} that can come to the left hand side of an assignement, aka "l-value"
* Base class for {@link Block} that can come to the left hand side of an assignment, aka "l-value"
*
* Subtypes implement {@link #evalLValue(Env, Continuation)} that computes {@link LValue} object,
* which provides read/write access.
Expand Down

0 comments on commit a86fb03

Please sign in to comment.