author | Alan Dipert
<alan@dipert.org> 2019-10-17 21:27:41 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-17 21:27:41 UTC |
parent | 795d29a7eb1b2ae991d29c95204c7b84f35fe236 |
jacl.js | +74 | -10 |
diff --git a/jacl.js b/jacl.js index c5c9fa9..8d20ee3 100644 --- a/jacl.js +++ b/jacl.js @@ -10,6 +10,20 @@ class Cons { for(let i = xs.length-1; i >= 0; i--) list = new Cons(xs[i], list); return list; } + append(...xs) { + if (xs.length === 0) { + return this; + } else if (xs.length === 1 && !(xs[0] instanceof Cons)) { + const copy = Cons.listOf(...this); + let last = copy; + while (last.cdr) last = last.cdr; + last.cdr = xs[0]; + return copy; + } else { + const [x, ...more] = xs; + return Cons.listOf(...this, ...x).append(...more); + } + } [Symbol.iterator]() { let ptr = this, proper = true, @@ -682,16 +696,63 @@ JACLPKG.intern('.').setMacro().fvalue = function(topic, ...ops) { }; JACLPKG.exportSymbol('.'); -CLPKG.intern('QUOTE').setMacro().fvalue = function(x) { +CLPKG.intern('QUOTE').setMacro().fvalue = function(env, form, x) { return Cons.listOf(JACLPKG.intern('%QUOTE'), x); }; -JACLPKG.intern('QUASIQUOTE').setMacro().fvalue = function(x) { +JACLPKG.intern('UNQUOTE').setMacro().fvalue = function(env, form) { + throw new Error(`Comma not inside backquote`); +}; + +JACLPKG.intern('QUASIQUOTE').setMacro().fvalue = function(env, form) { + const transform = (form, wrapInList = true) => { + const maybeWrap = x => wrapInList ? Cons.listOf(x) : x; + if (form instanceof Cons && JACLPKG.intern('UNQUOTE') === form.car) { + return maybeWrap(form.cdr.car); + } else if (form instanceof Cons && JACLPKG.intern('UNQUOTE-SPLICING') === form.car) { + return form.cdr.car; + } else { + return maybeWrap(transformQuasiquoteArgument(form)); + } + }; + const transformCompound = compound => { + const rec = object => { + if (object instanceof Cons && (!(object.car instanceof Cons) || JACLPKG.intern('UNQUOTE') === object.car)) { + return Cons.listOf(transform(object.car), transform(object.cdr, false)); + } else if (object instanceof Cons && JACLPKG.intern('UNQUOTE-SPLICING') === object.car) { + throw new Error(`UNQUOTE-SPLICING in dotted list`); + } else { + return new Cons(transform(object.car), rec(object.cdr)); + } + }; + return rec(compound); + }; + const transformQuasiquoteArgument = argument => { + if (argument instanceof Cons && JACLPKG.intern('UNQUOTE') === argument.car) { + return argument.cdr.car; + } else if (argument instanceof Cons && JACLPKG.intern('UNQUOTE-SPLICING') === argument.car) { + throw new Error(`UNQUOTE-SPLICING at top`); + } else if (argument instanceof Cons) { + console.log(argument); + const out = Cons.listOf( + JACLPKG.intern('%CALL'), + Cons.listOf( + JACLPKG.intern('%DOT'), + JSPKG.intern('Cons'), + new LispSymbol('append', null) + ) + ).append(transformCompound(argument)); + console.log(out); + throw new Error('omg'); + } else { + return Cons.listOf(CLPKG.intern('QUOTE'), argument); + } + }; const expand = form => { if (form instanceof Cons) { const expanded = new Cons(expand(form.car), expand(form.cdr)); - if (JACLPKG.intern('QUASIQUOTE') === form.car) { - return transformQuasiquoteArgument(form.cdr.car); + if (JACLPKG.intern('QUASIQUOTE') === expanded.car) { + return transformQuasiquoteArgument(expanded.cdr.car); } else { return expanded; } @@ -699,6 +760,8 @@ JACLPKG.intern('QUASIQUOTE').setMacro().fvalue = function(x) { return form; } }; + + return expand(form); }; const readJsString = async stream => { @@ -885,7 +948,7 @@ const analyzeList = (env, parent, form) => { if (isMacroForm(form)) { while (isMacroForm(form)) { const [sym, ...args] = form; - form = sym.func()(...args); + form = sym.func()(env, form, ...args); } return analyze(env, parent, form); } else if (analyzeSpecials.has(form.car)) { @@ -893,6 +956,7 @@ const analyzeList = (env, parent, form) => { } else if (isLambdaForm(form.car) || form.car instanceof LispSymbol) { return parseCall(env, parent, form); } else { + console.log('call', form); throw new Error(`Invalid call`) } }; @@ -1067,11 +1131,11 @@ var buf = new BufferedStream(), console.log("read:", obj); const node = analyze(emptyEnv, null, obj); console.log("analyzed:", node); - const sb = new StringBuffer(); - emitNode(sb.append.bind(sb), node); - const code = sb.toString(); - console.log("generated:", code); - console.log("evaled:", eval(code)); + //const sb = new StringBuffer(); + //emitNode(sb.append.bind(sb), node); + //const code = sb.toString(); + //console.log("generated:", code); + //console.log("evaled:", eval(code)); } })()