Allow macros to define more than one field
This commit is contained in:
@@ -56,20 +56,20 @@ class EmbeddedScript {
|
|||||||
|
|
||||||
// This brings in the DSL's functions and global variables.
|
// This brings in the DSL's functions and global variables.
|
||||||
// As a side-effect, it also fills the KissState with the macros and reader macros that make the DSL syntax
|
// As a side-effect, it also fills the KissState with the macros and reader macros that make the DSL syntax
|
||||||
for (field in Kiss.build(dslFile, k)) {
|
classFields = classFields.concat(Kiss.build(dslFile, k));
|
||||||
classFields.push(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reader.readAndProcess(Stream.fromFile(scriptFile), k, (nextExp) -> {
|
Reader.readAndProcess(Stream.fromFile(scriptFile), k, (nextExp) -> {
|
||||||
var field = Kiss.readerExpToField(nextExp, k, false);
|
var fields = Kiss.readerExpToFields(nextExp, k, false);
|
||||||
if (field != null) {
|
if (fields.length > 0) {
|
||||||
classFields.push(field);
|
classFields = classFields.concat(fields);
|
||||||
} else {
|
} else {
|
||||||
// In a DSL script, anything that's not a field definition is a command line
|
// In a DSL script, anything that's not a field definition is a command line
|
||||||
commandList.push(macro function(self) {
|
commandList.push(macro function(self) {
|
||||||
${Kiss.readerExpToHaxeExpr(nextExp, k)};
|
${Kiss.readerExpToHaxeExpr(nextExp, k)};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// This return is essential for type unification of concat() and push() above... ugh.
|
||||||
|
return;
|
||||||
// TODO also allow label setting and multiple commands coming from the same expr?
|
// TODO also allow label setting and multiple commands coming from the same expr?
|
||||||
// Multiple things could come from the same expr by returning begin, or a call to a function that does more stuff
|
// Multiple things could come from the same expr by returning begin, or a call to a function that does more stuff
|
||||||
// i.e. knot declarations need to end the previous knot, and BELOW that set a label for the new one, then increment the read count
|
// i.e. knot declarations need to end the previous knot, and BELOW that set a label for the new one, then increment the read count
|
||||||
|
@@ -124,18 +124,18 @@ class Kiss {
|
|||||||
throw CompileError.fromExp(loadArgs[0], "only argument to load should be a string literal");
|
throw CompileError.fromExp(loadArgs[0], "only argument to load should be a string literal");
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
var field = readerExpToField(nextExp, k);
|
var fields = readerExpToFields(nextExp, k);
|
||||||
if (field != null) {
|
#if test
|
||||||
#if test
|
for (field in fields) {
|
||||||
switch (field.kind) {
|
switch (field.kind) {
|
||||||
case FVar(_, expr) | FFun({ret: _, args: _, expr: expr}):
|
case FVar(_, expr) | FFun({ret: _, args: _, expr: expr}):
|
||||||
Sys.println(expr.toString());
|
Sys.println(expr.toString());
|
||||||
default:
|
default:
|
||||||
throw CompileError.fromExp(nextExp, 'cannot print the expression of generated field $field');
|
throw CompileError.fromExp(nextExp, 'cannot print the expression of generated field $field');
|
||||||
}
|
}
|
||||||
#end
|
|
||||||
classFields.push(field);
|
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
|
classFields = classFields.concat(fields);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ class Kiss {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readerExpToField(exp:ReaderExp, k:KissState, errorIfNot = true):Null<Field> {
|
public static function readerExpToFields(exp:ReaderExp, k:KissState, errorIfNot = true):Array<Field> {
|
||||||
var fieldForms = k.fieldForms;
|
var fieldForms = k.fieldForms;
|
||||||
|
|
||||||
// Macros at top-level are allowed if they expand into a fieldform, or null like defreadermacro
|
// Macros at top-level are allowed if they expand into a fieldform, or null like defreadermacro
|
||||||
@@ -168,19 +168,27 @@ class Kiss {
|
|||||||
var identAliases = k.identAliases;
|
var identAliases = k.identAliases;
|
||||||
|
|
||||||
return switch (exp.def) {
|
return switch (exp.def) {
|
||||||
|
// Multiple field/macro definitions wrapped in `begin` are acceptable
|
||||||
|
case CallExp({pos: _, def: Symbol(mac)}, args) if (mac == "begin"):
|
||||||
|
var fields = [];
|
||||||
|
for (arg in args) {
|
||||||
|
fields = fields.concat(readerExpToFields(arg, k, errorIfNot));
|
||||||
|
}
|
||||||
|
fields;
|
||||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||||
|
trace(mac);
|
||||||
var expandedExp = macros[mac](exp, args, k);
|
var expandedExp = macros[mac](exp, args, k);
|
||||||
if (expandedExp != null) readerExpToField(expandedExp, k, errorIfNot) else null;
|
if (expandedExp != null) readerExpToFields(expandedExp, k, errorIfNot) else [];
|
||||||
case CallExp({pos: _, def: Symbol(alias)}, args) if (callAliases.exists(alias)):
|
case CallExp({pos: _, def: Symbol(alias)}, args) if (callAliases.exists(alias)):
|
||||||
var aliasedExp = CallExp(callAliases[alias].withPosOf(exp), args).withPosOf(exp);
|
var aliasedExp = CallExp(callAliases[alias].withPosOf(exp), args).withPosOf(exp);
|
||||||
readerExpToField(aliasedExp, k, errorIfNot);
|
readerExpToFields(aliasedExp, k, errorIfNot);
|
||||||
case CallExp({pos: _, def: Symbol(alias)}, args) if (identAliases.exists(alias)):
|
case CallExp({pos: _, def: Symbol(alias)}, args) if (identAliases.exists(alias)):
|
||||||
var aliasedExp = CallExp(identAliases[alias].withPosOf(exp), args).withPosOf(exp);
|
var aliasedExp = CallExp(identAliases[alias].withPosOf(exp), args).withPosOf(exp);
|
||||||
readerExpToField(aliasedExp, k, errorIfNot);
|
readerExpToFields(aliasedExp, k, errorIfNot);
|
||||||
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
||||||
fieldForms[formName](exp, args, k);
|
[fieldForms[formName](exp, args, k)];
|
||||||
default:
|
default:
|
||||||
if (errorIfNot) throw CompileError.fromExp(exp, 'top-level expressions must be (or expand into) field definitions'); else return null;
|
if (errorIfNot) throw CompileError.fromExp(exp, 'top-level expressions must be (or expand into) field or macro definitions'); else [];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -187,8 +187,8 @@ class SpecialForms {
|
|||||||
map["lambda"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
map["lambda"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||||
wholeExp.checkNumArgs(2, null, "(lambda [[argsNames...]] [body...])");
|
wholeExp.checkNumArgs(2, null, "(lambda [[argsNames...]] [body...])");
|
||||||
var returnsValue = switch (args[0].def) {
|
var returnsValue = switch (args[0].def) {
|
||||||
case TypedExp("Void", argNames):
|
case TypedExp("Void", argNames):
|
||||||
args[0] = argNames;
|
args[0] = argNames;
|
||||||
false;
|
false;
|
||||||
default:
|
default:
|
||||||
true;
|
true;
|
||||||
|
17
src/test/cases/MacroTestCase.hx
Normal file
17
src/test/cases/MacroTestCase.hx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package test.cases;
|
||||||
|
|
||||||
|
import utest.Test;
|
||||||
|
import utest.Assert;
|
||||||
|
import kiss.Prelude;
|
||||||
|
import kiss.List;
|
||||||
|
import haxe.ds.Option;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
@:build(kiss.Kiss.build("kiss/src/test/cases/MacroTestCase.kiss"))
|
||||||
|
class MacroTestCase extends Test {
|
||||||
|
function testMultipleFieldForms() {
|
||||||
|
Assert.equals(5, myVar);
|
||||||
|
Assert.equals(6, myFunc());
|
||||||
|
}
|
||||||
|
}
|
6
src/test/cases/MacroTestCase.kiss
Normal file
6
src/test/cases/MacroTestCase.kiss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
(defmacro defMultiple [varName funcName]
|
||||||
|
`{
|
||||||
|
(defvar ,varName 5)
|
||||||
|
(defun ,funcName [] 6)})
|
||||||
|
|
||||||
|
(defMultiple myVar myFunc)
|
Reference in New Issue
Block a user