author | Alan Dipert
<alan@dipert.org> 2020-01-17 05:53:25 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-01-17 05:53:25 UTC |
parent | 2a7f4d1987016ebb24e865b7adc6fce0984b3118 |
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"),