Pass EmbeddedScript commands the script instance
This commit is contained in:
@@ -9,7 +9,10 @@ import sys.io.File;
|
|||||||
import kiss.Kiss;
|
import kiss.Kiss;
|
||||||
import kiss.cloner.Cloner;
|
import kiss.cloner.Cloner;
|
||||||
|
|
||||||
typedef Command = () -> Void;
|
// Commands handlers accept a Dynamic argument because when fork() happens
|
||||||
|
// (1) the calling context can no longer assume the instance it constructed is the instance running the command.
|
||||||
|
// (2) I don't know how to put a generic parameter <T extends EmbeddedScript> on the Command typedef.
|
||||||
|
typedef Command = (Dynamic) -> Void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Utility class for making statically typed, debuggable, embedded Kiss-based DSLs.
|
Utility class for making statically typed, debuggable, embedded Kiss-based DSLs.
|
||||||
@@ -24,12 +27,9 @@ class EmbeddedScript {
|
|||||||
|
|
||||||
private var instructions:Array<Command> = null;
|
private var instructions:Array<Command> = null;
|
||||||
private var breakPoints:Map<Int, () -> Bool> = [];
|
private var breakPoints:Map<Int, () -> Bool> = [];
|
||||||
// Break handlers accept a Dynamic argument because when fork() happens
|
private var onBreak:Command = null;
|
||||||
// (1) the calling context can no longer assume the instance it constructed is the instance hitting the breakpoint.
|
|
||||||
// (2) I don't know how to put a generic parameter <T extends EmbeddedScript> on a BreakHandler function type.
|
|
||||||
private var onBreak:(Dynamic) -> Void = null;
|
|
||||||
|
|
||||||
public function setBreakHandler(handler:(Dynamic) -> Void) {
|
public function setBreakHandler(handler:Command) {
|
||||||
onBreak = handler;
|
onBreak = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class EmbeddedScript {
|
|||||||
classFields.push(field);
|
classFields.push(field);
|
||||||
} else {
|
} else {
|
||||||
// In a DSL script, anything that's not a field definition is a command line
|
// In a DSL script, anything that's not a field definition is a command line
|
||||||
commandList.push(macro function() {
|
commandList.push(macro function(self) {
|
||||||
${Kiss.readerExpToHaxeExpr(nextExp, k)};
|
${Kiss.readerExpToHaxeExpr(nextExp, k)};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ class EmbeddedScript {
|
|||||||
expr: macro {
|
expr: macro {
|
||||||
if (instructions == null)
|
if (instructions == null)
|
||||||
resetInstructions();
|
resetInstructions();
|
||||||
instructions[instructionPointer]();
|
instructions[instructionPointer](this);
|
||||||
++instructionPointer;
|
++instructionPointer;
|
||||||
if (breakPoints.exists(instructionPointer) && breakPoints[instructionPointer]()) {
|
if (breakPoints.exists(instructionPointer) && breakPoints[instructionPointer]()) {
|
||||||
running = false;
|
running = false;
|
||||||
@@ -208,7 +208,10 @@ class EmbeddedScript {
|
|||||||
// fields, otherwise DSL state will be lost when forking, which is unacceptable
|
// fields, otherwise DSL state will be lost when forking, which is unacceptable
|
||||||
var fork = new kiss.cloner.Cloner().clone(this);
|
var fork = new kiss.cloner.Cloner().clone(this);
|
||||||
fork.instructions[instructionPointer] = command;
|
fork.instructions[instructionPointer] = command;
|
||||||
|
trace(fork.breakPoints);
|
||||||
|
trace('running a fork from ' + Std.string(instructionPointer + 1));
|
||||||
fork.run();
|
fork.run();
|
||||||
|
trace("fork finished");
|
||||||
fork;
|
fork;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ class Helpers {
|
|||||||
CallExp(evalUnquotes(func, k, args), evalUnquoteLists(callArgs, k, args).map(evalUnquotes.bind(_, k, args)));
|
CallExp(evalUnquotes(func, k, args), evalUnquoteLists(callArgs, k, args).map(evalUnquotes.bind(_, k, args)));
|
||||||
case ListExp(elements):
|
case ListExp(elements):
|
||||||
ListExp(evalUnquoteLists(elements, k, args).map(evalUnquotes.bind(_, k, args)));
|
ListExp(evalUnquoteLists(elements, k, args).map(evalUnquotes.bind(_, k, args)));
|
||||||
|
case TypedExp(type, innerExp):
|
||||||
|
TypedExp(type, evalUnquotes(innerExp, k, args));
|
||||||
case FieldExp(field, innerExp):
|
case FieldExp(field, innerExp):
|
||||||
FieldExp(field, evalUnquotes(innerExp, k, args));
|
FieldExp(field, evalUnquotes(innerExp, k, args));
|
||||||
case KeyValueExp(keyExp, valueExp):
|
case KeyValueExp(keyExp, valueExp):
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class Kiss {
|
|||||||
return switch (exp.def) {
|
return switch (exp.def) {
|
||||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||||
var expandedExp = macros[mac](exp, args, k);
|
var expandedExp = macros[mac](exp, args, k);
|
||||||
if (expandedExp != null) readerExpToField(macros[mac](expandedExp, args, k), k) else null;
|
if (expandedExp != null) readerExpToField(macros[mac](expandedExp, args, k), k, errorIfNot) else null;
|
||||||
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
||||||
fieldForms[formName](exp, args, k);
|
fieldForms[formName](exp, args, k);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class DSLTestCase extends Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testFork() {
|
function testFork() {
|
||||||
new DSLScript().fork([() -> Assert.equals(5, 5), () -> Assert.equals(7, 7)]);
|
new DSLScript().fork([(self) -> Assert.equals(5, 5), (self) -> Assert.equals(7, 7)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,8 @@ class BootCodeExample extends EmbeddedScript {}
|
|||||||
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeDSL.kiss", "src/year2020/inputs/day8.txt"))
|
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeDSL.kiss", "src/year2020/inputs/day8.txt"))
|
||||||
class BootCodeReal extends EmbeddedScript {}
|
class BootCodeReal extends EmbeddedScript {}
|
||||||
|
|
||||||
|
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeFixDSL.kiss", "src/year2020/inputs/day8-example.txt"))
|
||||||
|
class BootCodeFixExample extends EmbeddedScript {}
|
||||||
|
|
||||||
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeFixDSL.kiss", "src/year2020/inputs/day8.txt"))
|
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeFixDSL.kiss", "src/year2020/inputs/day8.txt"))
|
||||||
class BootCodeFix extends EmbeddedScript {}
|
class BootCodeFix extends EmbeddedScript {}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
(defmethod setBreakPoint [] (addBreakPoint instructionPointer))
|
(defmethod setBreakPoint [] (addBreakPoint instructionPointer))
|
||||||
|
|
||||||
(defmethod nop [v] (setBreakPoint))
|
(defmethod nop [v :Dynamic self] (self.setBreakPoint))
|
||||||
(defmethod acc [v] (setBreakPoint) (set accumulator (+ accumulator v)))
|
(defmethod acc [v :Dynamic self] (self.setBreakPoint) (set self.accumulator (+ self.accumulator v)))
|
||||||
(defmethod jmp [v]
|
(defmethod jmp [v :Dynamic self]
|
||||||
(setBreakPoint)
|
(self.setBreakPoint)
|
||||||
//(print (+ "jumping " (Std.string v) " from " (Std.string instructionPointer)))
|
(print (+ "jumping " (Std.string v) " from " (Std.string (+ 1 self.instructionPointer))))
|
||||||
(set instructionPointer (+ instructionPointer (- v 1))))
|
(set self.instructionPointer (+ self.instructionPointer (- v 1))))
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
`(,(ReaderExp.Symbol
|
`(,(ReaderExp.Symbol
|
||||||
(begin (stream.dropWhitespace) (nextToken stream)))
|
(begin (stream.dropWhitespace) (nextToken stream)))
|
||||||
(,(ReaderExp.Symbol
|
(,(ReaderExp.Symbol
|
||||||
(begin (stream.dropWhitespace) (stream.expect "+/-" (lambda [] (stream.takeChars 1)))))
|
(begin (stream.dropWhitespace) (stream.expect "+/-" (lambda [] (stream.takeChars 1)))))
|
||||||
0
|
0
|
||||||
,(ReaderExp.Symbol
|
,(ReaderExp.Symbol
|
||||||
(nextToken stream)))))
|
(nextToken stream)))
|
||||||
|
self))
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
(load "BootCodeCommon.kiss")
|
(load "BootCodeCommon.kiss")
|
||||||
|
|
||||||
(defvar &mut forked false)
|
(defvar :Map<Int,Bool> instructionsTested (new Map<Int,Bool>))
|
||||||
|
(defprop &mut forked false)
|
||||||
|
(defprop &mut forkedAt -1)
|
||||||
|
|
||||||
(defreadermacro ["jmp" "nop"] [stream]
|
(defreadermacro ["jmp" "nop"] [stream]
|
||||||
(let [inst
|
(let [inst
|
||||||
@@ -13,13 +15,26 @@
|
|||||||
arg
|
arg
|
||||||
(ReaderExp.Symbol (nextToken stream))]
|
(ReaderExp.Symbol (nextToken stream))]
|
||||||
(stream.dropWhitespace)
|
(stream.dropWhitespace)
|
||||||
`(if forked
|
`(cond
|
||||||
(,instSymbol (,op 0 ,arg))
|
((or self.forked (instructionsTested.exists self.instructionPointer))
|
||||||
(begin
|
(print "can't fork again")
|
||||||
|
(,instSymbol (,op 0 ,arg) self))
|
||||||
(fork [
|
(true
|
||||||
(lambda [] (when ,(ReaderExp.Symbol (Std.string (= inst "nop"))) (set forked true)) (jmp (,op 0 ,arg)))
|
(dictSet instructionsTested self.instructionPointer true)
|
||||||
(lambda [] (when ,(ReaderExp.Symbol (Std.string (= inst "jmp"))) (set forked true)) (nop (,op 0 ,arg)))
|
(self.setBreakPoint)
|
||||||
|
(self.fork [
|
||||||
|
(lambda [:Dynamic self]
|
||||||
|
(print self.accumulator)
|
||||||
|
(when ,(ReaderExp.Symbol (Std.string (= inst "nop")))
|
||||||
|
(print (set self.forked true))
|
||||||
|
(set self.forkedAt self.instructionPointer))
|
||||||
|
(jmp (,op 0 ,arg) self))
|
||||||
|
(lambda [:Dynamic self]
|
||||||
|
(print self.accumulator)
|
||||||
|
(when ,(ReaderExp.Symbol (Std.string (= inst "jmp")))
|
||||||
|
(print (set self.forked true))
|
||||||
|
(set self.forkedAt self.instructionPointer))
|
||||||
|
(nop (,op 0 ,arg) self))
|
||||||
])))))
|
])))))
|
||||||
|
|
||||||
// Define the default reader LAST because default readers tend to break everything
|
// Define the default reader LAST because default readers tend to break everything
|
||||||
|
|||||||
@@ -79,14 +79,30 @@
|
|||||||
(assert (= 39645 (Bags.totalChildBags "shiny gold" parentMap)))
|
(assert (= 39645 (Bags.totalChildBags "shiny gold" parentMap)))
|
||||||
|
|
||||||
// Day 8
|
// Day 8
|
||||||
|
(print "BootCodeExample")
|
||||||
(let [example (new BootCodeExample)]
|
(let [example (new BootCodeExample)]
|
||||||
(example.setBreakHandler (lambda [example] (assert (= 5 .accumulator example))))
|
(example.setBreakHandler (lambda [example] (assert (= 5 .accumulator example))))
|
||||||
(example.run))
|
(example.run))
|
||||||
|
(print "BootCodeReal")
|
||||||
(let [bootCode (new BootCodeReal)]
|
(let [bootCode (new BootCodeReal)]
|
||||||
(bootCode.setBreakHandler (lambda [bootCode] (assert (= 2058 bootCode.accumulator) (assert true))))
|
(bootCode.setBreakHandler (lambda [bootCode] (assert (= 2058 (print bootCode.accumulator)))))
|
||||||
(bootCode.run))
|
(bootCode.run))
|
||||||
|
(print "BootCodeFixExample")
|
||||||
|
(let [bootCode (new BootCodeFixExample)]
|
||||||
|
(bootCode.setBreakHandler
|
||||||
|
(lambda [bootCodeFork]
|
||||||
|
(if (= bootCodeFork.instructionPointer (bootCodeFork.instructionCount))
|
||||||
|
(print (+ "answer could be " (Std.string bootCodeFork.accumulator) " forked at " (Std.string bootCodeFork.forkedAt)))
|
||||||
|
(print "hit an infinite loop"))))
|
||||||
|
(bootCode.addBreakPoint (bootCode.instructionCount))
|
||||||
|
(bootCode.run))
|
||||||
|
(print "BootCodeFixReal")
|
||||||
(let [bootCode (new BootCodeFix)]
|
(let [bootCode (new BootCodeFix)]
|
||||||
(bootCode.setBreakHandler (lambda [:BootCodeFix bootCodeFork] (when (= bootCodeFork.instructionPointer (bootCodeFork.instructionCount)) (print .accumulator (the BootCodeFix bootCodeFork)))))
|
(bootCode.setBreakHandler
|
||||||
|
(lambda [bootCodeFork]
|
||||||
|
(if (= bootCodeFork.instructionPointer (bootCodeFork.instructionCount))
|
||||||
|
(print (+ "answer could be " (Std.string bootCodeFork.accumulator) " forked at " (Std.string (+ 1 bootCodeFork.forkedAt))))
|
||||||
|
(print "hit an infinite loop"))))
|
||||||
(bootCode.addBreakPoint (bootCode.instructionCount))
|
(bootCode.addBreakPoint (bootCode.instructionCount))
|
||||||
(bootCode.run))
|
(bootCode.run))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user