Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Last active October 6, 2016 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pervognsen/350244 to your computer and use it in GitHub Desktop.
Save pervognsen/350244 to your computer and use it in GitHub Desktop.
/*
It is a reader extension for list hoisting and splicing. You can write
(a `b` c) and it will be read as (b a c). It is right recursive, so
that (a `b` c `d` e) is read as (b a (d c e)). When the unhoisted
segments are lists of length greater than one, they are wrapped in a
singleton list rather than spliced. Thus (a b `c` d e) is read as (c
(a b) (d e)). The splicing and hoisting tries to have a natural DWIM
feel.
While this lets you perform surgery on any lists in the reader, its
strongest point is obviously infix function applications. Thus you can
write x `+` y `+` z. The mechanics of hoisting and splicing is such
that all infix operators are effectively right associative with
uniform precedence. Thus x `*` y `+` z is read as (* x (+ y z)).
Here is a quick demo transcript from the REPL:
Clojure 1.2.0-master-SNAPSHOT
user=> (defn dwim [x] (if (coll? x) (vec x) [x]))
#'user/dwim
user=> (defmacro => [bindings body] `(fn ~(dwim bindings) ~body))
#'user/=>
user=> (defmacro where [body bindings] `(let ~(dwim bindings) ~body))
#'user/where
user=> (defmacro || [body bindings] `(for ~(dwim bindings) ~body))
#'user/||
user=> (def add (x y `=>` x `+` y))
#'user/add
user=> (add 3 5)
8
user=> (def curried-add (x `=>` y `=>` x `+` y))
#'user/curried-add
user=> ((curried-add 3) 5)
8
user=> ((x `+` y `+` z) `where` x 3, y (x `+` 2), z (x `*` y))
23
user=> ((x `+` y) `||` x (range 10), y (range 10), :when (even? x))
(0 1 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9 10 11 4 5 6 7 8 9 10 11 12 13 6 7
8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 17)
user=> (0 `cons` 2 `cons` [1 2] `(map +)` [3 4])
(0 2 4 6)
*/
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index dc8ce97..62e3b75 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -37,6 +37,7 @@ static Symbol VECTOR = Symbol.create("clojure.core", "vector");
static Symbol WITH_META = Symbol.create("clojure.core", "with-meta");
static Symbol META = Symbol.create("clojure.core", "meta");
static Symbol DEREF = Symbol.create("clojure.core", "deref");
+static Symbol INFIX = Symbol.intern("infix");
//static Symbol DEREF_BANG = Symbol.create("clojure.core", "deref!");
static IFn[] macros = new IFn[256];
@@ -686,7 +687,18 @@ public static class SyntaxQuoteReader extends AFn{
RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
Object form = read(r, true, null, true);
- return syntaxQuote(form);
+
+ int ch = r.read();
+ if ((char) ch == '`') {
+ if (!(form instanceof IObj))
+ throw new Exception("Backtick bracketing only works with metadata carriers (IObjs).");
+ IObj o = (IObj) form;
+ IPersistentMap meta = o.meta() != null ? o.meta() : PersistentArrayMap.EMPTY;
+ return o.withMeta(meta.assoc(INFIX, true));
+ }
+ unread(r, ch);
+
+ return syntaxQuote(form);
}
finally
{
@@ -897,7 +909,7 @@ public static class ListReader extends AFn{
List list = readDelimitedList(')', r, true);
if(list.isEmpty())
return PersistentList.EMPTY;
- IObj s = (IObj) PersistentList.create(list);
+ IObj s = (IObj) infixup(PersistentList.create(list));
// IObj s = (IObj) RT.seq(list);
if(line != -1)
return s.withMeta(RT.map(RT.LINE_KEY, line));
@@ -905,6 +917,38 @@ public static class ListReader extends AFn{
return s;
}
+ static private Object revcat(Object initial, Object tail) throws Exception {
+ for (ISeq s = RT.seq(initial); s != null; s = s.next())
+ tail = RT.cons(s.first(), tail);
+ return tail;
+ }
+
+ static private Object wrap(Object list) throws Exception {
+ return RT.count(list) > 1 ? new PersistentList(list) : list;
+ }
+
+ static private Object splice(Object initial, Object tail) throws Exception {
+ return revcat(wrap(revcat(initial, null)), wrap(tail));
+ }
+
+ static private Object concat(Object initial, Object tail) throws Exception {
+ if (initial instanceof IPersistentList)
+ return revcat(revcat(initial, null), tail);
+ else
+ return RT.cons(initial, tail);
+ }
+
+ static private Object infixup(Object list) throws Exception {
+ Object left = null;
+ for(ISeq s = RT.seq(list); s != null; s = s.next()) {
+ Object o = s.first();
+ if (RT.meta(o) != null && RT.meta(o).containsKey(INFIX))
+ return concat(o, splice(left, infixup(s.next())));
+ left = RT.cons(o, left);
+ }
+ return list;
+ }
+
}
static class CtorReader extends AFn{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment