From 29f6f0ae0b6ad59c9cca20428855cb91c835af1c Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Thu, 31 Dec 2020 15:48:58 -0700 Subject: [PATCH] separate call and ident aliases --- src/kiss/Helpers.hx | 11 -------- src/kiss/Kiss.hx | 37 ++++++++++++++++--------- src/kiss/Macros.hx | 20 ++++++++++--- src/test/cases/BasicTestCase.hx | 4 +++ src/test/cases/BasicTestCase.kiss | 7 ++++- src/test/cases/ReaderMacroTestCase.kiss | 6 ++-- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index 18deee8..eda4439 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -153,17 +153,6 @@ class Helpers { } } - // alias replacements are processed by the reader - public static function defAlias(k:KissState, whenItsThis:String, makeItThisInstead:ReaderExpDef) { - // The alias has to be followed by a terminator to count! - for (terminator in Reader.terminators) { - k.readTable[whenItsThis + terminator] = (s:Stream, k) -> { - s.putBackString(terminator); - makeItThisInstead; - } - } - } - /** Throw a CompileError if the given expression has the wrong number of arguments **/ diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index af6d919..09f9260 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -26,7 +26,9 @@ typedef KissState = { specialForms:Map, macros:Map, wrapListExps:Bool, - loadedFiles:Map + loadedFiles:Map, + callAliases:Map, + identAliases:Map }; class Kiss { @@ -41,21 +43,18 @@ class Kiss { specialForms: SpecialForms.builtins(), macros: Macros.builtins(), wrapListExps: true, - loadedFiles: new Map() + loadedFiles: new Map(), + // Helpful built-in aliases + callAliases: [ + "print" => Symbol("Prelude.print"), "sort" => Symbol("Prelude.sort"), "groups" => Symbol("Prelude.groups"), "zip" => Symbol("Prelude.zip"), + "pairs" => Symbol("Prelude.pairs"), // TODO test pairs + "memoize" => Symbol("Prelude.memoize"), // TODO test memoize + "map" => Symbol("Lambda.map"), "filter" => Symbol("Lambda.filter"), // TODO use truthy as the default filter function + "has" => Symbol("Lambda.has"), "count" => Symbol("Lambda.count")], + identAliases: new Map() }; // Helpful aliases - k.defAlias("print", Symbol("Prelude.print")); - k.defAlias("sort", Symbol("Prelude.sort")); - k.defAlias("groups", Symbol("Prelude.groups")); - k.defAlias("zip", Symbol("Prelude.zip")); - k.defAlias("pairs", Symbol("Prelude.pairs")); // TODO test pairs - k.defAlias("memoize", Symbol("Prelude.memoize")); // TODO test memoize - k.defAlias("map", Symbol("Lambda.map")); - k.defAlias("filter", Symbol("Lambda.filter")); // TODO use truthy as the default filter function - k.defAlias("has", Symbol("Lambda.has")); - k.defAlias("count", Symbol("Lambda.count")); - return k; } @@ -132,11 +131,19 @@ class Kiss { // 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) { case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)): var expandedExp = macros[mac](exp, args, k); if (expandedExp != null) readerExpToField(expandedExp, k, errorIfNot) else null; + 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); + 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); case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)): fieldForms[formName](exp, args, k); default: @@ -150,6 +157,8 @@ class Kiss { // 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); case Symbol(name): Context.parse(name, exp.macroPos()); case StrExp(s): @@ -158,6 +167,8 @@ class Kiss { convert(macros[mac](exp, args, k)); 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)): + convert(CallExp(k.callAliases[alias].withPosOf(exp), args).withPosOf(exp)); case CallExp(func, args): ECall(convert(func), [for (argExp in args) convert(argExp)]).withMacroPosOf(exp); case ListExp(elements): diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index 6892be1..2362a8d 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -322,13 +322,25 @@ class Macros { }; macros["defalias"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { - wholeExp.checkNumArgs(2, 2, "(defalias [whenItsThis] [makeItThis])"); - k.defAlias(switch (exps[0].def) { + wholeExp.checkNumArgs(2, 2, "(defalias [[&call or &ident] whenItsThis] [makeItThis])"); + var aliasMap:Map = null; + var nameExp = switch (exps[0].def) { + case MetaExp("call", nameExp): + aliasMap = k.callAliases; + nameExp; + case MetaExp("ident", nameExp): + aliasMap = k.identAliases; + nameExp; + default: + throw CompileError.fromExp(exps[0], 'first argument to defalias should be a symbol for the alias annotated with either &call or &ident'); + }; + var name = switch (nameExp.def) { case Symbol(whenItsThis): whenItsThis; default: - throw CompileError.fromExp(exps[0], 'first argument to defalias should be a symbol for the alias'); - }, exps[1].def); + throw CompileError.fromExp(exps[0], 'first argument to defalias should be a symbol for the alias annotated with either &call or &ident'); + }; + aliasMap[name] = exps[1].def; return null; }; diff --git a/src/test/cases/BasicTestCase.hx b/src/test/cases/BasicTestCase.hx index d1e194e..08b39a7 100644 --- a/src/test/cases/BasicTestCase.hx +++ b/src/test/cases/BasicTestCase.hx @@ -257,6 +257,10 @@ class BasicTestCase extends Test { function testDefmacroWithLogic() { _testDefmacroWithLogic(); } + + function testCallAlias() { + _testCallAlias(); + } } class BasicObject { diff --git a/src/test/cases/BasicTestCase.kiss b/src/test/cases/BasicTestCase.kiss index e259fc2..5c7683e 100644 --- a/src/test/cases/BasicTestCase.kiss +++ b/src/test/cases/BasicTestCase.kiss @@ -392,4 +392,9 @@ (defun _testDefmacroWithLogic [] (Assert.equals "Welcome Stevo (Guest #1)" (macroWithLogic "Stevo")) - (Assert.equals "Welcome Bob (Guest #2)" (macroWithLogic "Bob"))) \ No newline at end of file + (Assert.equals "Welcome Bob (Guest #2)" (macroWithLogic "Bob"))) + +// Make sure built-in call aliases don't override user-defined variables +(defun _testCallAlias [] + (let [map [=>"hey" "you"]] + (Assert.equals "you" (dictGet map "hey")))) \ No newline at end of file diff --git a/src/test/cases/ReaderMacroTestCase.kiss b/src/test/cases/ReaderMacroTestCase.kiss index 4e9f1eb..ba66561 100644 --- a/src/test/cases/ReaderMacroTestCase.kiss +++ b/src/test/cases/ReaderMacroTestCase.kiss @@ -9,9 +9,9 @@ (defun myBool [] (begin !false)) -(defalias pluppers +) -(defalias fluffers 5) -(defalias buffers 4) +(defalias &call pluppers +) +(defalias &ident fluffers 5) +(defalias &ident buffers 4) (defvar mySum (pluppers fluffers buffers))