From b759c71a5b532d8d042f61d79a6510956768cca7 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Tue, 27 Apr 2021 19:08:34 -0600 Subject: [PATCH] Refactor to allow field forms anywhere --- kiss/src/kiss/EmbeddedScript.hx | 11 ++- kiss/src/kiss/Kiss.hx | 121 ++++++++++++-------------------- kiss/src/kiss/Macros.hx | 13 ++++ 3 files changed, 61 insertions(+), 84 deletions(-) diff --git a/kiss/src/kiss/EmbeddedScript.hx b/kiss/src/kiss/EmbeddedScript.hx index a7310c29..1f0a9d1e 100644 --- a/kiss/src/kiss/EmbeddedScript.hx +++ b/kiss/src/kiss/EmbeddedScript.hx @@ -63,15 +63,14 @@ class EmbeddedScript { scriptFile = Path.join([loadingDirectory, scriptFile]); Reader.readAndProcess(Stream.fromFile(scriptFile), k, (nextExp) -> { - 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 + var expr = Kiss.readerExpToHaxeExpr(nextExp, k); + + if (expr != null) { commandList.push(macro function(self) { - ${Kiss.readerExpToHaxeExpr(nextExp, k)}; + $expr; }); } + // 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? diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index f6eb9852..f0f9911f 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -30,7 +30,9 @@ typedef KissState = { wrapListExps:Bool, loadedFiles:Map, callAliases:Map, - identAliases:Map + identAliases:Map, + fields:Array, + loadingDirectory:String }; class Kiss { @@ -59,7 +61,9 @@ class Kiss { "has" => Symbol("Lambda.has"), "count" => Symbol("Lambda.count") ], - identAliases: new Map() + identAliases: new Map(), + fields: [], + loadingDirectory: "" }; // Helpful aliases @@ -101,49 +105,30 @@ class Kiss { } return _try(() -> { - var classFields:Array = if (useClassFields) Context.getBuildFields() else []; - var stream = Stream.fromFile(Path.join([loadingDirectory, kissFile])); - if (k == null) k = defaultKissState(); - Reader.readAndProcess(stream, k, (nextExp) -> { - #if test - Sys.println(nextExp.def.toString()); - #end - switch (nextExp.def) { - // (load... ) is the specialest of forms because it calls build() again and those fields need to be merged - case CallExp({pos: _, def: Symbol("load")}, loadArgs): - nextExp.checkNumArgs(1, 1, "(load \"[file]\")"); - switch (loadArgs[0].def) { - case StrExp(otherKissFile): - if (!k.loadedFiles.exists(otherKissFile)) { - var loadedFields = Kiss.build(otherKissFile, k, false); - for (field in loadedFields) { - classFields.push(field); - } - k.loadedFiles[otherKissFile] = true; - } - default: - throw CompileError.fromExp(loadArgs[0], "only argument to load should be a string literal"); - } - default: - 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 = classFields.concat(fields); - } - }); + if (useClassFields) + k.fields = Context.getBuildFields(); + k.loadingDirectory = loadingDirectory; - classFields; + load(kissFile, k); + + k.fields; + }); + } + + public static function load(kissFile:String, k:KissState) { + k.loadedFiles[kissFile] = true; + var stream = Stream.fromFile(Path.join([k.loadingDirectory, kissFile])); + Reader.readAndProcess(stream, k, (nextExp) -> { + #if test + Sys.println(nextExp.def.toString()); + #end + + var expr = readerExpToHaxeExpr(nextExp, k); + + // if non-null, stuff it in main() }); } @@ -154,52 +139,23 @@ class Kiss { if (k == null) k = defaultKissState(); - var fields = []; + if (useClassFields) + k.fields = Context.getBuildFields(); for (file in kissFiles) { - fields = fields.concat(build(file, k, useClassFields)); + build(file, k, false); } - return fields; + return k.fields; } - public static function readerExpToFields(exp:ReaderExp, k:KissState, errorIfNot = true):Array { + public static function readerExpToHaxeExpr(exp:ReaderExp, k:KissState):Null { + var macros = k.macros; var fieldForms = k.fieldForms; - - // Macros at top-level are allowed if they expand into a fieldform, or null like defreadermacro - var macros = k.macros; - var callAliases = k.callAliases; - 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)): - var expandedExp = macros[mac](exp, args, k); - 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); - 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); - readerExpToFields(aliasedExp, k, errorIfNot); - case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)): - [fieldForms[formName](exp, args, k)]; - default: - if (errorIfNot) throw CompileError.fromExp(exp, 'top-level expressions must be (or expand into) field or macro definitions'); else []; - }; - } - - public static function readerExpToHaxeExpr(exp:ReaderExp, k:KissState):Expr { - var macros = k.macros; var specialForms = k.specialForms; // Bind the table arguments of this function for easy recursive calling/passing var convert = readerExpToHaxeExpr.bind(_, k); + var expr = switch (exp.def) { case Symbol(alias) if (k.identAliases.exists(alias)): readerExpToHaxeExpr(k.identAliases[alias].withPosOf(exp), k); @@ -211,8 +167,16 @@ class Kiss { }; case StrExp(s): EConst(CString(s)).withMacroPosOf(exp); + case CallExp({pos: _, def: Symbol(ff)}, args) if (fieldForms.exists(ff)): + k.fields.push(fieldForms[ff](exp, args, k)); + null; // Field forms are no-ops case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)): - convert(macros[mac](exp, args, k)); + var expanded = macros[mac](exp, args, k); + if (expanded != null) { + convert(expanded); + } else { + null; + }; case CallExp({pos: _, def: Symbol(specialForm)}, args) if (specialForms.exists(specialForm)): specialForms[specialForm](exp, args, k); case CallExp({pos: _, def: Symbol(alias)}, args) if (k.callAliases.exists(alias)): @@ -256,6 +220,7 @@ class Kiss { #if test // Sys.println(expr.toString()); // For very fine-grained codegen inspection--slows compilation a lot. #end + return expr; } diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index 029294b1..0dcfcb75 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -19,6 +19,19 @@ class Macros { public static function builtins() { var macros:Map = []; + macros["load"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { + wholeExp.checkNumArgs(1, 1, "(load \"[file]\")"); + switch (args[0].def) { + case StrExp(otherKissFile): + if (!k.loadedFiles.exists(otherKissFile)) { + Kiss.load(otherKissFile, k); + } + default: + throw CompileError.fromExp(args[0], "only argument to load should be a string literal"); + } + null; + }; + function destructiveVersion(op:String, assignOp:String) { macros[assignOp] = (wholeExp:ReaderExp, exps:Array, k) -> { wholeExp.checkNumArgs(2, null, '($assignOp [var] [v1] [values...])');