6个版本

0.1.5 2024年1月29日
0.1.4 2024年1月29日
0.1.2 2023年10月15日
0.1.1 2023年4月2日

#92 in 进程宏

每月 35次下载

GPL-3.0-only

175KB
3K SLoC

do-with-in

crates.io License: GPL-3.0-only

基于部分分阶段的使用Rust元编程的模板语言。

这个crate允许你在编译时运行代码以生成其他proc_macro将消耗的标记。它也适用于即兴的现场代码生成,以及为您的代码制作模板(如Jinja或Mustache)。它使用基于处理器、变量和用户选择的符号的简单内部语言。Rust的阶段分离模型对该crate能做什么确实有一些限制。

目录

背景

这个包的想法是为了允许在我的幻想CPU模拟器中进行一种特定的重构,这使得代码折叠更容易,并且更容易进行实验;那个项目使用相当复杂的进程宏以类型安全和集成的方式来快速生成系统模拟器,用于在类型安全和集成方式下对不同系统设计进行实验,取代了大量非类型安全和非集成的即兴Perl脚本和构建系统代码生成。但是,在大型进程宏的使用地点进行重构不是Rust有很好故事的事情,所以我最终不得不自己写这个故事,导致了这个crate的出现。

尽管它可以用于一些类似的东西,但它也证明可以用于简单的代码模板化,这是声明性宏难以处理的。

限制

我们没有真正的反引号拼接,因为这在Rust中由于Rust的阶段分离性质而几乎不可能。

安装

cargo add do-with-in

用法

在其最简单的形式中,你可以用do_with_in!将其他proc_macro的调用包装起来,以便在调用该事物期间进行元编程 - 也就是说,在输入本身 - 使用一种相当基本的语言。

#[macro_use]
extern crate do_with_in;

use do_with_in::*;
// ...
fn main() {
    do_with_in!{
        sigil: ~ // One of $, %, #, or ~
        do // Separates front matter from body
        // Define a handler called `header` that wraps its argument
        ~(mk header <head><title> ~(run ~1) </title></head>)

        html!{
            <html>
                ~(header {"My title"})
            </html>
        }
    }
// ...
}

这里有一个使用 do_with_in! 进行站点元编程的例子:examples/typed-html.rs,并且当前最接近于记录如何使用提供的各种功能的测试位于 do_with_in_internal_macros/tests/do_with_in_test.rs

API

do_with_in!()

这是本crate大多数用户将使用的proc_macro。

有前置内容,可以定义符号;如果没有定义符号,则默认为 $。然后是 do,之后就是元编程发生的地方。

在元编程部分,变量是带有符号的前缀标识符。您可以使用 letvar 处理器创建和赋值。带有符号的前缀的数字是可以在处理器内部设置的特别变量;您不能使用 let 或 var 赋值。带有符号的前缀的括号开始处理器调用;调用的处理器将是括号内的第一个标记,它必须是一个标识符。其余所有内容都通过宏保持不变。

环境

处理器

默认处理器

如果您直接使用 do_with_in!,环境将预填充一组默认处理器,使其具有“电池包含”的体验。请参阅 fn.genericDefaultHandlers 了解已记录的默认处理器列表。

let & var

创建和赋值变量,带有前缀的标识符。两者的区别在于 let 在变量定义或使用过程中都不会进行插值,而 var 则两者都会进行插值。

使用 let 定义的变量的赋值值在它被使用之前保持不变

%(let bar = {let y = "bar"; })
%bar
//  y == "bar"

而使用 var 设置的变量可以引用其他元编程变量

%(let foo = { 5; })
%(var bar = { %foo })
x = %bar
// x == 5

运行

评估代码块

mk 标识符

在使用站点定义新处理器的一种简单方法。允许使用位置参数,例如 ~1

// mk defines the handler..
~(mk embolden <b> ~(run ~1) </b>)
// .. which can then be called with arguments
~(embolden {"World"})

concat 参数

将其参数连接成一个字符串。

let x = $(concat 1 "abc" 2);
// x == "1abc2"

withSigil 新符号 参数

重新定义处理器作用域中使用的符号。

$(let a = {"foo"})
let a = $(withSigil # #(concat #a "bar"));

导入

基本文件包含。路径由引号段指定;使用特殊未引号的标识符 Base 作为crate根。

$(import Base "src" "import.$")

可以在前置内容中包含 file: 提示,以允许导入使用相对路径。

do_with_in!{
  file: "src/importable.rs"
  sigil: $
  do
  $(import "import.$")
}

许可证

GPL-3.0-only © flaviusb.

查看 LICENSE 文件中的许可。

依赖项

~0.4–8.5MB
~66K SLoC