『科技排头』在Rust代码中编写Python是种怎样的体验?( 三 )


过程宏是作为编译器插件实现的 。 您需要编写一个函数 , 该函数可以访问编译器看到的单词流 , 然后就可以执行所需的任何操作 , 最后需要返回一个新的单词流供编译器使用(或者用于自定义的用途):
#[proc_macro]pubfnpython(input:TokenStream)->TokenStream{todo!}上述单词流不够好 。 因为我们需要源代码 , 而不仅仅是单词 。 虽然目前还没有成功 , 但是让我们继续吧 , 也许过程宏更大的灵活性能够解决问题 。
由于过程宏在编译过程中运行Rust代码 , 因此它们需要使用单独的proc-macro类库中 , 这个类库在您编译其他内容之前已经被编译好 。
$cargonew--libpython-macroCreatedlibrary`python-macro`package查看python-macro/Cargo.toml:
[lib]proc-macro=true查看Cargo.toml:
[dependencies]python-macro={path=''./python-macro''}让我们从一个只有panics(todo!)的实现开始 , 在输出TokenStream之后:
//python-macro/ src/lib.rsexterncrateproc_macro;useproc_macro::TokenStream;#[proc_macro]pubfnpython(input:TokenStream)->TokenStream{dbg!(input.to_string);todo!}// src/main.rsusepython_macro::python;fnmain{println!(''Hello...'');python!{print(''...World!'')print(''Bye.'')}}$cargorCompilingpython-macrov0.1.0Compilingscratchpadv0.1.0error[E0658]:proceduralmacroscannotbeexpandedtostatements--> src/main.rs:5:5|5|/python!{6||print(''...World!'')7||print(''Bye.'')8||}||_____^|=note:seeissue#54727<https://github.com/rust-lang/rust/issues/54727>formoreinformation=help:add`#![feature(proc_macro_hygiene)]`tothecrateattributestoenable天啊 , 这里发生了什么?
Rust错误为“过程宏不能扩展为语句” , 以及有关启用“hygienicmacros”的内容 。 Macrohygiene是Rust宏的出色功能 , 不会意外地将任何名称“泄漏”给外界(反之亦然) 。 如果宏扩展使用了名为的x的临时变量 , 则它将与宏外部的任何代码中出现的变量x分开 。
但是 , 此功能对于过程宏还不稳定 。 因此 , 过程宏除了作为一个单独的项(例如在文件范围内 , 但不在函数内)之外 , 不允许出现在任何地方 。
接下来 , 我们会发现存在一个非常可怕但令人着迷的解决方法—让我们启用实验功能#![feature(proc_macro_hygiene)]并继续我们的冒险 。
(如果你将来读到这篇文章时 , proc_macro_hygiene已经稳定下来了:你可以跳过最后几段 。 ^^)
$sed-i'1i#![feature(proc_macro_hygiene)]' src/main.rs$cargorCompilingscratchpadv0.1.0[python-macro/ src/lib.rs:6]input.to_string=''print(''...World!'')print(''Bye.'')''error:procmacropanicked--> src/main.rs:6:5|6|/python!{7||print(''...World!'')8||print(''Bye.'')9||}||_____^|=help:message:notyetimplementederror:abortingduetopreviouserrorerror:couldnotcompile`scratchpad`.在向我们展示了它的字符串输入参数之后 , 我们的过程宏即如预期般地崩溃了:
print(''...World!'')print(''Bye.'')
正如预期的那样 , 空白符再次被丢弃了 。 :(
是时候选择放弃了 。
不过或者..也许有一种方法可以解决这个问题 。
『科技排头』在Rust代码中编写Python是种怎样的体验?
文章图片
重建空白符
尽管rustc编译器只在解析和编译时使用单词 , 但是在某种程度上它仍然可以准确地知道何时报告错误 。 单词中没有换行符 , 但是它仍然知道我们的错误发生在第6到第9行 。 那它如何做到的?
事实证明 , 单词中包含很多信息 。 它们包含一个Span , 是单词在源文件中的开始和结束的位置 。 Span可以告诉单词在哪个文件、行和列编号处开始和结束 。