author | Alan Dipert
<alan@dipert.org> 2019-10-30 13:35:12 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-30 13:35:12 UTC |
parent | a12d9d1758f8df650039e527fe379951bf3b963e |
jacl.js | +62 | -11 |
diff --git a/jacl.js b/jacl.js index c7088b9..effedb1 100644 --- a/jacl.js +++ b/jacl.js @@ -6,11 +6,22 @@ class Cons { 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 listOf(...xs) { let list = null; for(let i = xs.length-1; i >= 0; i--) list = new Cons(xs[i], list); return list; } + 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; @@ -895,27 +906,27 @@ const parseLambdaList = list => { } 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) { + 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: - if (!(opt[0] instanceof LispSymbol)) + if (!(x.car instanceof LispSymbol)) throw new Error(`Optional parameter name not a symbol`); - sections.optional.push([opt[0], UNDEFINED, UNDEFINED]); + sections.optional.push([x.car, UNDEFINED, UNDEFINED]); break; case 2: - if (!(opt[0] instanceof LispSymbol)) + if (!(x.car instanceof LispSymbol)) throw new Error(`Optional parameter name not a symbol`); - sections.optional.push([opt[0], opt[1], UNDEFINED]); + sections.optional.push([x.car, x.cdr.car, UNDEFINED]); break; case 3: { - if (!(opt[0] instanceof LispSymbol)) + if (!(x.car instanceof LispSymbol)) throw new Error(`Optional parameter name not a symbol`); - if (!(opt[2] instanceof LispSymbol)) + if (!(Cons.nth(x, 2) instanceof LispSymbol)) throw new Error(`Optional parameter svar not a symbol`); - sections.optional.push(opt); + sections.optional.push(Cons.toArray(x)); break; } default: @@ -949,6 +960,44 @@ const parseLambdaList = list => { } break; case 'key': + if (clSym(x, '&OPTIONAL')) { + throw new Error(`Misplaced &OPTIONAL`); + } else if (clSym(x, '&KEY')) { + throw new Error(`Repeated &KEY`); + } else if (clSym(x, '&REST')) { + state = 'rest'; + } else if (x instanceof LispSymbol) { + sections.key.push([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: + if (!(x.car instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + sections.optional.push([x.car, UNDEFINED, UNDEFINED]); + break; + case 2: + if (!(x.car instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + sections.optional.push([x.car, x.cdr.car, UNDEFINED]); + break; + case 3: { + if (!(x.car instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + if (!(Cons.nth(x, 2) instanceof LispSymbol)) + throw new Error(`Optional parameter svar not a symbol`); + sections.optional.push(Cons.toArray(x)); + break; + } + default: + throw new Error(`Invalid &OPTIONAL spec`); + } + } else { + throw new Error(`Optional argument not symbol or list`); + } break; case 'aux': break; @@ -1445,7 +1494,9 @@ var buf = new BufferedStream(), emitNode(sb.append.bind(sb), node); const code = sb.toString(); console.log('generated:', code); - console.log('evaled:', eval(code)); + const evald = eval(code); + console.log('evaled:', evald); + console.log('parsed', parseLambdaList(evald)); } })()