git » jacl.git » commit 75d348a

lambda list parsing: basically done-ish

author Alan Dipert
2019-10-31 03:42:55 UTC
committer Alan Dipert
2019-10-31 03:42:55 UTC
parent e7badf39d8aea9e359526afaa0e709e7f5d7be0e

lambda list parsing: basically done-ish

jacl.js +74 -43

diff --git a/jacl.js b/jacl.js
index 75c1571..e73c87c 100644
--- a/jacl.js
+++ b/jacl.js
@@ -866,11 +866,11 @@ const analyzeBlock = (env, parent, forms) => {
 };
 
 const parseLambdaList = list => {
-  const clSym = (x, name) => (x instanceof LispSymbol) &&
+  const eqClSym = (x, name) => (x instanceof LispSymbol) &&
     x.packageName === 'COMMON-LISP' &&
     x.name === name;
 
-  const checkLocal = x => {
+  const checkValidLocal = x => {
     if (!(x instanceof LispSymbol) || x.packageName == 'KEYWORD')
       throw new Error(`${x} not a valid local variable`);
     return x;
@@ -890,50 +890,41 @@ const parseLambdaList = list => {
     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';
+        if (eqClSym(x, '&OPTIONAL')
+            || eqClSym(x, '&KEY')
+            || eqClSym(x, '&REST')
+            || eqClSym(x, '&AUX')) {
+          state = x.name.substring(1).toLowerCase();
         } else if (x instanceof LispSymbol) {
-          sections.required.push(checkLocal(x));
+          sections.required.push(checkValidLocal(x));
         } else {
           throw new Error(`Required argument is not a symbol`);
         }
         break;
       case 'optional':
-        if (clSym(x, '&OPTIONAL')) {
+        if (eqClSym(x, '&OPTIONAL')) {
           throw new Error(`Repeated &OPTIONAL`);
-        } else if (clSym(x, '&KEY')) {
-          state = 'key';
-        } else if (clSym(x, '&REST')) {
-          state = 'rest';
+        } else if (eqClSym(x, '&KEY')
+                   || eqClSym(x, '&REST')
+                   || eqClSym(x, '&AUX')) {
+          state = x.name.substring(1).toLowerCase();
         } else if (x instanceof LispSymbol) {
-          sections.optional.push([checkLocal(x), UNDEFINED, UNDEFINED]);
-        } else if (x instanceof Cons) {
-          if (!Cons.isProperList(x))
-            throw new Error(`&OPTIONAL parameter list is improper`);
-          switch (Cons.length(x)) {
-            case 0:
-              throw new Error(`Empty optional parameter`);
-            case 1:
-              sections.optional.push([checkLocal(x.car), UNDEFINED, UNDEFINED]);
-              break;
-            case 2:
-              sections.optional.push([checkLocal(x.car), x.cdr.car, UNDEFINED]);
-              break;
-            case 3: {
-              checkLocal(x.car);
-              checkLocal(Cons.nth(x, 2));
-              sections.optional.push(Cons.toArray(x));
-              break;
-            }
-            default:
-              throw new Error(`Invalid &OPTIONAL spec`);
-          }
+          sections.optional.push({
+            name: checkValidLocal(x),
+            initform: UNDEFINED,
+            svar: UNDEFINED
+          });
+        } else if (Cons.isProperList(x)) {
+          const len = Cons.length(x);
+          if (len === 0 || len > 3)
+            throw new Error(`&OPTIONAL parameter list wrong size`);
+          sections.optional.push({
+            name: checkValidLocal(x.car),
+            initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED,
+            svar: len === 3 ? checkValidLocal(Cons.nth(x, 2)) : UNDEFINED
+          });
         } else {
-          throw new Error(`Optional argument not symbol or list`);
+          throw new Error(`&OPTIONAL parameter not symbol or valid list`);
         }
         break;
       case 'rest':
@@ -945,23 +936,63 @@ const parseLambdaList = list => {
         state = 'after-rest';
         break;
       case 'after-rest':
-        if (x instanceof LispSymbol &&
-          x.packageName === 'COMMON-LISP' &&
-          (x.name === '&OPTIONAL' ||
-            x.name === '&KEY' ||
-            x.name === '&AUX')) {
+        if (eqClSym(x, '&OPTIONAL')
+            || eqClSym(x, '&KEY')
+            || eqClSym(x, '&AUX')) {
           state = x.name.substring(1).toLowerCase();
           if (sections[state].length)
             throw new Error(`Duplicate ${x.name}`);
-        } else if (clSym(x, '&REST')) {
+        } else if (eqClSym(x, '&REST')) {
           throw new Error(`Repeated &REST`);
         } else {
           throw new Error(`Expected keyword after &REST param`);
         }
         break;
       case 'key':
+        if (eqClSym(x, '&KEY')) {
+          throw new Error(`Repeated &KEY`);
+        } else if (eqClSym(x, '&OPTIONAL') || eqClSym(x, '&REST')) {
+          throw new Error(`Misplaced ${x.name}`);
+        } else if (eqClSym(x, '&AUX')) {
+          state = 'aux';
+        } else if (x instanceof LispSymbol) {
+          sections.key.push({
+            name: checkValidLocal(x),
+            initform: UNDEFINED,
+            svar: UNDEFINED
+          });
+        } else if (Cons.isProperList(x)) {
+          const len = Cons.length(x);
+          if (len === 0 || len > 3)
+            throw new Error(`&KEY parameter list wrong size`);
+          sections.key.push({
+            name: checkValidLocal(x.car),
+            initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED,
+            svar: len === 3 ? checkValidLocal(Cons.nth(x, 2)) : UNDEFINED
+          });
+        } else {
+          throw new Error(`&KEY parameter not symbol or valid list`);
+        }
         break;
       case 'aux':
+        if (eqClSym(x, '&AUX')) {
+          throw new Error(`Repeated &AUX`);
+        } else if (eqClSym(x, '&OPTIONAL')
+            || eqClSym(x, '&KEY')
+            || eqClSym(x, '&REST')) {
+          throw new Error(`Misplaced ${x.name}`);
+        } else if (x instanceof LispSymbol) {
+          sections.aux.push({ name: checkValidLocal(x), initform: UNDEFINED });
+        } else if (Cons.isProperList(x)) {
+          if (Cons.length(x) !== 2)
+            throw new Error(`&AUX parameter list wrong size`);
+          sections.key.push({
+            name: checkValidLocal(x.car),
+            initform: x.cdr.car
+          });
+        } else {
+          throw new Error(`&AUX parameter not symbol or valid list`);
+        }
         break;
     }
   }