无论如何从Java调用Clojure宏?
以下是我要做的事情:
RT.var("clojure.core", "require").invoke(Symbol.create("clojure.contrib.prxml"));
Var prxml = RT.var("clojure.contrib.prxml", "prxml");
Var withOutStr = RT.var("clojure.core", "with-out-str");
String stringXML = (String) withOutStr.invoke((prxml.invoke("[:Name \"Bob\"]")));
prxml默认写入* out *,这就是为什么我需要用带有-out-str的宏包装它来返回字符串。
我收到此错误:
[java] java.lang.IllegalArgumentException: Wrong number of args (1) passed to: core$with-out-str
[java] at clojure.lang.AFn.throwArity(AFn.java:437)
[java] at clojure.lang.RestFn.invoke(RestFn.java:412)
[java] at clojure.lang.Var.invoke(Var.java:365)
[java] at JavaClojure.xml.main(Unknown Source)
答案 0 :(得分:7)
你必须自己使用outSutStr。
class YourClass {
static final Var withBindings = RT.var("clojure.core", "with-bindings*");
static final Var list = RT.var("clojure.core", "list*");
static final Var out = RT.var("clojure.core", "*out*");
static final Var prxml = RT.var("clojure.contrib.prxml", "prxml");
static String withOutStr(IFn f, Object args...) {
StringWriter wtr = new StringWriter();
withBindings.applyTo(list.invoke(RT.map(out, wtr), f, args));
return wtr.toString();
}
...
String stringXML = withOutStr(prxml, "[:Name \"Bob\"]");
}
答案 1 :(得分:5)
问题是基本的。
invoke(和它的姐妹,apply)用于函数。
宏不是函数,因此无法调用它们。需要编译宏。在正常的Lisps中,它们可以被评估或宏扩展或其他任何东西。在10米的环顾中,显然Clojure没有简单的RT.eval(String脚本)功能来实现这一目标。
但是,这就是需要做的事情。你需要编译并执行它。
我看到package将Clojure与Java JSR 223集成在一起,IT有一个eval(因为223有一个eval)。但我不知道a)包装是否有用或b)如果这是您想要的方向。
答案 2 :(得分:1)
免责声明:我对clojure知之甚少(我的经验是使用其他函数式语言和Java)
然而,我的直觉表明问题出在prxml.invoke()
左右。这里的想法是该语句过早评估,并将结果发送到withOutStr(而不是让withOutStr评估它)。
单独在线查看来源......特别是RT,Var& AFn以及with-out-str的clojure doc我会尝试以下内容:
String stringXML = (String) withOutStr.invoke(RT.list(prxml,"[:Name \"Bob\"]"));
编辑:我也怀疑它能够从java调用clojure宏,否则Var上的isMacro()函数看起来很傻......
编辑2:下载了clojure,并尝试了它...不起作用所以现在忽略它。
编辑3:with-out-str显然需要2个参数:
final Cons consXML = (Cons) withOutStr.invoke(prxml, RT.list("[:Name \"Bob\"]"));
final Object[] objs = RT.seqToArray(consXML);
System.out.println(Arrays.toString(objs));
的输出为:[clojure.core/let, [s__4095__auto__ (new java.io.StringWriter)], (clojure.core/binding [clojure.core/*out* s__4095__auto__] (clojure.core/str s__4095__auto__))]
我想知道这是否会评估一些有用的东西(不确定我是否在绑定上是正确的,必须弄清楚如何通过Java评估缺点。
编辑4:通过编译器和更多代码,似乎宏实际上有2个隐藏参数。见the commit 17a8c90
在编译器中复制方法我有:
final ISeq form = RT.cons(withOutStr, RT.cons(prxml, RT.cons("[:Name \"Bob\"]", null)));
final Cons consXML = (Cons) withOutStr.applyTo(RT.cons(form, RT.cons(null, form.next())));
System.out.println(consXML.toString());
// Output: (clojure.core/let [s__4095__auto__ (new java.io.StringWriter)] (clojure.core/binding [clojure.core/*out* s__4095__auto__] #'clojure.contrib.prxml/prxml "[:Name \"Bob\"]" (clojure.core/str s__4095__auto__)))
这似乎更有希望,但它仍然需要评估let表达式,这似乎在编译器中有一个特例。
答案 3 :(得分:0)
如果要从C#或Java执行Clojure代码,请使用Clojure的load-string函数。这将采用普通字符串并执行它,就像您在REPL中键入字符串一样。这包括处理宏。
这是C#代码的简短版本。 Java版本远不止于此。
private static readonly IFn LOAD_STRING = Clojure.var("clojure.core", "load-string");
private void executeButton_Click(object sender, EventArgs e)
{
try
{
object result = LOAD_STRING.invoke(sourceTextBox.Text);
if (null == result)
resultTextBox.Text = "null";
else
resultTextBox.Text = result.ToString() + " (" + result.GetType().Name + ")";
}
catch (Exception ex)
{
resultTextBox.Text = ex.ToString();
}
}
您可以看到示例程序here的完整版本。它包含大量示例代码来演示Clojure interop。