author | Alan Dipert
<alan@dipert.org> 2020-06-19 05:14:36 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-06-19 05:14:36 UTC |
parent | e3ce4ec9865e09c9ee699438a595de9d8c4564d6 |
boot.lisp | +1 | -1 |
jacl-tests.lisp | +35 | -0 |
jacl.js | +21 | -2 |
diff --git a/boot.lisp b/boot.lisp index cd137e7..5c12307 100644 --- a/boot.lisp +++ b/boot.lisp @@ -43,7 +43,7 @@ `(jacl:%progn (jacl:%js "(~{}.fvalue = ~{})" ',name - (jacl:%lambda nil ,params ,@body)) + (jacl:%lambda ,name ,params ,@body)) ',name)) (defun %export (symbol &optional (package *package*)) diff --git a/jacl-tests.lisp b/jacl-tests.lisp index a2f141c..6dc7cd8 100644 --- a/jacl-tests.lisp +++ b/jacl-tests.lisp @@ -169,3 +169,38 @@ (assert= sum 6 "with tags in body"))) (\. @|QUnit| (|start|)) + +(\. @|QUnit| (|module| @"Functions")) + +(deftest "Named JACL:%LAMBDA" + (let* ((invocations 0) + (f (jacl:%lambda self (x y) + (when (< x y) + (setq invocations (1+ invocations)) + (self (1+ x) y))))) + (funcall f 0 10) + (assert= invocations 10))) + +(defun tak (x y z) + (if (not (< y x)) + z + (tak + (tak (1- x) y z) + (tak (1- y) z x) + (tak (1- z) x y)))) + +(defun now () + (\. (%js "performance") (|now|))) + +(defmacro time (memo &rest body) + (let ((start (gensym)) + (ret (gensym))) + `(let ((,start (now)) + (,ret (progn ,@body))) + (log ,memo (- (now) ,start)) + ,ret))) + +(deftest "Recursion" + (assert= 7 (time @"tak" (tak 18 12 6)))) + + diff --git a/jacl.js b/jacl.js index c6977bd..66d9afe 100644 --- a/jacl.js +++ b/jacl.js @@ -1291,6 +1291,7 @@ const analyzeSpecials = new Map([ declarations = List.toArray(List.rest(body[0])); body = body.slice(1); } + env = name ? env.withLocalFunctions([name]) : env; node = makeNode('lambda', { env: env, parent: parent, @@ -1498,6 +1499,13 @@ const parseCall = (env, parent, form) => { node.args = args.map(analyze.bind(null, env.withContext('expr'), node)); if (isLambdaForm(func)) { node.f = analyze(env.withContext('expr'), parent, func); + } else if (env.hasLocalFunction(func)) { + node.f = makeNode('local', { + env: env.withContext('expr'), + parent: node, + form: func, + children: [] + }); } else if (func instanceof LispSymbol) { node.f = makeNode('global', { env: env.withContext('expr'), @@ -1557,6 +1565,7 @@ class Env { } static init(env) { env.locals = new Set(); + env.localFunctions = new Set(); // sym => tagbodyName env.tags = new Map(); env.context = 'expr'; @@ -1566,6 +1575,7 @@ class Env { clone() { const newEnv = new Env(false); newEnv.locals = new Set(this.locals); + newEnv.localFunctions = new Set(this.localFunctions); newEnv.tags = new Map(this.tags); newEnv.context = this.context; newEnv.counter = this.counter; @@ -1576,6 +1586,11 @@ class Env { newEnv.locals = new Set([...this.locals, ...syms]); return newEnv; } + withLocalFunctions(syms) { + const newEnv = this.clone(); + newEnv.localFunctions = new Set([...this.localFunctions, ...syms]); + return newEnv; + } newId() { return this.counter(); } @@ -1590,6 +1605,9 @@ class Env { hasLocal(sym) { return this.locals.has(sym); } + hasLocalFunction(sym) { + return this.localFunctions.has(sym); + } hasTag(tag) { return this.tags.has(tag); } @@ -1862,9 +1880,9 @@ const emitNode = (print, node) => { } case 'lambda': { if (node.declarations.find(x => List.first(x) === JACLPKG.intern('ASYNC'))) { - print(`(async function ${mungeSym(node.name)}(`); + print(`(async function ${mungeSym(node.name, 'local')}(`); } else { - print(`(function ${mungeSym(node.name)}(`); + print(`(function ${mungeSym(node.name, 'local')}(`); } // Emit required argument names // TODO Move lambda list production to a function for use by macro ll, @@ -2260,6 +2278,7 @@ const loadLispScripts = async () => { const node = optimize(analyze(emptyEnv, null, obj)); const sb = new StringBuffer(); emitNode(sb.append.bind(sb), node); + // console.log(sb.toString()); eval(sb.toString()); } console.log(`;Loaded ${child.src} in ${(new Date())-start} ms`);