我正在尝试实现Processing的Lisp版本,为此,我使用macro_lisp
板条箱在编译时将Lisp代码转换为Rust。
当我按如下方式构建代码时,它可以工作:
main.rs
fn main() {
include!("hello.lisp");
}
hello.lisp
lisp!(println "hello")
请注意,我必须将hello.lisp
中lisp!()
的内容包装在hello.lisp
中。
我希望它的结构如下:
main.rs
fn main() {
lisp!(include!("hello.lisp"));
}
hello.lisp
println "hello"
但这给了我以下错误:
error: expected expression, found `<eof>`
--> src/main.rs:47:18
|
47 | lisp!(include!("draw.lisp"));
| ^ expected expression
那里应该没有EOF,应该有hello "list"
。
我在做什么错?我需要打补丁macro_lisp
吗?
答案 0 :(得分:4)
不幸的是,您想要的并不容易实现。
宏的功能与功能明显不同。此问题的重要部分是“嵌套宏调用”是从“外部到内部”评估的(与函数不同,函数首先评估参数,因此“内部到外部”)。我们可以通过这个小程序看到效果:
Double
您会看到on the Playground,它会打印macro_rules! foo {
($x:literal) => { "literal" };
($x:ident ! ()) => { "ident ! ()" };
}
macro_rules! bar {
() => { 3 };
}
fn main() {
let s = foo!(bar!());
println!("{}", s);
}
。这意味着在评估ident ! ()
之前未评估bar!()
宏。 (此外,编译器甚至会警告未使用的宏定义foo!
。)
bar
也不例外,因此我们不能将其用作其他宏的参数。那么可以怎么做呢?我可以想到两种方式,这两种方式都不是特别容易或优雅的:
编写一个程序宏,该宏加载文件并发出令牌流include!
,其中lisp! { ... }
是文件内容。正确设置所有路径(并确保Lisp文件更改时确保正确重新编译所有路径)可能很棘手,但从理论上讲应该可以。
使用构建脚本将源代码中的...
字符串手动替换为文件内容。显然,您实际上并不想修改您的真实源代码(已签入git),但必须对此有所了解。一些箱子使用这种策略,但仅出于非常特殊的原因。在您的情况下,我不建议这样做。
在您的情况下,我会三思而后行,在Rust中使用很多LISP代码是否是一个好主意,因为没有任何好的方法可以使它工作(据我所知)。