git » jacl.git » commit 4a65f75

tak

author Alan Dipert
2020-06-19 05:14:36 UTC
committer Alan Dipert
2020-06-19 05:14:36 UTC
parent e3ce4ec9865e09c9ee699438a595de9d8c4564d6

tak

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`);