如何将文件的内容作为宏的参数包含在内?

时间:2019-01-29 21:35:36

标签: macros rust

我正在尝试实现Processing的Lisp版本,为此,我使用macro_lisp板条箱在编译时将Lisp代码转换为Rust。

当我按如下方式构建代码时,它可以工作:

main.rs

fn main() {
    include!("hello.lisp");
}

hello.lisp

lisp!(println "hello")

请注意,我必须将hello.lisplisp!()的内容包装在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吗?

1 个答案:

答案 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代码是否是一个好主意,因为没有任何好的方法可以使它工作(据我所知)。