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

Add steppable/interruptible interpreter #79

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 38 additions & 0 deletions IterativeREADME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
IterativeInterp
===============

An alternative interpreter that has been frankensteined into running somewhat iteratively.

Usage
-----

Make a new instance of IterativeInterp:
`var myInterp = new IterativeInterp();`

Pass `prepareScript` a script (`Expr`) that has been parsed with the regular `hscript.Parser` parser, as well as an optional `Dynamic->Void` callback function.

`myInterp.prepareScript(myScript, myCallbackFunction)`

Step through the script however you wish. An OpenFL example would be:
```
//Each frame, run the interpreter for 100 steps or until the script returns (whichever comes first)
addEventListener(Event.ENTER_FRAME, function(e){
myInterp.step(100);
});
```

Mechanism
---------
There are two major changes from the original, fully-recursive interpreter.

First, the contents of Expr.EBlock(a:Array<Expr>) expressions are now evaluated one-Expr-per-step, rather than all in one call. This ensures that `while` and other loops cannot lock up your program; even if the script is in an infinite loop, it will yield after the step amount given in `IterativeInterp.step(steps:Int)`. Each nested EBlock runs in its own Haxe-level stack frame, with local variable access attempts bubbling up to higher stack frames.

Second, functions defined within a script now indicate that they have been called via `ECall`, and all `Expr`s that could return a value will first check whether that `Expr` is an `ECall` waiting on a result before returning. A hscript-defined function being evaluated will force the script to re-attempt the current `Expr` evaluation before continuing.

Use Case
--------
IterativeInterp is intended to be used in situations where arbitrary hscript is being executed on non-threaded targets, to ensure that said hscript cannot crash or infinitely hang the program.

Drawbacks
---------
As all value-returning expressions are now essentially checking for function calls, and multiple function calls (such as an array declared with multiple members all being the result of a function call), there is considerable overhead. IterativeInterp is likely to be several factors slower than regular Interp.
2 changes: 1 addition & 1 deletion hscript/Interp.hx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ package hscript;
import haxe.PosInfos;
import hscript.Expr;

private enum Stop {
enum Stop {
SBreak;
SContinue;
SReturn;
Expand Down
Loading