git » jacl.git » commit e408cbd

quasiquote is go

author Alan Dipert
2019-10-17 23:10:12 UTC
committer Alan Dipert
2019-10-17 23:10:12 UTC
parent ddb5e10199d5266f04121d3968f32dc83e1a165d

quasiquote is go

jacl.js +76 -50

diff --git a/jacl.js b/jacl.js
index 8d20ee3..b00299f 100644
--- a/jacl.js
+++ b/jacl.js
@@ -10,18 +10,21 @@ class Cons {
     for(let i = xs.length-1; i >= 0; i--) list = new Cons(xs[i], list);
     return list;
   }
-  append(...xs) {
+  static append(...xs) {
     if (xs.length === 0) {
-      return this;
-    } else if (xs.length === 1 && !(xs[0] instanceof Cons)) {
-      const copy = Cons.listOf(...this);
+      return null;
+    } else if (xs.length === 1) {
+      return xs[0];
+    } else if (xs.length === 2 && !(xs[1] instanceof Cons)) {
+      const copy = Cons.listOf(...xs[0]);
       let last = copy;
       while (last.cdr) last = last.cdr;
-      last.cdr = xs[0];
+      last.cdr = xs[1];
       return copy;
+    } else if (xs.length === 2) {
+      return Cons.listOf(...xs[0], ...xs[1]);
     } else {
-      const [x, ...more] = xs;
-      return Cons.listOf(...this, ...x).append(...more);
+      return xs.reduce((a, b) => Cons.append(a,b));
     }
   }
   [Symbol.iterator]() {
@@ -704,50 +707,74 @@ 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(
+
+const transform = (form, wrapInList = true) => {
+  const maybeWrap = x => {
+    if (wrapInList) {
+      return 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');
+          new LispSymbol('listOf', null)
+        ),
+        x
+      );
+    } else {
+      return 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 && 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 if (object === null) {
+      return null;
     } else {
-      return Cons.listOf(CLPKG.intern('QUOTE'), argument);
+      return new Cons(transform(object.car), rec(object.cdr));
     }
   };
+  return rec(compound);
+};
+
+const ppr = x => {
+  if (x instanceof Cons) {
+    return Array.from(x).map(ppr);
+  } else if (x instanceof LispSymbol) {
+    return x.name;
+  } else {
+    return x.toString();
+  }
+};
+
+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) {
+    const foo = Cons.listOf(
+      JACLPKG.intern('%CALL'),
+      Cons.listOf(JACLPKG.intern('%DOT'), JSPKG.intern('Cons'), new LispSymbol('append', null)),
+      ...transformCompound(argument)
+    );
+    return foo;
+  } else {
+    return Cons.listOf(CLPKG.intern('QUOTE'), argument);
+  }
+};
+
+JACLPKG.intern('QUASIQUOTE').setMacro().fvalue = function(env, form) {
   const expand = form => {
     if (form instanceof Cons) {
       const expanded = new Cons(expand(form.car), expand(form.cdr));
@@ -956,7 +983,6 @@ 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`)
   }
 };
@@ -1131,11 +1157,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));
   }
 })()