Experimental AsyncEmbeddedScript caching
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
DSLScript*.json
|
@@ -6,8 +6,11 @@ import haxe.macro.Context;
|
||||
import haxe.macro.PositionTools;
|
||||
import sys.io.File;
|
||||
import haxe.io.Path;
|
||||
using haxe.io.Path;
|
||||
import kiss.Helpers;
|
||||
using kiss.Helpers;
|
||||
using tink.MacroApi;
|
||||
|
||||
#end
|
||||
|
||||
import kiss.Kiss;
|
||||
@@ -15,10 +18,32 @@ import kiss.ReaderExp;
|
||||
import kiss.Prelude;
|
||||
import kiss.cloner.Cloner;
|
||||
using StringTools;
|
||||
import hscript.Parser;
|
||||
import hscript.Interp;
|
||||
|
||||
typedef Continuation = () -> Void;
|
||||
typedef AsyncCommand = (AsyncEmbeddedScript, Continuation) -> Void;
|
||||
|
||||
class ObjectInterp<T> extends Interp {
|
||||
var obj:T;
|
||||
public function new(obj:T) {
|
||||
this.obj = obj;
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
override function resolve(id:String):Dynamic {
|
||||
var fieldVal = Reflect.field(obj, id);
|
||||
if (fieldVal != null)
|
||||
return fieldVal;
|
||||
else
|
||||
return super.resolve(id);
|
||||
}
|
||||
|
||||
// TODO setting variables should try to set them on the object,
|
||||
// but Interp.assign and Interp.evalAssignOp look very complicated to override
|
||||
}
|
||||
|
||||
/**
|
||||
Utility class for making statically typed, debuggable, ASYNC-BASED embedded Kiss-based DSLs.
|
||||
Examples are in the hollywoo project.
|
||||
@@ -31,6 +56,12 @@ class AsyncEmbeddedScript {
|
||||
private var labels:Map<String,Int> = [];
|
||||
private var noSkipInstructions:Map<Int,Bool> = [];
|
||||
|
||||
private var parser = new Parser();
|
||||
private var interp:ObjectInterp<AsyncEmbeddedScript>;
|
||||
|
||||
private var hscriptInstructions:Map<Int,String> = [];
|
||||
private function hscriptInstructionFile() return "";
|
||||
|
||||
public function setBreakHandler(handler:AsyncCommand) {
|
||||
onBreak = handler;
|
||||
}
|
||||
@@ -46,7 +77,17 @@ class AsyncEmbeddedScript {
|
||||
breakPoints.remove(instruction);
|
||||
}
|
||||
|
||||
public function new() {}
|
||||
public function new() {
|
||||
interp = new ObjectInterp(this);
|
||||
if (hscriptInstructionFile().length > 0) {
|
||||
#if (sys || hxnodejs)
|
||||
var cacheJson:haxe.DynamicAccess<String> = haxe.Json.parse(sys.io.File.getContent(hscriptInstructionFile()));
|
||||
for (key => value in cacheJson) {
|
||||
hscriptInstructions[Std.parseInt(key)] = value;
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
private function resetInstructions() {}
|
||||
|
||||
@@ -56,6 +97,17 @@ class AsyncEmbeddedScript {
|
||||
return instructions.length;
|
||||
}
|
||||
|
||||
#if test
|
||||
public var ranHscriptInstruction = false;
|
||||
#end
|
||||
private function runHscriptInstruction(instructionPointer:Int, cc:Continuation) {
|
||||
#if test
|
||||
ranHscriptInstruction = true;
|
||||
#end
|
||||
interp.variables['cc'] = cc;
|
||||
interp.execute(parser.parseString(hscriptInstructions[instructionPointer]));
|
||||
}
|
||||
|
||||
private function runInstruction(instructionPointer:Int, withBreakPoints = true) {
|
||||
lastInstructionPointer = instructionPointer;
|
||||
if (instructions == null)
|
||||
@@ -77,8 +129,12 @@ class AsyncEmbeddedScript {
|
||||
} else {
|
||||
() -> {};
|
||||
}
|
||||
if (hscriptInstructions.exists(instructionPointer)) {
|
||||
runHscriptInstruction(instructionPointer, continuation);
|
||||
} else {
|
||||
instructions[instructionPointer](this, continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public function run(withBreakPoints = true) {
|
||||
runInstruction(0, withBreakPoints);
|
||||
@@ -145,6 +201,17 @@ class AsyncEmbeddedScript {
|
||||
var loadingDirectory = Path.directory(classPath);
|
||||
var classFields = []; // Kiss.build() will already include Context.getBuildFields()
|
||||
|
||||
var hscriptInstructions:Map<String,String> = [];
|
||||
var cache:Map<String,String> = [];
|
||||
var cacheFile = scriptFile.withoutExtension() + ".cache.json";
|
||||
if (sys.FileSystem.exists(cacheFile)) {
|
||||
var cacheJson:haxe.DynamicAccess<String> = haxe.Json.parse(sys.io.File.getContent(cacheFile));
|
||||
for (key => value in cacheJson)
|
||||
cache[key] = value;
|
||||
}
|
||||
|
||||
var hscriptInstructionFile = scriptFile.withoutExtension() + ".hscript.json";
|
||||
|
||||
var commandList:Array<Expr> = [];
|
||||
var labelsList:Array<Expr> = [];
|
||||
var noSkipList:Array<Expr> = [];
|
||||
@@ -174,6 +241,18 @@ class AsyncEmbeddedScript {
|
||||
// As a side-effect, it also fills the KissState with the macros and reader macros that make the DSL syntax
|
||||
classFields = classFields.concat(Kiss.build(dslFile, k));
|
||||
|
||||
if (Lambda.count(cache) > 0) {
|
||||
classFields.push({
|
||||
name: "hscriptInstructionFile",
|
||||
access: [AOverride],
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
args: [],
|
||||
expr: macro return $v{hscriptInstructionFile}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
scriptFile = Path.join([loadingDirectory, scriptFile]);
|
||||
|
||||
Context.registerModuleDependency(Context.getLocalModule(), scriptFile);
|
||||
@@ -183,6 +262,14 @@ class AsyncEmbeddedScript {
|
||||
Kiss.measure('Compiling kiss: $scriptFile', () -> {
|
||||
#end
|
||||
function process(nextExp) {
|
||||
var cacheKey = Reader.toString(nextExp.def);
|
||||
if (cache.exists(cacheKey)) {
|
||||
hscriptInstructions[Std.string(commandList.length)] = cache[cacheKey];
|
||||
trace(hscriptInstructions);
|
||||
commandList.push(macro null);
|
||||
return;
|
||||
}
|
||||
|
||||
nextExp = Kiss.macroExpand(nextExp, k);
|
||||
|
||||
// Allow packing multiple commands into one exp with a (commands <...>) statement
|
||||
@@ -208,6 +295,7 @@ class AsyncEmbeddedScript {
|
||||
var c = macro function(self, cc) {
|
||||
$expr;
|
||||
};
|
||||
cache[cacheKey] = expr.toString();
|
||||
commandList.push(c.expr.withMacroPosOf(nextExp));
|
||||
}
|
||||
|
||||
@@ -242,6 +330,9 @@ class AsyncEmbeddedScript {
|
||||
})
|
||||
});
|
||||
|
||||
sys.io.File.saveContent(cacheFile, haxe.Json.stringify(cache));
|
||||
sys.io.File.saveContent(hscriptInstructionFile, haxe.Json.stringify(hscriptInstructions));
|
||||
|
||||
return classFields;
|
||||
}
|
||||
#end
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// TODO make a better position reification scheme here
|
||||
(defReaderMacro "goop" [stream] `(Assert.isTrue true))
|
||||
(defReaderMacro "gloop" [stream] `(Assert.isFalse false))
|
||||
(prop Assert utest.Assert)
|
2
src/test/cases/DSLScriptThatWillCache.dsl
Normal file
2
src/test/cases/DSLScriptThatWillCache.dsl
Normal file
@@ -0,0 +1,2 @@
|
||||
goop
|
||||
gloop
|
@@ -3,6 +3,7 @@ package test.cases;
|
||||
import utest.Test;
|
||||
import utest.Assert;
|
||||
import kiss.EmbeddedScript;
|
||||
import kiss.AsyncEmbeddedScript;
|
||||
import kiss.Prelude;
|
||||
|
||||
class DSLTestCase extends Test {
|
||||
@@ -13,7 +14,29 @@ class DSLTestCase extends Test {
|
||||
function testFork() {
|
||||
new DSLScript().fork([(self) -> Assert.equals(5, 5), (self) -> Assert.equals(7, 7)]);
|
||||
}
|
||||
|
||||
function testAsync() {
|
||||
var script = new AsyncDSLScript();
|
||||
script.run();
|
||||
Assert.isFalse(script.ranHscriptInstruction);
|
||||
}
|
||||
|
||||
function testAsyncFromCache() {
|
||||
var script = new AsyncDSLScriptThatWillCache();
|
||||
script.run();
|
||||
var script2 = new AsyncDSLScriptThatWillCache2();
|
||||
Assert.isTrue(script.ranHscriptInstruction || script2.ranHscriptInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
@:build(kiss.EmbeddedScript.build("DSL.kiss", "DSLScript.dsl"))
|
||||
class DSLScript extends EmbeddedScript {}
|
||||
|
||||
@:build(kiss.AsyncEmbeddedScript.build("", "DSL.kiss", "DSLScript.dsl"))
|
||||
class AsyncDSLScript extends AsyncEmbeddedScript {}
|
||||
|
||||
@:build(kiss.AsyncEmbeddedScript.build("", "DSL.kiss", "DSLScriptThatWillCache.dsl"))
|
||||
class AsyncDSLScriptThatWillCache extends AsyncEmbeddedScript {}
|
||||
|
||||
@:build(kiss.AsyncEmbeddedScript.build("", "DSL.kiss", "DSLScriptThatWillCache.dsl"))
|
||||
class AsyncDSLScriptThatWillCache2 extends AsyncEmbeddedScript {}
|
4
test.sh
4
test.sh
@@ -15,6 +15,10 @@ elif [ "$KISS_TARGET" = nodejs ]; then
|
||||
lix install haxelib:hxnodejs
|
||||
fi
|
||||
|
||||
if [ -e DSLScript.cache.json ]; then
|
||||
rm DSLScript*.json
|
||||
fi
|
||||
|
||||
if [ ! -z "$2" ]; then
|
||||
haxe -D cases=$2 build-scripts/common-args.hxml build-scripts/common-test-args.hxml build-scripts/$KISS_TARGET/test.hxml
|
||||
else
|
||||
|
Reference in New Issue
Block a user