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 2022年4月21日

#1499解析器实现

Download history 12/week @ 2024-03-26 50/week @ 2024-04-02 19/week @ 2024-05-28 67/week @ 2024-06-04 55/week @ 2024-06-11 50/week @ 2024-06-18 48/week @ 2024-06-25

每月下载量 229

MIT/Apache

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