Change quasiquote algorithm
[jackhill/mal.git] / impls / dart / step7_quote.dart
index f32a490..c8fd399 100644 (file)
@@ -23,31 +23,33 @@ void setupEnv(List<String> argv) {
       "(fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
 }
 
-MalType quasiquote(MalType ast) {
-  bool isPair(MalType ast) {
-    return ast is MalIterable && ast.isNotEmpty;
+bool starts_with(MalType ast, String sym) {
+  return ast is MalList && ast.length == 2 && ast.first == new MalSymbol(sym);
+}
+
+MalType qq_loop(List<MalType> xs) {
+  var acc = new MalList([]);
+  for (var i=xs.length-1; 0<=i; i-=1) {
+    if (starts_with(xs[i], "splice-unquote")) {
+      acc = new MalList([new MalSymbol("concat"), (xs[i] as MalList)[1], acc]);
+    } else {
+      acc = new MalList([new MalSymbol("cons"), quasiquote(xs[i]), acc]);
+    }
   }
+  return acc;
+}
 
-  if (!isPair(ast)) {
+MalType quasiquote(MalType ast) {
+  if (starts_with(ast, "unquote")) {
+    return (ast as MalList).elements[1];
+  } else if (ast is MalList) {
+    return qq_loop(ast.elements);
+  } else if (ast is MalVector) {
+    return new MalList([new MalSymbol("vec"), qq_loop(ast.elements)]);
+  } else if (ast is MalSymbol || ast is MalHashMap) {
     return new MalList([new MalSymbol("quote"), ast]);
   } else {
-    var list = ast as MalIterable;
-    if (list.first == new MalSymbol("unquote")) {
-      return list[1];
-    } else if (isPair(list.first) &&
-        (list.first as MalIterable).first == new MalSymbol("splice-unquote")) {
-      return new MalList([
-        new MalSymbol("concat"),
-        (list.first as MalIterable)[1],
-        quasiquote(new MalList(list.sublist(1)))
-      ]);
-    } else {
-      return new MalList([
-        new MalSymbol("cons"),
-        quasiquote(list[0]),
-        quasiquote(new MalList(list.sublist(1)))
-      ]);
-    }
+    return ast;
   }
 }
 
@@ -142,6 +144,8 @@ MalType EVAL(MalType ast, Env env) {
                     EVAL(args[1], new Env(env, params, funcArgs)));
           } else if (symbol.value == "quote") {
             return args.single;
+          } else if (symbol.value == "quasiquoteexpand") {
+            return quasiquote(args.first);
           } else if (symbol.value == "quasiquote") {
             ast = quasiquote(args.first);
             continue;