git » jacl.git » commit f11ea3a

Progress on List interface and Slice type

author Alan Dipert
2020-01-17 05:53:25 UTC
committer Alan Dipert
2020-01-17 05:53:25 UTC
parent 2a7f4d1987016ebb24e865b7adc6fce0984b3118

Progress on List interface and Slice type

jacl.js +93 -24

diff --git a/jacl.js b/jacl.js
index f85a1fe..c0db4a8 100644
--- a/jacl.js
+++ b/jacl.js
@@ -10,16 +10,91 @@ class TagEx extends Error {
   }
 }
 
+class List {
+  static length(x) {
+    if (x === null) return 0;
+    if (x instanceof Cons) {
+      return x.cdr === null ? 1 : 1 + List.length(x.cdr);
+    } else if (x instanceof Slice) {
+      return x.length - x.start;
+    } else {
+      throw new Error(`Unsupported type`);
+    }
+  }
+  static nth(x, n) {
+    if (x === null) return null;
+    if (x instanceof Cons) {
+      return n === 0 ? x.car : List.nth(x.cdr, n-1);
+    } else if (x instanceof Slice) {
+      if (n > (x.end - x.start)) throw new Error(`Out of range`);
+      return x.arr[x.start + n];
+    } else {
+      throw new Error(`Unsupported type`);
+    }
+  }
+  static isProperList(x) {
+    if (x === null) return true;
+    if (x instanceof Cons) return List.isProperList(x.cdr);
+    if (x instanceof Slice) return true;
+    return false;
+  }
+  static first(x) {
+    if (x === null) return null;
+    if (x instanceof Cons) return x.car;
+    if (x instanceof Slice) {
+      return (x.end - x.start) ? x.arr[x.start] : null;
+    }
+    throw new Error(`Unsupported type`);
+  }
+  static rest(x) {
+    if (x === null) return null;
+    if (x instanceof Cons) return x.cdr;
+    if (x instanceof Slice) {
+      if ((x.end - x.start) <= 1) return null;
+      return new Slice(x.arr.slice(x.start + 1, x.end));
+    }
+    throw new Error(`Unsupported type`);
+  }
+  static toArray(x) {
+    if (x === null) return [];
+    if (x instanceof Cons) return Array.from(x);
+    if (x instanceof Slice) return x.arr.slice(x.start, x.end);
+    throw new Error(`Unsupported type`);
+  }
+}
+
+class Slice {
+  constructor(...xs) {
+    this.arr = xs;
+    this.start = 0;
+    this.end = xs.length;
+  }
+  static withArray(xs) {
+    const s = new Slice();
+    s.arr = xs;
+    s.start = 0;
+    s.end = xs.length;
+    return s;
+  }
+  [Symbol.iterator]() {
+    let i = this.start;
+    return {
+      next: () => {
+        if (i < this.end) {
+          return { value: this.arr[i++], done: false };
+        } else {
+          return { done: true };
+        }
+      }
+    };
+  }
+}
+
 class Cons {
   constructor(car, cdr = null) {
     this.car = car;
     this.cdr = cdr;
   }
-  static isProperList(cons) {
-    if (cons === null) return true;
-    if (!(cons instanceof Cons)) return false;
-    return Cons.isProperList(cons.cdr);
-  }
   static fromArray(xs) {
     let list = null;
     for(let i = xs.length-1; i >= 0; i--) list = new Cons(xs[i], list);
@@ -28,12 +103,6 @@ class Cons {
   static listOf(...xs) {
     return Cons.fromArray(xs);
   }
-  static length(cons) {
-    return cons.cdr === null ? 1 : 1 + Cons.length(cons.cdr);
-  }
-  static nth(cons, n) {
-    return n === 0 ? cons.car : Cons.nth(cons.cdr, n-1);
-  }
   static append(...xs) {
     if (xs.length === 0) {
       return null;
@@ -923,7 +992,7 @@ const parseLambdaList = list => {
     return x;
   };
 
-  let arr = Cons.toArray(list),
+  let arr = List.toArray(list),
     sections = {
       // Array of symbols
       required: [],
@@ -977,18 +1046,18 @@ const parseLambdaList = list => {
             initform: UNDEFINED,
             svar: UNDEFINED
           });
-        } else if (Cons.isProperList(x)) {
-          const len = Cons.length(x);
+        } else if (List.isProperList(x)) {
+          const len = List.length(x);
           if (len === 0 || len > 3) 
             throw new Error(`&OPTIONAL parameter list wrong size`);
           let svar = UNDEFINED;
           if (len === 3) {
-            svar = checkValidLocal(Cons.nth(x, 2));
+            svar = checkValidLocal(List.nth(x, 2));
             sections.optionalSvars.push(svar);
           }
           sections.optional.push({
             name: checkValidLocal(x.car),
-            initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED,
+            initform: len > 1 ? List.nth(x, 1) : UNDEFINED,
             svar: svar
           });
         } else {
@@ -1040,20 +1109,20 @@ const parseLambdaList = list => {
             initform: UNDEFINED,
             svar: UNDEFINED
           });
-        } else if (Cons.isProperList(x)) {
-          const len = Cons.length(x);
+        } else if (List.isProperList(x)) {
+          const len = List.length(x);
           if (len === 0 || len > 3) 
             throw new Error(`&KEY parameter list wrong size`);
           const key = LispSymbol.intern('KEYWORD', x.car.name);
           let svar = UNDEFINED;
           if (len === 3) {
-            svar = checkValidLocal(Cons.nth(x, 2));
+            svar = checkValidLocal(List.nth(x, 2));
             sections.keySvars.push(svar);
           }
           sections.key.push({
             key: key,
             name: checkValidLocal(x.car),
-            initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED,
+            initform: len > 1 ? List.nth(x, 1) : UNDEFINED,
             svar: svar
           });
         } else {
@@ -1069,8 +1138,8 @@ const parseLambdaList = list => {
           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)
+        } else if (List.isProperList(x)) {
+          if (List.length(x) !== 2)
             throw new Error(`&AUX parameter list wrong size`);
           sections.aux.push({
             name: checkValidLocal(x.car),
@@ -1189,7 +1258,7 @@ const analyzeSpecials = new Map([
     });
     node.locals = [];
     node.specials = [];
-    for (const [name, expr] of Cons.toArray(bindings)) {
+    for (const [name, expr] of List.toArray(bindings)) {
       const val = analyze(env.withContext('expr'), node, expr)
       if (name.isSpecial) {
         node.specials.push([name, val]);
@@ -1915,7 +1984,7 @@ JACLPKG.intern('.')
           `((~{})['${escapeSingle(op.toString())}'])`,
           form
         );
-      } else if (Cons.isProperList(op)) {
+      } else if (List.isProperList(op)) {
         const [meth, ...args] = op
         return Cons.listOf(
           JACLPKG.intern("%CALL"),