From db3402f885a28354ca4db5764d03d8000c8ffc5f Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Wed, 10 Nov 2021 15:45:04 -0700 Subject: [PATCH] throwCompileError in expBuilder --- src/kiss/CompileError.hx | 9 ++++++++ src/kiss/Helpers.hx | 38 +++++++++++++++++++++++++++++-- src/kiss/Kiss.hx | 17 +++++++++++--- src/kiss/KissInterp.hx | 4 ++++ src/kiss/Macros.hx | 4 +++- src/test/cases/MacroTestCase.hx | 4 ++++ src/test/cases/MacroTestCase.kiss | 10 ++++++++ 7 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/kiss/CompileError.hx b/src/kiss/CompileError.hx index ea1eac5..69441e0 100644 --- a/src/kiss/CompileError.hx +++ b/src/kiss/CompileError.hx @@ -19,6 +19,15 @@ class CompileError { return new CompileError([exp], message); } + public static function fromExpStr(pos:Position, expStr:String, message:String) { + switch (Reader.read(Stream.fromString(expStr), Kiss.defaultKissState())) { + case Some(exp): + return fromExp({pos: pos, def: exp.def}, message); + default: + throw 'bad'; // TODO better message + } + } + public static function fromArgs(exps:Array, message:String) { return new CompileError(exps, message); } diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index 539d02f..ed640e3 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -558,6 +558,21 @@ class Helpers { function list(exps:Array) { return ListExp(exps).withPosOf(posRef); } + function objectWith (bindings:Array, captures:Array) { + return callSymbol("objectWith", [list(bindings)].concat(captures)); + } + function str(s:String) { + return StrExp(s).withPosOf(posRef); + } + function raw(code:String) { + return RawHaxe(code).withPosOf(posRef); + } + function int(v:Int) { + return _symbol(Std.string(v)); + } + function float(v:Float) { + return _symbol(Std.string(v)); + } return { call: call, callSymbol: callSymbol, @@ -565,15 +580,34 @@ class Helpers { print: (arg:ReaderExp) -> CallExp(Symbol("print").withPosOf(posRef), [arg]).withPosOf(posRef), the: (type:ReaderExp, value:ReaderExp) -> callSymbol("the", [type, value]), list: list, - str: (s:String) -> StrExp(s).withPosOf(posRef), + str: str, symbol: _symbol, - raw: (code:String) -> RawHaxe(code).withPosOf(posRef), + int: int, + float: float, + raw: raw, typed: (path:String, exp:ReaderExp) -> TypedExp(path, exp).withPosOf(posRef), meta: (m:String, exp:ReaderExp) -> MetaExp(m, exp).withPosOf(posRef), field: field, keyValue: (key:ReaderExp, value:ReaderExp) -> KeyValueExp(key, value).withPosOf(posRef), begin: (exps:Array) -> callSymbol("begin", exps), let: (bindings:Array, body:Array) -> callSymbol("let", [list(bindings)].concat(body)), + objectWith: objectWith, + throwCompileError: (reason:String) -> { + callSymbol("throw", [ + callSymbol("CompileError.fromExpStr", [ + // pos + objectWith([ + _symbol("file"), str(posRef.pos.file), + _symbol("line"), int(posRef.pos.line), + _symbol("column"), int(posRef.pos.column), + _symbol("absoluteChar"), int(posRef.pos.absoluteChar), + ], []), + // expStr + str(Reader.toString(posRef.def)), + str(reason) + ]) + ]); + }, none: () -> None.withPosOf(posRef) }; } diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index 3844d84..b8a3ebc 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -376,7 +376,8 @@ class Kiss { static function disableMacro(copy:KissState, m:String, reason:String) { copy.macros[m] = (wholeExp:ReaderExp, exps, k) -> { var b = wholeExp.expBuilder(); - b.callSymbol("throw", [b.str('$m is unavailable in macros because $reason')]); + // have this throw during macroEXPANSION, not before (so assertThrows will catch it) + b.throwCompileError('$m is unavailable in macros because $reason'); }; } @@ -399,8 +400,18 @@ class Kiss { public static function forMacroEval(k:KissState): KissState { var copy = k.forHScript(); - // Disable macros that will cause problems in macro evaluation: - disableMacro(copy, "set", "you don't want your macros to be stateful"); + // Catch accidental misuse of (set) on macroVars + var setLocal = copy.specialForms["set"]; + copy.specialForms["set"] = (wholeExp:ReaderExp, exps, k:KissState) -> { + switch (exps[0].def) { + case Symbol(varName) if (k.macroVars.exists(varName)): + var b = wholeExp.expBuilder(); + // have this throw during macroEXPANSION, not before (so assertThrows will catch it) + copy.convert(b.throwCompileError('If you intend to change macroVar $varName, use defMacroVar instead. If not, rename your local variable for clarity.')); + default: + setLocal(wholeExp, exps, copy); + }; + }; return copy; } diff --git a/src/kiss/KissInterp.hx b/src/kiss/KissInterp.hx index ab02268..d9dd155 100644 --- a/src/kiss/KissInterp.hx +++ b/src/kiss/KissInterp.hx @@ -44,6 +44,10 @@ class KissInterp extends Interp { variables.set("Http", sys.Http); #end + #if macro + variables.set("CompileError", kiss.CompileError); + #end + // Might eventually need to simulate types in the namespace: variables.set("kiss", {}); } diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index aeeee95..e9d5866 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -470,7 +470,9 @@ class Macros { try { // Return the macro expansion: return Helpers.runAtCompileTime(b.callSymbol("begin", exps.slice(2)), k, args); - } catch (error) { + } catch (error:CompileError) { + throw error; + } catch (error:Dynamic) { throw CompileError.fromExp(wholeExp, 'Macro expansion error: $error'); }; }; diff --git a/src/test/cases/MacroTestCase.hx b/src/test/cases/MacroTestCase.hx index d35aea6..f4985e2 100644 --- a/src/test/cases/MacroTestCase.hx +++ b/src/test/cases/MacroTestCase.hx @@ -46,6 +46,10 @@ class MacroTestCase extends Test { _testSetMacroVar(); } + function testRedefineMacroVar() { + _testRedefineMacroVar(); + } + function testTryCatchWithoutDynamic () { _testTryCatchWithoutDynamic(); } diff --git a/src/test/cases/MacroTestCase.kiss b/src/test/cases/MacroTestCase.kiss index 3941637..b30e7aa 100644 --- a/src/test/cases/MacroTestCase.kiss +++ b/src/test/cases/MacroTestCase.kiss @@ -60,6 +60,7 @@ (_testPrintAtMacroTimeMacro) (Assert.pass)) +// Calling (set) on a macroVar is a faux-pas, but calling defMacroVar again is not (defMacroVar count 0) (defMacro _testSetMacroVarMacro [] (assertThrows (set count (+ count 1))) @@ -69,6 +70,15 @@ (_testSetMacroVarMacro) (Assert.pass)) +(defMacro _testRedefineMacroVarMacro [] + (defMacroVar count (+ count 1)) + (symbol (Std.string count))) + +(function _testRedefineMacroVar [] + (Assert.equals 1 (_testRedefineMacroVarMacro)) + (Assert.equals 2 (_testRedefineMacroVarMacro))) + + // ifLet and its derivatives should be disabled in defMacro bodies: (defMacro _testIfLetDisabledMacro [] (assertThrows (ifLet [a "b"] a))