\#if conditional compilation macro
This commit is contained in:
@@ -11,10 +11,14 @@ import kiss.Prelude;
|
|||||||
* macro definitions.
|
* macro definitions.
|
||||||
*/
|
*/
|
||||||
class KissInterp extends Interp {
|
class KissInterp extends Interp {
|
||||||
|
var nullForUnknownVar:Bool;
|
||||||
|
|
||||||
// TODO standardize this with KissConfig.prepareInterp
|
// TODO standardize this with KissConfig.prepareInterp
|
||||||
public function new() {
|
public function new(nullForUnknownVar = false) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.nullForUnknownVar = nullForUnknownVar;
|
||||||
|
|
||||||
variables.set("Prelude", Prelude);
|
variables.set("Prelude", Prelude);
|
||||||
variables.set("Lambda", Lambda);
|
variables.set("Lambda", Lambda);
|
||||||
variables.set("Std", Std);
|
variables.set("Std", Std);
|
||||||
@@ -23,6 +27,18 @@ class KissInterp extends Interp {
|
|||||||
variables.set("Throw", ExtraElementHandling.Throw);
|
variables.set("Throw", ExtraElementHandling.Throw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override function resolve(id:String):Dynamic {
|
||||||
|
if (nullForUnknownVar) {
|
||||||
|
return try {
|
||||||
|
super.resolve(id);
|
||||||
|
} catch (e:Dynamic) {
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return super.resolve(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override function exprReturn(e):Dynamic {
|
override function exprReturn(e):Dynamic {
|
||||||
// the default exprReturn() contains a try-catch which, though it is important, hides very important macroexpansion callstacks sometimes
|
// the default exprReturn() contains a try-catch which, though it is important, hides very important macroexpansion callstacks sometimes
|
||||||
#if macrotest
|
#if macrotest
|
||||||
|
@@ -8,6 +8,7 @@ import kiss.Kiss;
|
|||||||
import kiss.CompileError;
|
import kiss.CompileError;
|
||||||
import uuid.Uuid;
|
import uuid.Uuid;
|
||||||
import sys.io.Process;
|
import sys.io.Process;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
using kiss.Kiss;
|
using kiss.Kiss;
|
||||||
using kiss.Reader;
|
using kiss.Reader;
|
||||||
@@ -171,6 +172,32 @@ class Macros {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Conditional compilation is all based on this macro:
|
||||||
|
macros["#if"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k) -> {
|
||||||
|
wholeExp.checkNumArgs(2, 3, '(#if [cond] [then] [?else])');
|
||||||
|
|
||||||
|
var conditionExp = exps.shift();
|
||||||
|
var thenExp = exps.shift();
|
||||||
|
var elseExp = if (exps.length > 0) exps.shift(); else null;
|
||||||
|
|
||||||
|
var parser = new Parser();
|
||||||
|
var conditionInterp = new KissInterp(true);
|
||||||
|
var conditionStr = Reader.toString(conditionExp.def);
|
||||||
|
var conditionHScript = parser.parseString(Prelude.convertToHScript(conditionStr));
|
||||||
|
for (flag => value in Context.getDefines()) {
|
||||||
|
conditionInterp.variables.set(flag, value);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return if (Prelude.truthy(conditionInterp.execute(conditionHScript))) {
|
||||||
|
thenExp;
|
||||||
|
} else {
|
||||||
|
elseExp;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw CompileError.fromExp(conditionExp, 'condition for #if threw error $e');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function bodyIf(formName:String, negated:Bool, wholeExp:ReaderExp, args:Array<ReaderExp>, k) {
|
function bodyIf(formName:String, negated:Bool, wholeExp:ReaderExp, args:Array<ReaderExp>, k) {
|
||||||
wholeExp.checkNumArgs(2, null, '($formName [condition] [body...])');
|
wholeExp.checkNumArgs(2, null, '($formName [condition] [body...])');
|
||||||
var b = wholeExp.expBuilder();
|
var b = wholeExp.expBuilder();
|
||||||
|
@@ -36,6 +36,16 @@ class Reader {
|
|||||||
readTable['"'] = readString;
|
readTable['"'] = readString;
|
||||||
readTable["#"] = readRawString;
|
readTable["#"] = readRawString;
|
||||||
|
|
||||||
|
// Special symbols that wouldn't read as symbols, but should:
|
||||||
|
function forceSymbol(sym:String) {
|
||||||
|
readTable[sym] = (stream, k) -> Symbol(sym);
|
||||||
|
}
|
||||||
|
forceSymbol("#if");
|
||||||
|
forceSymbol("#when");
|
||||||
|
forceSymbol("#unless");
|
||||||
|
forceSymbol("#cond");
|
||||||
|
forceSymbol("#case");
|
||||||
|
|
||||||
readTable["/*"] = (stream, k) -> {
|
readTable["/*"] = (stream, k) -> {
|
||||||
stream.takeUntilAndDrop("*/");
|
stream.takeUntilAndDrop("*/");
|
||||||
null;
|
null;
|
||||||
@@ -314,7 +324,7 @@ class Reader {
|
|||||||
case '"':
|
case '"':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
stream.error('Invalid syntax for raw string. Delete $next');
|
stream.error('Invalid syntax for raw string.');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
|
20
src/test/cases/ConditionalCompilationTestCase.hx
Normal file
20
src/test/cases/ConditionalCompilationTestCase.hx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package test.cases;
|
||||||
|
|
||||||
|
import utest.Assert;
|
||||||
|
import utest.Test;
|
||||||
|
import kiss.Prelude;
|
||||||
|
|
||||||
|
@:build(kiss.Kiss.build())
|
||||||
|
class ConditionalCompilationTestCase extends Test {
|
||||||
|
function testIf() {
|
||||||
|
#if interp
|
||||||
|
Assert.isTrue(runningInHaxe);
|
||||||
|
#else
|
||||||
|
Assert.isFalse(runningInHaxe);
|
||||||
|
#end
|
||||||
|
|
||||||
|
#if (py || js)
|
||||||
|
Assert.isTrue(runningInPyOrJs);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
3
src/test/cases/ConditionalCompilationTestCase.kiss
Normal file
3
src/test/cases/ConditionalCompilationTestCase.kiss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
(defvar runningInHaxe (#if interp true false))
|
||||||
|
(defvar runningInPyOrJs (#if (or py js) true false))
|
||||||
|
|
Reference in New Issue
Block a user