git » jacl.git » commit ddb5e10

wip backquote

author Alan Dipert
2019-10-17 21:27:41 UTC
committer Alan Dipert
2019-10-17 21:27:41 UTC
parent 795d29a7eb1b2ae991d29c95204c7b84f35fe236

wip backquote

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));
   }
 })()