git » jacl.git » commit a12d9d1

Lambda list parse progress. &key and &aux to go

author Alan Dipert
2019-10-30 03:59:45 UTC
committer Alan Dipert
2019-10-30 03:59:45 UTC
parent 3d28e6d41d980474176b82a074f00140c1c030fc

Lambda list parse progress. &key and &aux to go

jacl.js +99 -26

diff --git a/jacl.js b/jacl.js
index b9aaed6..c7088b9 100644
--- a/jacl.js
+++ b/jacl.js
@@ -1,3 +1,4 @@
+// Sentinel used in a few places to distinguish from a user-provided value
 const UNDEFINED = new Object();
 
 class Cons {
@@ -320,6 +321,12 @@ for (const [k,v] of CLFUNCS) {
   CLPKG.exportSymbol(k);
 }
 
+// Lambda list keywords
+for (const kw of ['&OPTIONAL', '&REST', '&KEY', '&AUX']) {
+  CLPKG.intern(kw);
+  CLPKG.exportSymbol(kw);
+}
+
 // JS package constants
 const JSCONSTS = new Map([
   ['+FALSE+', false],
@@ -848,39 +855,105 @@ const analyzeBlock = (env, parent, forms) => {
 };
 
 const parseLambdaList = list => {
-  const isKw = x => (x instanceof LispSymbol) && x.name[0] === '&';
+  const clSym = (x, name) => (x instanceof LispSymbol) &&
+    x.packageName === 'COMMON-LISP' &&
+    x.name === name;
+
   let arr = Cons.toArray(list),
-    section = 'required',
     sections = {
       required: [],
       optional: [],
-      keyword: [],
-      rest: null
+      rest: null,
+      key: [],
+      aux: []
     },
-    expectingKw = false;
-  scan: for (let i = 0; i < arr.length; i++) {
-    const x = list[i];
-    if (x instanceof LispSymbol) {
-      switch (x.name) {
-        case '&OPTIONAL':
-        case '&KEY':
-          section = x.name.substring(1).toLowerCase();
-          continue scan;
-        case '&REST':
-          if (section.rest !== null) throw new Error(`Multiple &REST`);
-          if (i == arr.length-1) throw new Error(`Nothing after &REST`);
-          if (arr.length-i !== 2)
-            throw new Error(`One argument name must follow &REST`);
-          sections.rest = arr[i+1];
-          break scan;
-        default:
-          sections[section].push(x);
-      }
-    } else {
-      sections[section].push(x);
+    state = 'required';
+
+  while (arr.length) {
+    const x = arr.shift();
+    switch (state) {
+      case 'required':
+        if (clSym(x, '&OPTIONAL')) {
+          state = 'optional';
+        } else if (clSym(x, '&KEY')) {
+          throw new Error(`TODO`);
+        } else if (clSym(x, '&REST')) {
+          state = 'rest';
+        } else if (x instanceof LispSymbol) {
+          sections.required.push(x);
+        } else {
+          throw new Error(`Required argument is not a symbol`);
+        }
+        break;
+      case 'optional':
+        if (clSym(x, '&OPTIONAL')) {
+          throw new Error(`Repeated &OPTIONAL`);
+        } else if (clSym(x, '&KEY')) {
+          state = 'key';
+        } else if (clSym(x, '&REST')) {
+          state = 'rest';
+        } else if (x instanceof LispSymbol) {
+          sections.optional.push([x, UNDEFINED, UNDEFINED]);
+        } else if (x instanceof Cons) {
+          const opt = Cons.toArray(x);
+          console.log(opt);
+          switch (opt.length) {
+            case 0:
+              throw new Error(`Empty optional parameter`);
+            case 1:
+              if (!(opt[0] instanceof LispSymbol))
+                throw new Error(`Optional parameter name not a symbol`);
+              sections.optional.push([opt[0], UNDEFINED, UNDEFINED]);
+              break;
+            case 2:
+              if (!(opt[0] instanceof LispSymbol))
+                throw new Error(`Optional parameter name not a symbol`);
+              sections.optional.push([opt[0], opt[1], UNDEFINED]);
+              break;
+            case 3: {
+              if (!(opt[0] instanceof LispSymbol))
+                throw new Error(`Optional parameter name not a symbol`);
+              if (!(opt[2] instanceof LispSymbol))
+                throw new Error(`Optional parameter svar not a symbol`);
+              sections.optional.push(opt);
+              break;
+            }
+            default:
+              throw new Error(`Invalid &OPTIONAL spec`);
+          }
+        } else {
+          throw new Error(`Optional argument not symbol or list`);
+        }
+        break;
+      case 'rest':
+        if (sections.rest)
+          throw new Error(`Repeated &REST`);
+        if (!(x instanceof LispSymbol))
+          throw new Error(`&REST parameter not a symbol`);
+        sections.rest = x;
+        state = 'after-rest';
+        break;
+      case 'after-rest':
+        if (x instanceof LispSymbol &&
+          x.packageName === 'COMMON-LISP' &&
+          (x.name === '&OPTIONAL' ||
+            x.name === '&KEY' ||
+            x.name === '&AUX')) {
+          state = x.name.substring(1).toLowerCase();
+          if (sections[state].length)
+            throw new Error(`Duplicate ${x.name}`);
+        } else if (clSym(x, '&REST')) {
+          throw new Error(`Repeated &REST`);
+        } else {
+          throw new Error(`Expected keyword after &REST param`);
+        }
+        break;
+      case 'key':
+        break;
+      case 'aux':
+        break;
     }
   }
-
   return sections;
 };