author | Alan Dipert
<alan@dipert.org> 2019-10-31 03:42:55 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-31 03:42:55 UTC |
parent | e7badf39d8aea9e359526afaa0e709e7f5d7be0e |
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; } }