author | Alan Dipert
<alan@dipert.org> 2019-10-10 04:38:47 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-10 04:38:47 UTC |
parent | 63a0dbf7734a7765849e26cfa4b01b2350b822d5 |
index.html | +8 | -0 |
jacl.js | +56 | -29 |
diff --git a/index.html b/index.html index bde2a66..1fb1f73 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,14 @@ <meta name="viewport" content="width=device-width"> <title>JACL tests</title> <link rel="stylesheet" href="qunit-2.9.2.css"> + <style type="text/css"> + @media (prefers-color-scheme: dark) { + body { + color: #ddd; + background-color: black; + } + } + </style> </head> <body> <div id="qunit"></div> diff --git a/jacl.js b/jacl.js index ec22e30..9f4c13a 100644 --- a/jacl.js +++ b/jacl.js @@ -690,18 +690,40 @@ const isLambdaForm = form => { && form.car.name === 'LAMBDA'; } -const assoc = (...objs) => Object.assign({}, ...objs); +const merge = (...objs) => Object.assign({}, ...objs); //(def specials '#{if def fn* do let* loop recur new set! ns}) +const stringy = x => x instanceof String || typeof x === 'string' || x instanceof LispString; const analyzeSpecials = new Map([ + [JACLPKG.intern("%DOT"), (env, form) => { + const [, target, field] = form; + if (!(field instanceof LispSymbol || stringy(field))) + throw new Error(`%DOT field must be a symbol, JS string, or Lisp string`) + return { + op: "js-field", + env: env, + form: form, + target: analyze(merge(env, { context: "val" }), target), + field: field instanceof LispSymbol ? field.name : field.toString() + }; + }], [JACLPKG.intern("%CALL"), (env, form) => { + env = merge(env, { context: "val" }); + const [, func, ...args] = form; + return { + op: "call", + env: env, + form: form, + f: analyze(env, func), + args: args.map(analyze.bind(null, env)) + } }] ]); const parseCall = (env, [func, ...args]) => { - env = assoc(env, {context: "expr"}); - const fenv = assoc(env, {context: "fun"}) + env = merge(env, { context: "val" }); + const fenv = merge(env, { context: "fun" }) return { env: env, op: "call", @@ -727,34 +749,37 @@ const analyzeList = (env, form) => { }; const analyzeSymbol = (env, form) => { - if (env.context === "fun" || env.context === "val") { - if (env.locals[env.context].has(form)) { - return { - op: "local", - env: env, - form: form, - name: env.locals[env.context].get(form) - }; - } else { - return { - op: "sym", - ctx: env.context, - env: env, - form: form, - }; - } + ret = x => merge({ env: env, form: form }, x); + if (form.packageName === 'KEYWORD') { + return ret({ op: "constant" }); + } else if (form.packageName === 'JS' && !form.getPackage().isExported(form.name)) { + return ret({ op: "js-var", name: form.name }); + } else if (env.context === "fun" && env.locals.fun.has(form)) { + return ret({ op: "local", name: env.locals.fun.get(form) }); + } else if (env.locals.val.has(form)) { + return ret({ op: "local", name: env.locals.val.get(form) }); } else { - throw new Error(`Unknown context: ${env.context}`); + return ret({ op: "global" }); } }; -const emptyEnv = { - locals: { - fun: new Map(), - val: new Map() - }, - context: "val" -}; +class Env { + constructor() { + this.locals = { + fun: new Map(), + val: new Map() + }; + this.context = "val"; + } + copy() { + const newEnv = new Env(); + newEnv.locals.fun = new Map(this.locals.fun); + newEnv.locals.val = new Map(this.locals.val); + return newEnv; + } +} + +const emptyEnv = new Env(); const analyze = (env, form) => { if (form instanceof LispSymbol) { @@ -845,9 +870,11 @@ var buf = new BufferedStream(), (async function() { for await(const obj of rdr) { console.log("read:", obj); - console.log("compiled:", compile(obj, null)); - console.log("evaled:", eval(compile(obj, null))); + console.log(analyze(emptyEnv, obj)); + //console.log("compiled:", compile(obj, null)); + //console.log("evaled:", eval(compile(obj, null))); } })() // buf.writeEach(String.raw`(\. (js |window|) (|alert| (to-js "hello, world!")))`) +// buf.writeEach('(jacl::%call (jacl::%dot js::|window| |alert|) "hi")')