diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index 745725b6..271e17c7 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -58,7 +58,9 @@ typedef KissState = { macroVars:Map, collectedBlocks:Map>, inStaticFunction:Bool, - typeHints:Array + typeHints:Array, + conversionStack:Array, + stateChanged:Bool }; #end @@ -168,6 +170,8 @@ class Kiss { collectedBlocks: new Map(), inStaticFunction: false, typeHints: [], + conversionStack: [], + stateChanged: false }; k.doc = (form:String, minArgs:Null, maxArgs:Null, expectedForm = "", doc = "") -> { @@ -413,8 +417,27 @@ class Kiss { } static var macroUsed = false; - + static var expCache:haxe.DynamicAccess = null; + static var cacheFile = ".kissCache.json"; + static var cacheThreshold = 0.2; public static function readerExpToHaxeExpr(exp:ReaderExp, k:KissState):Expr { + #if kissCache + if (expCache == null) { + expCache = if (sys.FileSystem.exists(cacheFile)) { + haxe.Json.parse(File.getContent(cacheFile)); + } else { + {}; + } + } + var str = Reader.toString(exp.def); + if (expCache.exists(str)) { + return Context.parse(expCache[str], Helpers.macroPos(exp)); + } + #end + + if (k.conversionStack.length == 0) k.stateChanged = false; + k.conversionStack.push(exp); + var macros = k.macros; var fieldForms = k.fieldForms; var specialForms = k.specialForms; @@ -437,6 +460,7 @@ class Kiss { var none = EBlock([]).withMacroPosOf(exp); + var startTime = haxe.Timer.stamp(); var expr = switch (exp.def) { case None: none; @@ -455,6 +479,7 @@ class Kiss { var field = fieldForms[ff](exp, args.copy(), k); k.fieldList.push(field); k.fieldDict[field.name] = field; + k.stateChanged = true; none; // Field forms are no-ops case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)): checkNumArgs(mac); @@ -516,8 +541,13 @@ class Kiss { default: throw KissError.fromExp(exp, 'conversion not implemented'); }; - #if test - // Sys.println(expr.toString()); // For very fine-grained codegen inspection--slows compilation a lot. + var conversionTime = haxe.Timer.stamp() - startTime; + k.conversionStack.pop(); + #if kissCache + if (conversionTime > cacheThreshold && !k.stateChanged) { + expCache[str] = expr.toString(); + File.saveContent(cacheFile, haxe.Json.stringify(expCache)); + } #end return expr; diff --git a/kiss/src/kiss/Macros.hx b/kiss/src/kiss/Macros.hx index 4c7fcc9c..010800af 100644 --- a/kiss/src/kiss/Macros.hx +++ b/kiss/src/kiss/Macros.hx @@ -410,6 +410,7 @@ class Macros { k.doc("defmacro", 3, null, '(defMacro [] )'); macros["defmacro"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = switch (exps[0].def) { case Symbol(name): name; default: throw KissError.fromExp(exps[0], "macro name should be a symbol"); @@ -528,6 +529,7 @@ class Macros { k.doc("undefmacro", 1, 1, '(undefMacro )'); macros["undefmacro"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = switch (exps[0].def) { case Symbol(name): name; default: throw KissError.fromExp(exps[0], "macro name should be a symbol"); @@ -540,6 +542,7 @@ class Macros { k.doc("defreadermacro", 3, null, '(defReaderMacro <"" or []> [] )'); macros["defreadermacro"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; // reader macros declared in the form (defreadermacro &start ...) will only be applied // at the beginning of lines @@ -610,6 +613,7 @@ class Macros { k.doc("undefreadermacro", 1, 1, '(undefReaderMacro ["" or ])'); macros["undefreadermacro"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; // reader macros undeclared in the form (undefReaderMacro &start ...) will be removed from the table // for reader macros that must be at the beginning of lines // at the beginning of lines @@ -663,6 +667,7 @@ class Macros { k.doc("defalias", 2, 2, "(defAlias <<&call or &ident> whenItsThis> )"); macros["defalias"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = getAliasName(k, exps[0], "defAlias"); aliasMap[name] = exps[1].def; @@ -672,6 +677,7 @@ class Macros { k.doc("undefalias", 1, 1, "(undefAlias <<&call or &ident> alias>)"); macros["undefalias"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = getAliasName(k, exps[0], "undefAlias"); aliasMap.remove(name); @@ -896,6 +902,7 @@ class Macros { k.doc("defMacroVar", 2, 2, "(defMacroVar )"); macros["defMacroVar"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = exps[0].symbolNameValue(); k.macroVars[name] = Helpers.runAtCompileTimeDynamic(exps[1], k); @@ -905,6 +912,7 @@ class Macros { k.doc("setMacroVar",2, 2, "(setMacroVar )"); macros["setMacroVar"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var name = exps[0].symbolName().withPosOf(exps[0]); var b = wholeExp.expBuilder(); @@ -913,6 +921,7 @@ class Macros { k.doc("defMacroFunction", 3, null, "(defMacroFunction [] )"); macros["defMacroFunction"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { + k.stateChanged = true; var b = wholeExp.expBuilder(); var name = exps[0].symbolNameValue(); var lambdaExp = b.callSymbol("lambda", [exps[1]].concat(exps.slice(2)));