Allow macros to define more than one field

This commit is contained in:
2021-04-02 12:32:58 -06:00
parent a75dd13903
commit 1e3fe69bb4
5 changed files with 50 additions and 19 deletions

View File

@@ -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

View File

@@ -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 [];
};
}

View 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());
}
}

View File

@@ -0,0 +1,6 @@
(defmacro defMultiple [varName funcName]
`{
(defvar ,varName 5)
(defun ,funcName [] 6)})
(defMultiple myVar myFunc)