4个版本 (破坏性更新)

0.4.0 2024年1月26日
0.3.0 2024年1月18日
0.2.0 2024年1月4日
0.0.1 2023年12月26日

#603过程宏

Download history 1/week @ 2024-05-03 5/week @ 2024-05-17 2/week @ 2024-05-24 15/week @ 2024-05-31 15/week @ 2024-06-07 9/week @ 2024-06-14 7/week @ 2024-06-21 3/week @ 2024-06-28 2/week @ 2024-07-05 7/week @ 2024-07-12 14/week @ 2024-07-19 29/week @ 2024-07-26

每月52次下载
8 个crates中(2个直接使用) 使用

MIT/Apache

42KB
790

目录

代码产品库

该库通过生成代码生成来扩展一个 TokenTree 到另一个 TokenTree 的引擎。

产品语法

产品语法包含以下三个元素,下面将详细介绍

  1. 使用花括号定义产品的作用域
    ${ ... }
    
  2. 线性作用域,按顺序展开所有定义而不是生成产品
    $[ ... ]
    
  3. 使用括号定义
    $( ... )
    
  4. 引用,可以是索引的或命名的
    $0 $foo
    

由于我们使用Rust TokenTrees,唯一的要求是一切都要进行标记化。这意味着文字必须正确格式化,所有开括号都必须关闭。

所有与产品语法相关的语法元素都从井号 $ 开始。

产品作用域

允许在 $ 后跟花括号中的本地产品定义。这些作用域从内向外评估,并展开所有定义的产品;非本地的命名引用将被转发到父作用域。

${
    $(name: (one)(two))
    $(number: (1)(2))
    $name:$number ;
}

展开为

one:1 ;
one:2 ;
two:1 ;
two:2 ;

线性作用域

允许在 $ 后跟方括号中的本地定义。线性作用域中的所有定义必须有相同数量的元素。这些作用域从内向外评估,并将所有定义的元素 一起 展开,而不是其产品;非本地的命名引用将被转发到父作用域。

$[
    $(name: (one)(two))
    $(number: (1)(2))
    $name:$number ;
]

展开为

one:1 ;
two:2 ;

定义

一个嵌套括号的列表。内部括号包含生成的Rust令牌。产品定义可以是命名的或匿名的,隐藏的或可见的。这导致三种主要形式(不支持匿名隐藏)。对于只有一个项目的命名定义,可以省略括号。

名称可以用于在产品中的多个位置引用产品。这同样适用于外部作用域的名称。未命名的定义只能通过产品列表中的数字位置来引用;这些将仅是局部的,而不是引用外部作用域。

隐藏的定义在其定义位置不会展开,但可以在以后引用。这在对一个生产首先进行定义并多次使用时非常有用。可见的定义将在其定义位置展开,也可以在以后引用。

  • $((a)(b)) - 未命名,可见
    定义一个未命名的产品将在其位置隐式地留下一个数字引用。因此,它将在其定义位置展开。这对于定义只使用一次的生产特别有用。
  • $($name:(a)(b)) - 命名,可见
    在命名的生产名称前使用 $ 将在其位置留下一个引用。因此,它将在其定义位置展开。
  • $(name:(a)(b)) - 命名,隐藏
    在命名的生产名称前不使用 $ 将不会留下任何引用。因此,它不会在其定义位置展开。这可以在想要在作用域的开始定义一组生产并在以后引用它们时使用。
  • $($name:item)$(name:item) - 单个项目的特殊形式
    单个命名的非括号项可以不包含周围的括号。注意:当项目需要括号时,需要将其放在双括号中。

产品定义的编号从一开始;命名定义包含在编号中。

注意 1
生产只能包含 Rust 令牌,不能包含产品定义或引用。目前为了简化而特意选择这样做。

注意 2
代码展开是所有定义产品的乘积。未引用的隐藏产品仍会多次创建相同的代码。这同样适用于空定义

$(unused: (1)()(3))
$(used: (a)(b))
foo($used)

将展开为

foo(a)
foo(a)
foo(a)
foo(b)
foo(b)
foo(b)

引用

引用用于引用先前定义的生产。它们以 $ 后跟一个数字或命名的产品定义的名称书写。当命名时,这可以引用外部作用域中的名称。编号引用仅在定义它们的产品的范围内有效;索引从零开始。

未解析的引用将导致编译错误。必须在使用之前定义所有内容。

转义$字符

使用 $ 字符来开始产品语法元素。要使用字面量 $ 字符,只需将其加倍。

示例

以下三个示例都将展开为

impl Trait<Foo> for MyType<Foo> {}
impl Trait<Bar> for MyType<Bar> {}
impl Trait<Baz> for MyType<Baz> {}
  1. 使用未命名的产品定义
impl Trait<$((Foo)(Bar)(Baz))> for MyType<$1> {}
  1. 使用命名的隐藏产品定义
$(T: (Foo)(Bar)(Baz))
impl Trait<$T> for MyType<$T> {}
  1. 使用命名、可见的产品定义
impl Trait<$($T: (Foo)(Bar)(Baz))> for MyType<$T> {}

类似于预处理器的过程宏替换

当产品定义仅包含一个项目时,代码产品扩展行为类似于简单的预处理器,替换定义的产品。此时括号变为可选。例如

$(T: Foo)
$(U: Bar)
impl Trait<$T> for MyType<$U> {}

将展开为

impl Trait<Foo> for MyType<Bar> {}

EBNF语法

以下是作为参考的非正式代码产品语法EBNF。实际实现根据选择的ScopeMode略有不同。

start = { product_entity | rust } ;
rust = ?any_valid_rust_token? ;
product_entity = "$" ( scope | definition | reference ) ;
scope = product_scope | linear_scope ;
product_scope = "{" { product_entity | rust } "}" ;
linear_scope = "[" { product_entity | rust } "]" ;
definition = "(" [ "$" name ":" ] { product } ")" ;
product = "(" { rust } ")" ;
name = ?identifier? ;
reference = "#" ( ?number? | ?identifier? ) ;

依赖项

~58KB