Enhanced EmbeddedScript error messages
This commit is contained in:
@@ -18,14 +18,18 @@ typedef Command = () -> Void;
|
|||||||
projects/aoc/year2020/BootCode.hx
|
projects/aoc/year2020/BootCode.hx
|
||||||
**/
|
**/
|
||||||
class EmbeddedScript {
|
class EmbeddedScript {
|
||||||
var instructionPointer = 0;
|
public var instructionPointer(default, null) = 0;
|
||||||
|
|
||||||
var running = false;
|
var running = false;
|
||||||
|
|
||||||
private var instructions:Array<Command> = null;
|
private var instructions:Array<Command> = null;
|
||||||
private var breakPoints:Map<Int, () -> Bool> = [];
|
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;
|
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({
|
classFields.push({
|
||||||
pos: PositionTools.make({
|
pos: PositionTools.make({
|
||||||
min: 0,
|
min: 0,
|
||||||
@@ -106,9 +129,9 @@ class EmbeddedScript {
|
|||||||
if (breakPoints.exists(instructionPointer) && breakPoints[instructionPointer]()) {
|
if (breakPoints.exists(instructionPointer) && breakPoints[instructionPointer]()) {
|
||||||
running = false;
|
running = false;
|
||||||
if (onBreak != null) {
|
if (onBreak != null) {
|
||||||
onBreak();
|
onBreak(this);
|
||||||
}
|
}
|
||||||
} else if (instructionPointer >= instructions.length) {
|
} else if (instructionPointer < 0 || instructionPointer >= instructions.length) {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,32 +187,59 @@ 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 {
|
public static function runAtCompileTime(exp:ReaderExp, k:KissState, ?args:Map<String, Dynamic>):Dynamic {
|
||||||
var code = k.convert(exp).toString(); // tink_macro to the rescue
|
var code = k.convert(exp).toString(); // tink_macro to the rescue
|
||||||
#if test
|
#if test
|
||||||
Prelude.print("Compile-time hscript: " + code);
|
Prelude.print("Compile-time hscript: " + code);
|
||||||
#end
|
#end
|
||||||
var parser = new Parser();
|
var parser = new Parser();
|
||||||
var interp = new Interp();
|
if (interps.length == 0) {
|
||||||
interp.variables.set("read", Reader.assertRead.bind(_, k.readTable));
|
var interp = new Interp();
|
||||||
interp.variables.set("readExpArray", Reader.readExpArray.bind(_, _, k.readTable));
|
interp.variables.set("read", Reader.assertRead.bind(_, k.readTable));
|
||||||
interp.variables.set("ReaderExp", ReaderExpDef);
|
interp.variables.set("readExpArray", Reader.readExpArray.bind(_, _, k.readTable));
|
||||||
interp.variables.set("nextToken", Reader.nextToken.bind(_, "a token"));
|
interp.variables.set("ReaderExp", ReaderExpDef);
|
||||||
interp.variables.set("kiss", {
|
interp.variables.set("nextToken", Reader.nextToken.bind(_, "a token"));
|
||||||
Reader: {
|
interp.variables.set("kiss", {
|
||||||
ReaderExpDef: ReaderExpDef
|
Reader: {
|
||||||
}
|
ReaderExpDef: ReaderExpDef
|
||||||
});
|
},
|
||||||
interp.variables.set("k", k);
|
Operand: {
|
||||||
interp.variables.set("args", args); // trippy
|
fromDynamic: Operand.fromDynamic
|
||||||
interp.variables.set("Helpers", Helpers);
|
}
|
||||||
interp.variables.set("Prelude", Prelude);
|
});
|
||||||
|
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) {
|
if (args != null) {
|
||||||
for (arg => value in args) {
|
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
|
#if test
|
||||||
Prelude.print("Compile-time value: " + Std.string(value));
|
Prelude.print("Compile-time value: " + Std.string(value));
|
||||||
#end
|
#end
|
||||||
@@ -254,8 +281,10 @@ class Helpers {
|
|||||||
throw CompileError.fromExp(innerExp, "unquote evaluated to null");
|
throw CompileError.fromExp(innerExp, "unquote evaluated to null");
|
||||||
} else if (Std.isOfType(unquoteValue, ReaderExpDef)) {
|
} else if (Std.isOfType(unquoteValue, ReaderExpDef)) {
|
||||||
unquoteValue;
|
unquoteValue;
|
||||||
} else {
|
} else if (Reflect.getProperty(unquoteValue, "def") != null) {
|
||||||
unquoteValue.def;
|
unquoteValue.def;
|
||||||
|
} else {
|
||||||
|
throw CompileError.fromExp(exp, "unquote didn't evaluate to a ReaderExp or ReaderExpDef");
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
throw CompileError.fromExp(exp, 'unquote evaluation not implemented');
|
throw CompileError.fromExp(exp, 'unquote evaluation not implemented');
|
||||||
|
|||||||
@@ -167,11 +167,10 @@ class Kiss {
|
|||||||
EField(convert(innerExp), field).withMacroPosOf(exp);
|
EField(convert(innerExp), field).withMacroPosOf(exp);
|
||||||
case KeyValueExp(keyExp, valueExp):
|
case KeyValueExp(keyExp, valueExp):
|
||||||
EBinop(OpArrow, convert(keyExp), convert(valueExp)).withMacroPosOf(exp);
|
EBinop(OpArrow, convert(keyExp), convert(valueExp)).withMacroPosOf(exp);
|
||||||
case Quasiquote(exp):
|
case Quasiquote(innerExp):
|
||||||
// TODO pass args here (including the recursive args value)
|
|
||||||
// This statement actually turns into an HScript expression before running
|
// This statement actually turns into an HScript expression before running
|
||||||
macro {
|
macro {
|
||||||
Helpers.evalUnquotes($v{exp}, k, args).def;
|
Helpers.evalUnquotes($v{innerExp}, k, args).def;
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
throw CompileError.fromExp(exp, 'conversion not implemented');
|
throw CompileError.fromExp(exp, 'conversion not implemented');
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ typedef Position = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Stream {
|
class Stream {
|
||||||
var content:String;
|
public var content(default, null):String;
|
||||||
|
|
||||||
var file:String;
|
var file:String;
|
||||||
var line:Int;
|
var line:Int;
|
||||||
var column:Int;
|
var column:Int;
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import kiss.Prelude;
|
|||||||
class DSLTestCase extends Test {
|
class DSLTestCase extends Test {
|
||||||
function testScript() {
|
function testScript() {
|
||||||
new DSLScript().run();
|
new DSLScript().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFork() {
|
||||||
new DSLScript().fork([() -> Assert.equals(5, 5), () -> Assert.equals(7, 7)]);
|
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"))
|
@: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.txt"))
|
||||||
|
class BootCodeFix extends EmbeddedScript {}
|
||||||
|
|||||||
@@ -4,4 +4,7 @@
|
|||||||
|
|
||||||
(defmethod nop [v] (setBreakPoint))
|
(defmethod nop [v] (setBreakPoint))
|
||||||
(defmethod acc [v] (setBreakPoint) (set accumulator (+ accumulator v)))
|
(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
|
// Day 8
|
||||||
(let [example (new BootCodeExample)]
|
(let [example (new BootCodeExample)]
|
||||||
(example.setBreakHandler (lambda [] (assert (= 5 example.accumulator))))
|
(example.setBreakHandler (lambda [example] (assert (= 5 .accumulator example))))
|
||||||
(example.run))
|
(example.run))
|
||||||
(let [bootCode (new BootCodeReal)]
|
(let [bootCode (new BootCodeReal)]
|
||||||
(bootCode.setBreakHandler (lambda [] (assert (= 2058 bootCode.accumulator) (assert true))))
|
(bootCode.setBreakHandler (lambda [bootCode] (assert (= 2058 bootCode.accumulator) (assert true))))
|
||||||
(bootCode.run)))
|
(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