author | Alan Dipert
<alan@dipert.org> 2019-11-04 05:41:17 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-11-04 05:41:17 UTC |
parent | 88007b81682bc209e3131047a4b168210086c8c5 |
jacl.js | +64 | -19 |
diff --git a/jacl.js b/jacl.js index edc1a45..d6c7f2b 100644 --- a/jacl.js +++ b/jacl.js @@ -888,8 +888,10 @@ const parseLambdaList = list => { optionalSvars: [], // null or symbol rest: null, - // Map of keyword => [symbol, expr, symbol] for key => [name, init, svar] - key: new Map(), + // Array of [symbol, expr, symbol] for [key, name, init, svar] + key: [], + // Array of &key svar names (symbols) + keySvars: [], // Array of [symbol, expr] for [name, init] aux: [] }, @@ -972,23 +974,28 @@ const parseLambdaList = list => { state = 'aux'; } else if (x instanceof LispSymbol) { const key = LispSymbol.intern('KEYWORD', x.name); - if (sections.key.has(key)) - throw new Error(`Duplicate key: ${key.name}`); - sections.key.set(key, { + sections.key.push({ + key: key, name: checkValidLocal(x), initform: UNDEFINED, svar: UNDEFINED }); } else if (Cons.isProperList(x)) { - throw new Error(`TODO`); - //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 - //}); + const len = Cons.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)); + sections.keySvars.push(svar); + } + sections.key.push({ + key: key, + name: checkValidLocal(x.car), + initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED, + svar: svar + }); } else { throw new Error(`&KEY parameter not symbol or valid list`); } @@ -1452,7 +1459,8 @@ const emitNode = (print, node) => { print('){\n'); // Emit argument length checks const min = node.lambdaList.required.length, - max = node.lambdaList.rest ? false : min + node.lambdaList.optional.length; + hasRest = node.lambdaList.rest || node.lambdaList.key.length, + max = hasRest ? false : min + node.lambdaList.optional.length; if (min >= 0 && min === max) { print(`if (arguments.length !== ${min}) throw new Error('Called with invalid number of arguments: ' + arguments.length);\n`); } else { @@ -1463,9 +1471,8 @@ const emitNode = (print, node) => { print(`if (arguments.length > ${max}) throw new Error('Called with too many arguments: ' + arguments.length);\n`); } } - // &optional stuff + // &optional if (node.lambdaList.optional.length) { - // Speculatively bind &optional svars to true if (node.lambdaList.optionalSvars.length) { print('var '); for (let i = 0; i < node.lambdaList.optionalSvars.length; i++) { @@ -1510,8 +1517,46 @@ const emitNode = (print, node) => { print(`Cons.fromArray(Array.prototype.slice.call(arguments, ${restStart}));\n`); } // &key - if (node.lambdaList.key) { - // TODO + if (node.lambdaList.key.length) { + if (node.lambdaList.keySvars.length) { + print('var '); + for (let i = 0; i < node.lambdaList.keySvars.length; i++) { + print(mungeSym(node.lambdaList.keySvars[i], 'local')); + print('=true'); + if (i < node.lambdaList.keySvars.length-1) print(','); + } + print(';\n'); + } + print('var '); + for (let i = 0; i < node.lambdaList.key.length; i++) { + print(mungeSym(node.lambdaList.key[i].name, 'local')); + print('=null') + if (i < node.lambdaList.key.length-1) print(','); + } + print(';\n'); + print(`if((arguments.length-${restStart})%2)throw new Error('Odd number of &key arguments');\n`); + print('var keyVals = {};\n'); + print(`for(var i=${restStart}; i<arguments.length; i+=2){\n`); + print(`if (!(arguments[i] instanceof LispSymbol) || arguments[i].packageName!=='KEYWORD')throw new Error('Not a keyword: '+arguments[i]);\n`); + print('keyVals[arguments[i].name]=arguments[i+1];\n'); + print('}\n'); + for (const kspec of node.lambdaList.key) { + print(`if(keyVals.hasOwnProperty('${escapeSingle(kspec.key.name)}')){\n`); + print(mungeSym(kspec.name, 'local')); + print(`=keyVals['${escapeSingle(kspec.key.name)}'];\n`); + if (kspec.initform !== UNDEFINED) { + print('}else{\n'); + print(mungeSym(kspec.name, 'local')); + print('='); + emitNode(print, kspec.initform); + print(';\n'); + if (kspec.svar !== UNDEFINED) { + print(mungeSym(kspec.svar, 'local')); + print('=false;\n'); + } + } + print('}\n'); + } } emitBlock(print, node.statements, node.ret); print('})');