throwCompileError in expBuilder
This commit is contained in:
@@ -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<ReaderExp>, message:String) {
|
||||
return new CompileError(exps, message);
|
||||
}
|
||||
|
@@ -558,6 +558,21 @@ class Helpers {
|
||||
function list(exps:Array<ReaderExp>) {
|
||||
return ListExp(exps).withPosOf(posRef);
|
||||
}
|
||||
function objectWith (bindings:Array<ReaderExp>, captures:Array<ReaderExp>) {
|
||||
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<ReaderExp>) -> callSymbol("begin", exps),
|
||||
let: (bindings:Array<ReaderExp>, body:Array<ReaderExp>) -> 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)
|
||||
};
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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", {});
|
||||
}
|
||||
|
@@ -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');
|
||||
};
|
||||
};
|
||||
|
@@ -46,6 +46,10 @@ class MacroTestCase extends Test {
|
||||
_testSetMacroVar();
|
||||
}
|
||||
|
||||
function testRedefineMacroVar() {
|
||||
_testRedefineMacroVar();
|
||||
}
|
||||
|
||||
function testTryCatchWithoutDynamic () {
|
||||
_testTryCatchWithoutDynamic();
|
||||
}
|
||||
|
@@ -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))
|
||||
|
Reference in New Issue
Block a user