6 个版本 (3 个重大更新)
0.7.0 | 2023年11月1日 |
---|---|
0.6.2 | 2022年5月31日 |
0.5.0 | 2022年5月10日 |
0.4.1 | 2022年5月5日 |
0.1.0 |
|
#1499 在 解析器实现 中
每月下载量 229
71KB
1.5K SLoC
eventree – 用于创建无损语法树的 Rust 库。
请参阅文档。
lib.rs
:
用于创建无损语法树的 Rust 库。
让我们构建一个可以表示以下表达式的语法树
foo+10*20
这是我们想要构建的树
Root
BinaryExpr
Ident "foo"
Plus "+"
BinaryExpr
Number "10"
Star "*"
Number "20"
我们有哪些节点和标记?
enum NodeKind {
Root,
BinaryExpr,
}
enum TokenKind {
Number,
Ident,
Plus,
Star,
}
在我们使用这些枚举之前,我们必须教 eventree 如何在它们和 u16
之间进行转换,后者可以存储在语法树中,无论用户定义了什么枚举。我知道这有很多样板代码,而且所有那些 unsafe
看起来真的很吓人,但我保证这并不太糟糕!
#[derive(Debug, PartialEq)]
#[repr(u8)]
enum NodeKind {
Root,
BinaryExpr,
}
#[derive(Debug, PartialEq)]
#[repr(u8)]
enum TokenKind {
Number,
Ident,
Plus,
Star,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum TreeConfig {}
unsafe impl eventree::TreeConfig for TreeConfig {
type NodeKind = NodeKind;
type TokenKind = TokenKind;
fn node_kind_to_raw(node_kind: Self::NodeKind) -> u16 {
node_kind as u16
}
fn token_kind_to_raw(token_kind: Self::TokenKind) -> u16 {
token_kind as u16
}
unsafe fn node_kind_from_raw(raw: u16) -> Self::NodeKind {
std::mem::transmute(raw as u8)
}
unsafe fn token_kind_from_raw(raw: u16) -> Self::TokenKind {
std::mem::transmute(raw as u8)
}
}
继续创建一个 SyntaxBuilder
,它允许您构建语法树
let mut builder = eventree::SyntaxBuilder::<TreeConfig>::new("foo+10*20");
正如其名所示(感谢 Quirl!),eventree 是围绕 事件 构建的。为了解释这意味着什么,让我再回顾一下之前提到的语法树
Root
BinaryExpr
Ident "foo"
Plus "+"
BinaryExpr
Number "10"
Star "*"
Number "20"
现在,作为事件
START_NODE Root
START_NODE BinaryExpr
ADD_TOKEN Ident "foo"
ADD_TOKEN Plus "+"
START_NODE BinaryExpr
ADD_TOKEN Number "10"
ADD_TOKEN Star "*"
ADD_TOKEN Number "20"
FINISH_NODE
FINISH_NODE
FINISH_NODE
这一点很棒的是,我们已经将树结构转换成了一个扁平的序列。也许如果我用这种方式展示它会更明显
[
START_NODE Root,
START_NODE BinaryExpr,
ADD_TOKEN Ident "foo",
ADD_TOKEN Plus "+",
START_NODE BinaryExpr,
ADD_TOKEN Number "10",
ADD_TOKEN Star "*",
ADD_TOKEN Number "20",
FINISH_NODE,
FINISH_NODE,
FINISH_NODE,
]
eventree 做的事情是将像上面那样的事件序列存储在一个 高效格式 中,同时提供方便的 API 来遍历树。
在我们继续前进之前,让我们构建这个树
use eventree::{SyntaxBuilder, TextRange};
let mut builder = SyntaxBuilder::<TreeConfig>::new("foo+10*20");
builder.start_node(NodeKind::Root);
builder.start_node(NodeKind::BinaryExpr);
builder.add_token(TokenKind::Ident, TextRange::new(0.into(), 3.into()));
builder.add_token(TokenKind::Plus, TextRange::new(3.into(), 4.into()));
builder.start_node(NodeKind::BinaryExpr);
builder.add_token(TokenKind::Number, TextRange::new(4.into(), 6.into()));
builder.add_token(TokenKind::Star, TextRange::new(6.into(), 7.into()));
builder.add_token(TokenKind::Number, TextRange::new(7.into(), 9.into()));
builder.finish_node();
builder.finish_node();
builder.finish_node();
注意,我们并没有直接指定每个标记的文本,而是只是传递了原始输入中每个标记的范围。
接下来我们将介绍 eventree 提供的一些 API 的示例。
use eventree::{SyntaxBuilder, SyntaxNode, SyntaxToken, SyntaxTree, TextRange};
let mut builder = SyntaxBuilder::<TreeConfig>::new("foo+10*20");
builder.start_node(NodeKind::Root);
// ...
builder.finish_node();
let tree = builder.finish();
// let’s get the root of the tree
let root = tree.root();
// we can get the kind, text and range of nodes
assert_eq!(root.kind(&tree), NodeKind::Root);
assert_eq!(root.text(&tree), "foo+10*20");
assert_eq!(root.range(&tree), TextRange::new(0.into(), 9.into()));
// we can get the child nodes in the root; there’s just one, the BinaryExpr
let mut child_nodes = root.child_nodes(&tree);
let binary_expr = child_nodes.next().unwrap();
assert_eq!(binary_expr.kind(&tree), NodeKind::BinaryExpr);
assert!(child_nodes.next().is_none());
// let’s look at the descendant tokens of the BinaryExpr
let mut descendant_tokens = binary_expr.descendant_tokens(&tree);
// we can also get the kind, text and range of tokens
let ident = descendant_tokens.next().unwrap();
assert_eq!(ident.kind(&tree), TokenKind::Ident);
assert_eq!(ident.text(&tree), "foo");
assert_eq!(ident.range(&tree), TextRange::new(0.into(), 3.into()));
// let’s finish off by going through all descendant tokens
// until we reach the end
assert_eq!(descendant_tokens.next().unwrap().text(&tree), "+");
assert_eq!(descendant_tokens.next().unwrap().text(&tree), "10");
assert_eq!(descendant_tokens.next().unwrap().text(&tree), "*");
assert_eq!(descendant_tokens.next().unwrap().text(&tree), "20");
assert!(descendant_tokens.next().is_none());
希望这对您有所帮助!
依赖项
~66KB