author | Alan Dipert
<alan@dipert.org> 2019-11-03 16:28:22 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-11-03 16:28:22 UTC |
parent | 35eb9620bcb80fa7daed726c851c8012f3dc69b7 |
jacl.js | +40 | -14 |
diff --git a/jacl.js b/jacl.js index af46f4f..2b8c4cc 100644 --- a/jacl.js +++ b/jacl.js @@ -878,10 +878,17 @@ const parseLambdaList = list => { let arr = Cons.toArray(list), sections = { + // Array of symbols required: [], + // Array of [symbol, expr, symbol] for [name, init, svar] optional: [], + // Array of optional indexes that have svars + optionalWithSvars: [], + // null or symbol rest: null, + // Array of [keyword, symbol, expr, symbol] for [key, name, init, svar] key: [], + // Array of [symbol, expr] for [name, init] aux: [] }, state = 'required'; @@ -918,10 +925,15 @@ const parseLambdaList = list => { const len = Cons.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)); + sections.optionalWithSvars.push(sections.optional.length); + } sections.optional.push({ name: checkValidLocal(x.car), initform: len > 1 ? Cons.nth(x, 1) : UNDEFINED, - svar: len === 3 ? checkValidLocal(Cons.nth(x, 2)) : UNDEFINED + svar: svar }); } else { throw new Error(`&OPTIONAL parameter not symbol or valid list`); @@ -1419,32 +1431,46 @@ const emitNode = (print, node) => { if (context === 'sval') print(')()'); break; case 'lambda': { - // Emit argument names print('(function('); - print([ - ...node.lambdaList.required, - ...node.lambdaList.optional.map(o => o.name) - ].map(s => mungeSym(s, 'local')).join(',')); + // Emit required argument names + for (let i = 0; i < node.lambdaList.required.length; i++) { + print(mungeSym(node.lambdaList.required[i], 'local')) + if (i < node.lambdaList.required.length-1) print(','); + } + // Emit optional argument names + if (node.lambdaList.required.length && node.lambdaList.optional.length) + print(','); + for (let i = 0; i < node.lambdaList.optional.length; i++) { + print(mungeSym(node.lambdaList.optional[i].name, 'local')) + if (i < node.lambdaList.optional.length-1) print(','); + } print('){\n'); - // Emit argument length checks const min = node.lambdaList.required.length, max = node.lambdaList.rest ? false : min + node.lambdaList.optional.length; - - if (min > 0 && min === max) { + if (min >= 0 && min === max) { print(`if (arguments.length !== ${min}) throw new Error('Called with invalid number of arguments: ' + arguments.length);\n`); } else { - if (min > 0) { + if (min >= 0) { print(`if (arguments.length < ${min}) throw new Error('Called with too few arguments: ' + arguments.length);\n`); } if (max) { print(`if (arguments.length > ${max}) throw new Error('Called with too many arguments: ' + arguments.length);\n`); } } - - // TODO bind optional svars all to true - // TODO populate optionals, svars depending on arg length - + // Speculatively bind &optional svars to true + if (node.lambdaList.optionalWithSvars.length) { + print('var '); + for (let i = 0; i < node.lambdaList.optionalWithSvars.length; i++) { + const optIdx = node.lambdaList.optionalWithSvars[i], + ospec = node.lambdaList.optional[optIdx]; + print(mungeSym(ospec.svar, 'local')); + print('=true'); + if (i < node.lambdaList.optionalWithSvars.length-1) print(','); + } + print(';\n'); + } + // TODO Initialize &optionals and set svars depending on arg len emitBlock(print, node.statements, node.ret); print('})'); break;