Pass EmbeddedScript commands the script instance

This commit is contained in:
2020-12-11 17:31:31 -07:00
parent acd1289fe4
commit 219cadd040
9 changed files with 70 additions and 30 deletions

View File

@@ -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;
} }
]; ];

View File

@@ -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):

View File

@@ -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:

View File

@@ -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)]);
} }
} }

View File

@@ -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 {}

View File

@@ -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))))

View File

@@ -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))

View File

@@ -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

View File

@@ -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))