git » jacl.git » commit ab173ff

wip analysis

author Alan Dipert
2019-10-09 05:23:14 UTC
committer Alan Dipert
2019-10-09 05:23:14 UTC
parent 038f3a1dbcf8a7ddf0a55d590e89d5dde3336992

wip analysis

jacl.js +124 -2

diff --git a/jacl.js b/jacl.js
index ae969cc..0691e55 100644
--- a/jacl.js
+++ b/jacl.js
@@ -492,6 +492,43 @@ const isConstituent = ch => {
     || /[0-9]/.test(ch);
 }
 
+const symCharNames = new Map([
+  ['!', '_BANG_'],
+  ['$', '_DOLLAR_'],
+  ['%', '_PERCENT_'],
+  ['&', '_AMP_'],
+  ['*', '_STAR_'],
+  ['+', '_PLUS_'],
+  ['-', '_DASH_'],
+  ['.', '_DOT_'],
+  ['/', '_SLASH_'],
+  [':', '_COLON_'],
+  ['<', '_LT_'],
+  ['=', '_EQUAL_'],
+  ['>', '_GT_'],
+  ['?', '_QMARK_'],
+  ['@', '_AT_'],
+  ['[', '_LBRACE_'],
+  [']', '_RBRACE_'],
+  ['^', '_CARET_'],
+  ['_', '_UNDERSCORE_'],
+  ['{', '_LBRACK_'],
+  ['}', '_RBRACK_'],
+  ['~', '_TILDE_']
+]);
+
+const munge = s => {
+  let munged = '';
+  for (const ch of s) {
+    if (symCharNames.has(ch)) {
+      munged += symCharNames.get(ch);
+    } else {
+      munged += ch;
+    }
+  }
+  return munged;
+};
+
 class Token {
   constructor(initial = '') {
     this.str = initial;
@@ -589,8 +626,8 @@ class Reader {
   }
 }
 
-// Special forms related to interop
-for (const s of ['%DOT', '%CALL']) {
+// Special forms
+for (const s of ['%DOT', '%CALL', '%LAMBDA']) {
   JACLPKG.intern(s);
 }
 
@@ -653,6 +690,89 @@ const isLambdaForm = form => {
     && form.car.name === 'LAMBDA';
 }
 
+const assoc = (...objs) => Object.assign({}, ...objs);
+
+//(def specials '#{if def fn* do let* loop recur new set! ns})
+
+const specials = new Map([
+  [JACLPKG.intern("%CALL"), (env, form) => {
+  }]
+]);
+
+const parseCall = (env, [func, ...args]) => {
+  env = assoc(env, {context: "expr"});
+  const fenv = assoc(env, {context: "fexpr"})
+  return {
+    env: env,
+    op: "call",
+    f: analyze(fenv, func),
+    args: args.map(analyze.bind(null, env))
+  };
+};
+
+const analyzeList = (env, form) => {
+  if (isMacroForm(form)) {
+    while (isMacroForm(form)) {
+      const [sym, ...args] = form;
+      form = sym.func()(...args);
+    }
+    return analyze(env, form);
+  } else if (specials.has(form.car)) {
+    return specials.get(form.car)(env, form);
+  } else if (isLambdaForm(form) || form.car instanceof LispSymbol) {
+    return parseCall(env, form);
+  } else {
+    throw new Error(`Invalid call: ${form}`)
+  }
+};
+
+const analyzeSymbol = (env, form) => {
+  const ret = (x) => assoc({ env: env, form: form }, x);
+  if (env.context === "fexpr") {
+    if (env.flocals.has(form)) {
+      return ret({ op: "local", name: env.flocals.get(form) });
+    } else {
+      return ret({ op: "symfunc" });
+    }
+  } else if (env.context === "expr") {
+    if (env.vlocals.has(form)) {
+      return ret({ op: "local", name: env.get('vlocals').get(form) });
+    } else {
+      return ret({ op: "symval" });
+    }
+  } else {
+    throw new Error(`Unknown context: ${env.context}`);
+  }
+};
+
+const emptyEnv = { 
+  vlocals: new Map(),
+  flocals: new Map(),
+  context: "expr"
+};
+
+const analyze = (env, form) => {
+  if (form instanceof LispSymbol) {
+    return analyzeSymbol(env, form);
+  } else if (form instanceof Cons) {
+    return analyzeList(env, form);
+  } else {
+    return mapOf("op", "constant", "env", env, "form", form);
+  }
+};
+
+const compileLambda = ([, args, ...body], env) => {
+  env = new Set(...args, env);
+  let bodyStr = '';
+  if (body.length == 1) {
+    bodyStr = `return ${compile(body[0], env)};`;
+  } else if (body.length > 1) {
+    bodyStr += body.slice(0, -1).map(x => compile(x, env)).join(';');
+    bodyStr += `return ${compile(body[body.length-1], env)};`;
+  }
+  return `(function(${args.map(munge).join(',')}){${bodyStr}})`
+};
+
 const compile = (form, env) => {
   if (form instanceof Number || typeof form === 'number') {
     return form.toString();
@@ -698,6 +818,8 @@ const compile = (form, env) => {
         return compile(arg1, env);
       }
       return compile(arg1, env);
+    } else if (JACLPKG.intern('%LAMBDA') === op) {
+      return compileLambda(form, env);
     } else if (op instanceof LispSymbol) {
       return `LispSymbol.intern('${op.packageName}', '${op.name}').func()(` +
         args.map(x => compile(x, env)).join(',') +