Enhanced EmbeddedScript error messages
This commit is contained in:
@@ -18,14 +18,18 @@ typedef Command = () -> Void;
|
||||
projects/aoc/year2020/BootCode.hx
|
||||
**/
|
||||
class EmbeddedScript {
|
||||
var instructionPointer = 0;
|
||||
public var instructionPointer(default, null) = 0;
|
||||
|
||||
var running = false;
|
||||
|
||||
private var instructions:Array<Command> = null;
|
||||
private var breakPoints:Map<Int, () -> Bool> = [];
|
||||
private var onBreak:() -> Void = null;
|
||||
// Break handlers accept a Dynamic argument because when fork() happens
|
||||
// (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:() -> Void) {
|
||||
public function setBreakHandler(handler:(Dynamic) -> Void) {
|
||||
onBreak = handler;
|
||||
}
|
||||
|
||||
@@ -87,6 +91,25 @@ class EmbeddedScript {
|
||||
})
|
||||
});
|
||||
|
||||
classFields.push({
|
||||
pos: PositionTools.make({
|
||||
min: 0,
|
||||
max: File.getContent(scriptFile).length,
|
||||
file: scriptFile
|
||||
}),
|
||||
name: "instructionCount",
|
||||
access: [APublic],
|
||||
kind: FFun({
|
||||
ret: null,
|
||||
args: [],
|
||||
expr: macro {
|
||||
if (instructions == null)
|
||||
resetInstructions();
|
||||
return instructions.length;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
classFields.push({
|
||||
pos: PositionTools.make({
|
||||
min: 0,
|
||||
@@ -106,9 +129,9 @@ class EmbeddedScript {
|
||||
if (breakPoints.exists(instructionPointer) && breakPoints[instructionPointer]()) {
|
||||
running = false;
|
||||
if (onBreak != null) {
|
||||
onBreak();
|
||||
onBreak(this);
|
||||
}
|
||||
} else if (instructionPointer >= instructions.length) {
|
||||
} else if (instructionPointer < 0 || instructionPointer >= instructions.length) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +187,18 @@ class Helpers {
|
||||
}
|
||||
}
|
||||
|
||||
// This stack will contain multiple references to the same interp--to count how many layers deep it is.
|
||||
// This stack is like top in Inception. When empty, it proves that we're not running at compiletime yet.
|
||||
// When we ARE running at compiletime already, the pre-existing interp will be used
|
||||
static var interps:kiss.List<Interp> = [];
|
||||
|
||||
public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map<String, Dynamic>):Dynamic {
|
||||
var code = k.convert(exp).toString(); // tink_macro to the rescue
|
||||
#if test
|
||||
Prelude.print("Compile-time hscript: " + code);
|
||||
#end
|
||||
var parser = new Parser();
|
||||
if (interps.length == 0) {
|
||||
var interp = new Interp();
|
||||
interp.variables.set("read", Reader.assertRead.bind(_, k.readTable));
|
||||
interp.variables.set("readExpArray", Reader.readExpArray.bind(_, _, k.readTable));
|
||||
@@ -201,18 +207,39 @@ class Helpers {
|
||||
interp.variables.set("kiss", {
|
||||
Reader: {
|
||||
ReaderExpDef: ReaderExpDef
|
||||
},
|
||||
Operand: {
|
||||
fromDynamic: Operand.fromDynamic
|
||||
}
|
||||
});
|
||||
interp.variables.set("k", k);
|
||||
interp.variables.set("args", args); // trippy
|
||||
interp.variables.set("k", k.forCaseParsing());
|
||||
interp.variables.set("Helpers", Helpers);
|
||||
interp.variables.set("Prelude", Prelude);
|
||||
interp.variables.set("Std", Std);
|
||||
|
||||
interps.push(interp);
|
||||
} else {
|
||||
interps.push(interps[-1]);
|
||||
}
|
||||
var parsed = parser.parseString(code);
|
||||
|
||||
// TODO if an internal evaluation ever needs to end before its outer evaluation is done,
|
||||
// this will cause problems because the old args will be overwritten and lost
|
||||
interps[-1].variables.set("args", args); // trippy
|
||||
if (args != null) {
|
||||
for (arg => value in args) {
|
||||
interp.variables.set(arg, value);
|
||||
interps[-1].variables.set(arg, value);
|
||||
}
|
||||
}
|
||||
var value = interp.execute(parser.parseString(code));
|
||||
var value = if (interps.length == 1) {
|
||||
interps[-1].execute(parsed);
|
||||
} else {
|
||||
interps[-1].expr(parsed);
|
||||
};
|
||||
interps.pop();
|
||||
if (value == null) {
|
||||
throw CompileError.fromExp(exp, "compile-time evaluation returned null");
|
||||
}
|
||||
#if test
|
||||
Prelude.print("Compile-time value: " + Std.string(value));
|
||||
#end
|
||||
@@ -254,8 +281,10 @@ class Helpers {
|
||||
throw CompileError.fromExp(innerExp, "unquote evaluated to null");
|
||||
} else if (Std.isOfType(unquoteValue, ReaderExpDef)) {
|
||||
unquoteValue;
|
||||
} else {
|
||||
} else if (Reflect.getProperty(unquoteValue, "def") != null) {
|
||||
unquoteValue.def;
|
||||
} else {
|
||||
throw CompileError.fromExp(exp, "unquote didn't evaluate to a ReaderExp or ReaderExpDef");
|
||||
};
|
||||
default:
|
||||
throw CompileError.fromExp(exp, 'unquote evaluation not implemented');
|
||||
|
||||
@@ -167,11 +167,10 @@ class Kiss {
|
||||
EField(convert(innerExp), field).withMacroPosOf(exp);
|
||||
case KeyValueExp(keyExp, valueExp):
|
||||
EBinop(OpArrow, convert(keyExp), convert(valueExp)).withMacroPosOf(exp);
|
||||
case Quasiquote(exp):
|
||||
// TODO pass args here (including the recursive args value)
|
||||
case Quasiquote(innerExp):
|
||||
// This statement actually turns into an HScript expression before running
|
||||
macro {
|
||||
Helpers.evalUnquotes($v{exp}, k, args).def;
|
||||
Helpers.evalUnquotes($v{innerExp}, k, args).def;
|
||||
};
|
||||
default:
|
||||
throw CompileError.fromExp(exp, 'conversion not implemented');
|
||||
|
||||
@@ -14,7 +14,8 @@ typedef Position = {
|
||||
};
|
||||
|
||||
class Stream {
|
||||
var content:String;
|
||||
public var content(default, null):String;
|
||||
|
||||
var file:String;
|
||||
var line:Int;
|
||||
var column:Int;
|
||||
|
||||
@@ -8,6 +8,9 @@ import kiss.Prelude;
|
||||
class DSLTestCase extends Test {
|
||||
function testScript() {
|
||||
new DSLScript().run();
|
||||
}
|
||||
|
||||
function testFork() {
|
||||
new DSLScript().fork([() -> Assert.equals(5, 5), () -> Assert.equals(7, 7)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,6 @@ class BootCodeExample extends EmbeddedScript {}
|
||||
|
||||
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeDSL.kiss", "src/year2020/inputs/day8.txt"))
|
||||
class BootCodeReal extends EmbeddedScript {}
|
||||
|
||||
@:build(kiss.EmbeddedScript.build("src/year2020/BootCodeFixDSL.kiss", "src/year2020/inputs/day8.txt"))
|
||||
class BootCodeFix extends EmbeddedScript {}
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
|
||||
(defmethod nop [v] (setBreakPoint))
|
||||
(defmethod acc [v] (setBreakPoint) (set accumulator (+ accumulator v)))
|
||||
(defmethod jmp [v] (setBreakPoint) (set instructionPointer (+ instructionPointer (- v 1))))
|
||||
(defmethod jmp [v]
|
||||
(setBreakPoint)
|
||||
//(print (+ "jumping " (Std.string v) " from " (Std.string instructionPointer)))
|
||||
(set instructionPointer (+ instructionPointer (- v 1))))
|
||||
26
projects/aoc/src/year2020/BootCodeFixDSL.kiss
Normal file
26
projects/aoc/src/year2020/BootCodeFixDSL.kiss
Normal file
@@ -0,0 +1,26 @@
|
||||
(load "BootCodeCommon.kiss")
|
||||
|
||||
(defvar &mut forked false)
|
||||
|
||||
(defreadermacro ["jmp" "nop"] [stream]
|
||||
(let [inst
|
||||
(nextToken stream)
|
||||
instSymbol
|
||||
(ReaderExp.Symbol inst)
|
||||
op
|
||||
(begin (stream.dropWhitespace) (ReaderExp.Symbol
|
||||
(begin (stream.dropWhitespace) (stream.expect "+/-" (lambda [] (stream.takeChars 1))))))
|
||||
arg
|
||||
(ReaderExp.Symbol (nextToken stream))]
|
||||
(stream.dropWhitespace)
|
||||
`(if forked
|
||||
(,instSymbol (,op 0 ,arg))
|
||||
(begin
|
||||
|
||||
(fork [
|
||||
(lambda [] (when ,(ReaderExp.Symbol (Std.string (= inst "nop"))) (set forked true)) (jmp (,op 0 ,arg)))
|
||||
(lambda [] (when ,(ReaderExp.Symbol (Std.string (= inst "jmp"))) (set forked true)) (nop (,op 0 ,arg)))
|
||||
])))))
|
||||
|
||||
// Define the default reader LAST because default readers tend to break everything
|
||||
(load "BootCodeDSL.kiss")
|
||||
@@ -80,9 +80,14 @@
|
||||
|
||||
// Day 8
|
||||
(let [example (new BootCodeExample)]
|
||||
(example.setBreakHandler (lambda [] (assert (= 5 example.accumulator))))
|
||||
(example.setBreakHandler (lambda [example] (assert (= 5 .accumulator example))))
|
||||
(example.run))
|
||||
(let [bootCode (new BootCodeReal)]
|
||||
(bootCode.setBreakHandler (lambda [] (assert (= 2058 bootCode.accumulator) (assert true))))
|
||||
(bootCode.run)))
|
||||
(bootCode.setBreakHandler (lambda [bootCode] (assert (= 2058 bootCode.accumulator) (assert true))))
|
||||
(bootCode.run))
|
||||
(let [bootCode (new BootCodeFix)]
|
||||
(bootCode.setBreakHandler (lambda [:BootCodeFix bootCodeFork] (when (= bootCodeFork.instructionPointer (bootCodeFork.instructionCount)) (print .accumulator (the BootCodeFix bootCodeFork)))))
|
||||
(bootCode.addBreakPoint (bootCode.instructionCount))
|
||||
(bootCode.run))
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user