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.
|
||||
// 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.push(field);
|
||||
}
|
||||
classFields = classFields.concat(Kiss.build(dslFile, k));
|
||||
|
||||
Reader.readAndProcess(Stream.fromFile(scriptFile), k, (nextExp) -> {
|
||||
var field = Kiss.readerExpToField(nextExp, k, false);
|
||||
if (field != null) {
|
||||
classFields.push(field);
|
||||
var fields = Kiss.readerExpToFields(nextExp, k, false);
|
||||
if (fields.length > 0) {
|
||||
classFields = classFields.concat(fields);
|
||||
} else {
|
||||
// In a DSL script, anything that's not a field definition is a command line
|
||||
commandList.push(macro function(self) {
|
||||
${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?
|
||||
// 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
|
||||
|
@@ -124,18 +124,18 @@ class Kiss {
|
||||
throw CompileError.fromExp(loadArgs[0], "only argument to load should be a string literal");
|
||||
}
|
||||
default:
|
||||
var field = readerExpToField(nextExp, k);
|
||||
if (field != null) {
|
||||
var fields = readerExpToFields(nextExp, k);
|
||||
#if test
|
||||
for (field in fields) {
|
||||
switch (field.kind) {
|
||||
case FVar(_, expr) | FFun({ret: _, args: _, expr: expr}):
|
||||
Sys.println(expr.toString());
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
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)):
|
||||
trace(mac);
|
||||
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)):
|
||||
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)):
|
||||
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)):
|
||||
fieldForms[formName](exp, args, k);
|
||||
[fieldForms[formName](exp, args, k)];
|
||||
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 [];
|
||||
};
|
||||
}
|
||||
|
||||
|
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