package org.enumerable.lambda.support.clojure;
import static clojure.lang.Compiler.*;
import static clojure.lang.RT.*;
import static java.util.Arrays.*;
import static org.enumerable.lambda.exception.UncheckedException.*;
import java.io.StringReader;
import org.enumerable.lambda.Fn0;
import org.enumerable.lambda.Fn1;
import org.enumerable.lambda.Fn2;
import org.enumerable.lambda.Fn3;
import org.enumerable.lambda.annotation.NewLambda;
import org.enumerable.lambda.exception.LambdaWeavingNotEnabledException;
import clojure.lang.AFunction;
import clojure.lang.IFn;
import clojure.lang.IObj;
import clojure.lang.IPersistentMap;
import clojure.lang.LispReader;
import clojure.lang.Namespace;
import clojure.lang.PersistentList;
import clojure.lang.Symbol;
import clojure.lang.Var;
/**
* This is class is similar {@link org.enumerable.lambda.Lambda}, but instead of creating
* lambdas inheriting from {@link org.enumerable.lambda.Fn0} it creates lambdas implementing
* the {@link clojure.lang.IFn} interface, and can used together with
* {@link ClojureSeqs} and {@link clojure.lang.RT}.
*/
@SuppressWarnings("serial")
public class LambdaClojure {
static {
init();
}
static void init() {
try {
if (CURRENT_NS.get() == CLOJURE_NS)
CURRENT_NS.doReset(Namespace.findOrCreate(Symbol.create("user")));
} catch (Exception e) {
throw uncheck(e);
}
}
/**
* Defines a {@link IFn} from a lambda in the current namespace, like this:
*
* <pre>
* Var square = defn("square", fn(n, n * n));
* </pre>
*/
public static Var defn(String name, IFn body) {
return def(name, body);
}
/**
* Defines a var in the current namespace.
*/
public static Var def(String name, Object value) {
try {
return var(CURRENT_NS.get().toString(), name, value);
} catch (Exception e) {
throw uncheck(e);
}
}
/**
* Turns the array of forms into a {@link PersistentList} and evaluates it
* using {@link clojure.lang.Compiler}.
* <p>
* The objects must be forms Clojure understands, basically anything the
* {@link LispReader} can produce.
* <p>
* Note that if you send in an {@link IFn} which is not a {@link Var}, the
* compiler will create a new instance of it, which may not be what you
* want. If you define your lambdas with {@link #defn(String, IFn)} it
* returns a {@link Var} which can be used here.
*/
@SuppressWarnings("unchecked")
public static <R> R eval(Object... forms) {
try {
return (R) clojure.lang.Compiler.eval(PersistentList.create(asList(forms)));
} catch (Exception e) {
throw uncheck(e);
}
}
/**
* Evaluates the code using {@link clojure.lang.Compiler}.
*/
@SuppressWarnings("unchecked")
public static <R> R eval(String code) {
try {
return (R) load(new StringReader(code));
} catch (Exception e) {
throw uncheck(e);
}
}
static abstract class AFnFnBase extends AFunction {
int arity;
IPersistentMap meta;
public IPersistentMap meta() {
return meta;
}
public AFnFnBase() {
arity = Fn0.getAndCheckArityForMethod(getImplementingClass(), getMethod());
}
String getMethod() {
return "invoke";
}
Class<?> getImplementingClass() {
return getClass();
}
}
public static abstract class AFn0 extends AFnFnBase {
public abstract Object invoke();
}
public static abstract class AFn1 extends AFn0 {
public Object invoke() {
return invoke(default$1());
}
protected Object default$1() {
throwArity(0);
return null;
}
public abstract Object invoke(Object arg1);
}
public static abstract class AFn2 extends AFn1 {
public Object invoke(Object arg1) {
return invoke(arg1, default$2());
}
protected Object default$2() {
throwArity(1);
return null;
}
public abstract Object invoke(Object arg1, Object arg2);
}
public static abstract class AFn3 extends AFn2 {
public Object invoke(Object arg1, Object arg2) {
return invoke(arg1, arg2, default$3());
}
protected Object default$3() {
throwArity(2);
return null;
}
public abstract Object invoke(Object arg1, Object arg2, Object arg3);
}
/**
* Creates a new lambda implementing {@link IFn} taking no arguments.
*/
@NewLambda
public static AFn0 fn(Object body) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new lambda implementing {@link IFn} taking one argument.
*/
@NewLambda
public static AFn1 fn(Object a1, Object body) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new lambda implementing {@link IFn} taking two arguments.
*/
@NewLambda
public static AFn2 fn(Object a1, Object a2, Object body) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Creates a new lambda implementing {@link IFn} taking three arguments.
*/
@NewLambda
public static AFn3 fn(Object a1, Object a2, Object a3, Object body) {
throw new LambdaWeavingNotEnabledException();
}
/**
* Wraps the {@link IFn} in a {@link Fn0}.
*/
public static Fn0<Object> toFn0(final IFn f) {
return new Fn0<Object>() {
public Object call() {
try {
return f.invoke();
} catch (Exception e) {
throw uncheck(e);
}
}
};
}
/**
* Wraps the {@link IFn} in a {@link Fn1}.
*/
public static Fn1<Object, Object> toFn1(final IFn f) {
return new Fn1<Object, Object>() {
public Object call() {
try {
return f.invoke();
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1) {
try {
return f.invoke(a1);
} catch (Exception e) {
throw uncheck(e);
}
}
};
}
/**
* Wraps the {@link IFn} in a {@link Fn2}.
*/
public static Fn2<Object, Object, Object> toFn2(final IFn f) {
return new Fn2<Object, Object, Object>() {
public Object call() {
try {
return f.invoke();
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1) {
try {
return f.invoke(a1);
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1, Object a2) {
try {
return f.invoke(a1, a2);
} catch (Exception e) {
throw uncheck(e);
}
}
};
}
/**
* Wraps the {@link IFn} in a {@link Fn3}.
*/
public static Fn3<Object, Object, Object, Object> toFn3(final IFn f) {
return new Fn3<Object, Object, Object, Object>() {
public Object call() {
try {
return f.invoke();
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1) {
try {
return f.invoke(a1);
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1, Object a2) {
try {
return f.invoke(a1, a2);
} catch (Exception e) {
throw uncheck(e);
}
}
public Object call(Object a1, Object a2, Object a3) {
try {
return f.invoke(a1, a2, a3);
} catch (Exception e) {
throw uncheck(e);
}
}
};
}
/**
* Wraps the {@link Fn0} in an {@link IFn}.
*/
@SuppressWarnings("rawtypes")
public static IFn toIFn(final Fn0 f) {
return new AFn0() {
public IObj withMeta(IPersistentMap meta) {
AFn0 aFn = (AFn0) toIFn(f);
aFn.meta = meta;
return aFn;
}
public Object invoke() {
return f.call();
}
};
}
/**
* Wraps the {@link Fn1} in an {@link IFn}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static IFn toIFn(final Fn1 f) {
return new AFn1() {
public IObj withMeta(IPersistentMap meta) {
AFn1 aFn = (AFn1) toIFn(f);
aFn.meta = meta;
return aFn;
}
public Object invoke() {
if (arity > -1)
throwArity(0);
return f.call();
}
public Object invoke(Object arg1) {
return f.call(arg1);
}
Class<?> getImplementingClass() {
return f.getClass();
}
String getMethod() {
return "call";
}
};
}
/**
* Wraps the {@link Fn2} in an {@link IFn}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static IFn toIFn(final Fn2 f) {
return new AFn2() {
public IObj withMeta(IPersistentMap meta) {
AFn2 aFn = (AFn2) toIFn(f);
aFn.meta = meta;
return aFn;
}
public Object invoke() {
if (arity > -1)
throwArity(0);
return f.call();
}
public Object invoke(Object arg1) {
if (arity > -2)
throwArity(1);
return f.call(arg1);
}
public Object invoke(Object arg1, Object arg2) {
return f.call(arg1, arg2);
}
Class<?> getImplementingClass() {
return f.getClass();
}
String getMethod() {
return "call";
}
};
}
/**
* Wraps the {@link Fn3} in an {@link IFn}.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static IFn toIFn(final Fn3 f) {
return new AFn3() {
public IObj withMeta(IPersistentMap meta) {
AFn3 aFn = (AFn3) toIFn(f);
aFn.meta = meta;
return aFn;
}
public Object invoke() {
if (arity > -1)
throwArity(0);
return f.call();
}
public Object invoke(Object arg1) {
if (arity > -2)
throwArity(1);
return f.call(arg1);
}
public Object invoke(Object arg1, Object arg2) {
if (arity > -3)
throwArity(2);
return f.call(arg1, arg2);
}
public Object invoke(Object arg1, Object arg2, Object arg3) {
return f.call(arg1, arg2, arg3);
}
Class<?> getImplementingClass() {
return f.getClass();
}
String getMethod() {
return "call";
}
};
}
}