author | Alan Dipert
<alan@dipert.org> 2019-10-30 03:59:45 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-30 03:59:45 UTC |
parent | 3d28e6d41d980474176b82a074f00140c1c030fc |
jacl.js | +99 | -26 |
diff --git a/jacl.js b/jacl.js index b9aaed6..c7088b9 100644 --- a/jacl.js +++ b/jacl.js @@ -1,3 +1,4 @@ +// Sentinel used in a few places to distinguish from a user-provided value const UNDEFINED = new Object(); class Cons { @@ -320,6 +321,12 @@ for (const [k,v] of CLFUNCS) { CLPKG.exportSymbol(k); } +// Lambda list keywords +for (const kw of ['&OPTIONAL', '&REST', '&KEY', '&AUX']) { + CLPKG.intern(kw); + CLPKG.exportSymbol(kw); +} + // JS package constants const JSCONSTS = new Map([ ['+FALSE+', false], @@ -848,39 +855,105 @@ const analyzeBlock = (env, parent, forms) => { }; const parseLambdaList = list => { - const isKw = x => (x instanceof LispSymbol) && x.name[0] === '&'; + const clSym = (x, name) => (x instanceof LispSymbol) && + x.packageName === 'COMMON-LISP' && + x.name === name; + let arr = Cons.toArray(list), - section = 'required', sections = { required: [], optional: [], - keyword: [], - rest: null + rest: null, + key: [], + aux: [] }, - expectingKw = false; - scan: for (let i = 0; i < arr.length; i++) { - const x = list[i]; - if (x instanceof LispSymbol) { - switch (x.name) { - case '&OPTIONAL': - case '&KEY': - section = x.name.substring(1).toLowerCase(); - continue scan; - case '&REST': - if (section.rest !== null) throw new Error(`Multiple &REST`); - if (i == arr.length-1) throw new Error(`Nothing after &REST`); - if (arr.length-i !== 2) - throw new Error(`One argument name must follow &REST`); - sections.rest = arr[i+1]; - break scan; - default: - sections[section].push(x); - } - } else { - sections[section].push(x); + state = 'required'; + + while (arr.length) { + 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'; + } else if (x instanceof LispSymbol) { + sections.required.push(x); + } else { + throw new Error(`Required argument is not a symbol`); + } + break; + case 'optional': + if (clSym(x, '&OPTIONAL')) { + throw new Error(`Repeated &OPTIONAL`); + } else if (clSym(x, '&KEY')) { + state = 'key'; + } else if (clSym(x, '&REST')) { + state = 'rest'; + } 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) { + case 0: + throw new Error(`Empty optional parameter`); + case 1: + if (!(opt[0] instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + sections.optional.push([opt[0], UNDEFINED, UNDEFINED]); + break; + case 2: + if (!(opt[0] instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + sections.optional.push([opt[0], opt[1], UNDEFINED]); + break; + case 3: { + if (!(opt[0] instanceof LispSymbol)) + throw new Error(`Optional parameter name not a symbol`); + if (!(opt[2] instanceof LispSymbol)) + throw new Error(`Optional parameter svar not a symbol`); + sections.optional.push(opt); + break; + } + default: + throw new Error(`Invalid &OPTIONAL spec`); + } + } else { + throw new Error(`Optional argument not symbol or list`); + } + break; + case 'rest': + if (sections.rest) + throw new Error(`Repeated &REST`); + if (!(x instanceof LispSymbol)) + throw new Error(`&REST parameter not a symbol`); + sections.rest = x; + state = 'after-rest'; + break; + case 'after-rest': + if (x instanceof LispSymbol && + x.packageName === 'COMMON-LISP' && + (x.name === '&OPTIONAL' || + x.name === '&KEY' || + x.name === '&AUX')) { + state = x.name.substring(1).toLowerCase(); + if (sections[state].length) + throw new Error(`Duplicate ${x.name}`); + } else if (clSym(x, '&REST')) { + throw new Error(`Repeated &REST`); + } else { + throw new Error(`Expected keyword after &REST param`); + } + break; + case 'key': + break; + case 'aux': + break; } } - return sections; };