From 7ba7a847341b122983505491a01e3cea0bc56a43 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Sat, 4 Jun 2022 17:07:44 +0000 Subject: [PATCH] refactor to store minArgs, maxArgs, expectedForm, and a docstring of forms. close #55 --- kiss/src/kiss/FieldForms.hx | 84 +++++++++++++++++------------------ kiss/src/kiss/Kiss.hx | 45 +++++++++++++++++-- kiss/src/kiss/Macros.hx | 5 ++- kiss/src/kiss/SpecialForms.hx | 5 ++- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/kiss/src/kiss/FieldForms.hx b/kiss/src/kiss/FieldForms.hx index 2bd2afff..65292947 100644 --- a/kiss/src/kiss/FieldForms.hx +++ b/kiss/src/kiss/FieldForms.hx @@ -17,7 +17,7 @@ using StringTools; typedef FieldFormFunction = (wholeExp:ReaderExp, args:Array, k:KissState) -> Field; class FieldForms { - public static function builtins() { + public static function addBuiltins(k:KissState) { var map:Map = []; function renameAndDeprecate(oldName:String, newName:String) { @@ -27,17 +27,14 @@ class FieldForms { form(wholeExp, args, k); } map[newName] = form; + k.formDocs[newName] = k.formDocs[oldName]; } - map["defvar"] = varOrProperty.bind("var"); - renameAndDeprecate("defvar", "var"); - map["defprop"] = varOrProperty.bind("prop"); - renameAndDeprecate("defprop", "prop"); + varOrProperty("var", k); + varOrProperty("prop", k); - map["defun"] = funcOrMethod.bind("function"); - renameAndDeprecate("defun", "function"); - map["defmethod"] = funcOrMethod.bind("method"); - renameAndDeprecate("defmethod", "method"); + funcOrMethod("function", k); + funcOrMethod("method", k); return map; } @@ -98,45 +95,48 @@ class FieldForms { } } - static function varOrProperty(formName:String, wholeExp:ReaderExp, args:Array, k:KissState):Field { - wholeExp.checkNumArgs(1, 3, '($formName [optional: &mut] [optional :type] [variable] [optional value])'); + static function varOrProperty(formName:String, k:KissState) { + k.doc(formName, 1, 3, '($formName [optional: &mut] [optional :type] [variable] [optional value])'); + k.fieldForms[formName] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { + var name = Helpers.varName(formName, args[0]); + var access = fieldAccess(formName, name, args[0]); - var name = Helpers.varName(formName, args[0]); - var access = fieldAccess(formName, name, args[0]); - - return { - name: name, - access: access, - kind: FVar(Helpers.explicitType(args[0]), if (args.length > 1) k.convert(args[1]) else null), - pos: wholeExp.macroPos() - }; + ({ + name: name, + access: access, + kind: FVar(Helpers.explicitType(args[0]), if (args.length > 1) k.convert(args[1]) else null), + pos: wholeExp.macroPos() + } : Field); + } } - static function funcOrMethod(formName:String, wholeExp:ReaderExp, args:Array, k:KissState):Field { - wholeExp.checkNumArgs(2, null, '($formName [optional :type] [name] [[argNames...]] [body...])'); + static function funcOrMethod(formName:String, k:KissState) { + k.doc(formName, 2, null, '($formName [optional &dynamic] [optional :type] [name] [[argNames...]] [body...])'); + k.fieldForms[formName] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { - var name = Helpers.varName(formName, args[0]); - var access = fieldAccess(formName, name, args[0]); - var inStaticFunction = access.indexOf(AStatic) != -1; - var returnsValue = !isVoid(args[0]); + var name = Helpers.varName(formName, args[0]); + var access = fieldAccess(formName, name, args[0]); + var inStaticFunction = access.indexOf(AStatic) != -1; + var returnsValue = !isVoid(args[0]); - var wasInStatic = k.inStaticFunction; + var wasInStatic = k.inStaticFunction; - var f = { - name: name, - access: access, - kind: FFun( - Helpers.makeFunction( - args[0], - returnsValue, - args[1], - args.slice(2), - k.forStaticFunction(inStaticFunction), - formName)), - pos: wholeExp.macroPos() - }; + var f:Field = { + name: name, + access: access, + kind: FFun( + Helpers.makeFunction( + args[0], + returnsValue, + args[1], + args.slice(2), + k.forStaticFunction(inStaticFunction), + formName)), + pos: wholeExp.macroPos() + }; - k = k.forStaticFunction(wasInStatic); - return f; + k = k.forStaticFunction(wasInStatic); + return f; + } } } diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index 19e37fb2..0b8007f6 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -22,6 +22,13 @@ using haxe.io.Path; typedef ExprConversion = (ReaderExp) -> Expr; +typedef FormDoc = { + minArgs:Null, + maxArgs:Null, + ?expectedForm:String, + ?doc:String +}; + typedef KissState = { className:String, file:String, @@ -32,6 +39,8 @@ typedef KissState = { fieldForms:Map, specialForms:Map, macros:Map, + formDocs:Map, + doc:(String, Null, Null, ?String, ?String)->Void, wrapListExps:Bool, loadedFiles:Map>, callAliases:Map, @@ -59,9 +68,11 @@ class Kiss { startOfLineReadTable: new ReadTable(), startOfFileReadTable: new ReadTable(), endOfFileReadTable: new ReadTable(), - fieldForms: FieldForms.builtins(), - specialForms: SpecialForms.builtins(), - macros: Macros.builtins(), + fieldForms: new Map(), + specialForms: null, + macros: null, + formDocs: new Map(), + doc: null, wrapListExps: true, loadedFiles: new Map(), // Helpful built-in aliases @@ -131,6 +142,20 @@ class Kiss { inStaticFunction: false }; + k.doc = (form:String, minArgs:Null, maxArgs:Null, expectedForm = "", doc = "") -> { + k.formDocs[form] = { + minArgs: minArgs, + maxArgs: maxArgs, + expectedForm: expectedForm, + doc: doc + }; + return; + }; + + FieldForms.addBuiltins(k); + k.specialForms = SpecialForms.builtins(k); + k.macros = Macros.builtins(k); + return k; } @@ -333,9 +358,20 @@ class Kiss { var macros = k.macros; var fieldForms = k.fieldForms; var specialForms = k.specialForms; + var formDocs = k.formDocs; + // Bind the table arguments of this function for easy recursive calling/passing var convert = readerExpToHaxeExpr.bind(_, k); + function checkNumArgs(form:String) { + if (formDocs.exists(form)) { + var docs = formDocs[form]; + // null docs can get passed around by renameAndDeprecate functions. a check here is more DRY + if (docs != null) + exp.checkNumArgs(docs.minArgs, docs.maxArgs, docs.expectedForm); + } + } + if (k.hscript) exp = Helpers.removeTypeAnnotations(exp); @@ -355,11 +391,13 @@ class Kiss { case StrExp(s): EConst(CString(s)).withMacroPosOf(exp); case CallExp({pos: _, def: Symbol(ff)}, args) if (fieldForms.exists(ff)): + checkNumArgs(ff); var field = fieldForms[ff](exp, args.copy(), k); k.fieldList.push(field); k.fieldDict[field.name] = field; none; // Field forms are no-ops case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)): + checkNumArgs(mac); macroUsed = true; var expanded = macros[mac](exp, args.copy(), k); if (expanded != null) { @@ -368,6 +406,7 @@ class Kiss { none; }; case CallExp({pos: _, def: Symbol(specialForm)}, args) if (specialForms.exists(specialForm)): + checkNumArgs(specialForm); specialForms[specialForm](exp, args.copy(), k); case CallExp({pos: _, def: Symbol(alias)}, args) if (k.callAliases.exists(alias)): convert(CallExp(k.callAliases[alias].withPosOf(exp), args).withPosOf(exp)); diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index e8a38b24..44f236d1 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -22,7 +22,7 @@ using tink.MacroApi; typedef MacroFunction = (wholeExp:ReaderExp, args:Array, k:KissState) -> Null; class Macros { - public static function builtins() { + public static function builtins(k:KissState) { var macros:Map = []; function renameAndDeprecate(oldName:String, newName:String) { @@ -32,6 +32,7 @@ class Macros { form(wholeExp, args, k); } macros[newName] = form; + k.formDocs[newName] = k.formDocs[oldName]; } macros["load"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { @@ -265,8 +266,8 @@ class Macros { macros["or"] = _or; + k.doc("and", 1, null, "(and )"); function _and(wholeExp:ReaderExp, args:Array, k) { - wholeExp.checkNumArgs(1, null, "(and )"); var b = wholeExp.expBuilder(); var uniqueVarSymbol = b.symbol(); diff --git a/kiss/src/kiss/SpecialForms.hx b/kiss/src/kiss/SpecialForms.hx index 804cdfe9..8e595bf3 100644 --- a/kiss/src/kiss/SpecialForms.hx +++ b/kiss/src/kiss/SpecialForms.hx @@ -18,7 +18,7 @@ using tink.MacroApi; typedef SpecialFormFunction = (wholeExp:ReaderExp, args:Array, k:KissState) -> Expr; class SpecialForms { - public static function builtins() { + public static function builtins(k:KissState) { var map:Map = []; function renameAndDeprecate(oldName:String, newName:String) { @@ -28,6 +28,7 @@ class SpecialForms { form(wholeExp, args, k); } map[newName] = form; + k.formDocs[newName] = k.formDocs[oldName]; } map["begin"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { @@ -47,8 +48,8 @@ class SpecialForms { function arrayAccess(wholeExp:ReaderExp, args:Array, k:KissState) { return EArray(k.convert(args[0]), k.convert(args[1])).withMacroPosOf(wholeExp); }; + k.doc("nth", 2, 2, "(nth [list] [idx])"); map["nth"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { - wholeExp.checkNumArgs(2, 2, "(nth [list] [idx])"); arrayAccess(wholeExp, args, k); }; map["dictGet"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> {