separate call and ident aliases

This commit is contained in:
2020-12-31 15:48:58 -07:00
parent 1a84724979
commit 8fc512d511
6 changed files with 53 additions and 32 deletions

View File

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

View File

@@ -26,7 +26,9 @@ typedef KissState = {
specialForms:Map<String, SpecialFormFunction>,
macros:Map<String, MacroFunction>,
wrapListExps:Bool,
loadedFiles:Map<String, Bool>
loadedFiles:Map<String, Bool>,
callAliases:Map<String, ReaderExpDef>,
identAliases:Map<String, ReaderExpDef>
};
class Kiss {
@@ -41,21 +43,18 @@ class Kiss {
specialForms: SpecialForms.builtins(),
macros: Macros.builtins(),
wrapListExps: true,
loadedFiles: new Map<String, Bool>()
loadedFiles: new Map<String, Bool>(),
// 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):

View File

@@ -322,13 +322,25 @@ class Macros {
};
macros["defalias"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, 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<String, ReaderExpDef> = 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;
};

View File

@@ -257,6 +257,10 @@ class BasicTestCase extends Test {
function testDefmacroWithLogic() {
_testDefmacroWithLogic();
}
function testCallAlias() {
_testCallAlias();
}
}
class BasicObject {

View File

@@ -392,4 +392,9 @@
(defun _testDefmacroWithLogic []
(Assert.equals "Welcome Stevo (Guest #1)" (macroWithLogic "Stevo"))
(Assert.equals "Welcome Bob (Guest #2)" (macroWithLogic "Bob")))
(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"))))

View File

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