5 个版本 (破坏性更新)
0.5.0 | 2024年6月1日 |
---|---|
0.4.0 | 2024年5月25日 |
0.3.0 | 2024年5月11日 |
0.2.0 | 2024年5月8日 |
0.1.0 | 2024年5月6日 |
#2 在 #着色器
每月75 次下载
175KB
5.5K SLoC
WGSL 解析器
使用 Gramatika 编写的针对 WebGPU 着色语言的零拷贝递归下降解析器。
API
解析源文件
use parser::{Parse, ParseStream, SyntaxTree};
const SOURCE_TEXT: &str = include_str!("path/to/some/shader.wgsl");
fn main() -> anyhow::Result<()> {
let mut parser = ParseStream::from(SOURCE_TEXT);
// The `SyntaxTree`
let tree = parser.parse::<SyntaxTree>()?;
// A tuple of `(ArcStr, Vec<Token>)`
let (source, tokens) = parser.into_inner();
Ok(())
}
对源文件进行标记化而不进行完全解析
use gramatika::Lexer as _;
use parser::Lexer;
const SOURCE_TEXT: &str = include_str!("path/to/some/shader.wgsl");
fn main() {
let mut lexer = Lexer::new(SOURCE_TEXT.into());
// `Vec<Token>`
let tokens = lexer.scan();
}
语法树表示
SyntaxTree
包含一个表示由 WGSL 语法 定义的顶层语法类型的 Decl
的向量,例如。
-
Decl::Var(VarDecl{ .. })
@group(1) @binding(2) var<uniform> uniforms: Uniforms;
-
Decl::Const(VarDecl{ .. })
const FOO: u32 = 1u;
-
Decl::Struct(StructDecl{ .. })
struct Foo { foo: mat3x4<f32>, bar: vec2<u32>, baz: array<mat4x4<f32>, 256u>, }
-
Decl::Function(FunctionDecl{ .. })
fn sum(a: f32, b: f32) -> f32 { return a + b; }
那些声明包裹的结构可以包含子声明,例如。
- 在
StructDecl
中的Decl::Field(FieldDecl { .. })
- 在
FunctionDecl
中的Decl::Param(ParamDecl { .. })
FunctionDecl
的 body
包含一个 Stmt
的向量。
Stmt
是一个类似于 Decl
的枚举,其变体表示它所代表的语句类型,每个变体都包含一个内部结构来详细描述语法,通常递归地,例如。
// NOTE: This is not valid Rust code, just a pseudo-code example of what some
// `Stmt` might look like on the inside
Stmt::If(IfStmt {
..
else_branch: Some(ElseStmt {
..
body: Arc(Stmt::Block(BlockStmt {
..
stmts: Arc<[Stmt]>,
})),
}),
})
最后,Expr
是树中“最低”的语法形式,采用与上面 Decl
和 Stmt
相同的一般形式。
检查语法树
语法树的每个节点都派生了一个定制的 Debug
实现来打印树,这种格式类似于 Lisp(常用于表示语法树的格式)和 Rust 语法。
该格式看起来像这样
max(4, 2) // The expression represented by the tree below
(Expr::Primary (PrimaryExpr
expr: (Expr::FnCall (FnCallExpr
ident: (IdentExpr::Leaf `max` (Function (1:1...1:4))),
arguments: (ArgumentList
brace_open: `(` (Brace (1:4...1:5)),
arguments: [
(Expr::Primary (PrimaryExpr
expr: (Expr::Literal `4` (IntLiteral (1:5...1:6))),
)),
(Expr::Primary (PrimaryExpr
expr: (Expr::Literal `2` (IntLiteral (1:8...1:9))),
)),
],
brace_close: `)` (Brace (1:9...1:10)),
),
)),
))
遍历语法树
该包导出了一个Visitor
特质,可以用来高效地遍历树。该Visitor
为树中表示的每种语法类型定义了一个visit_
方法。visit_
方法对于包含子节点的节点必须返回FlowControl::Continue
来遍历它们的子节点,或者返回FlowControl::Break
来停止遍历当前分支。
默认的Visitor
实现对于每个节点都返回FlowControl::Continue
,因此你只需要实现特定用例需要的visit_
方法。
use std::collections::HashMap;
use gramatika::{Substr, Token as _};
use parser::{
decl::VarDecl,
expr::{IdentExpr, NamespacedIdent},
traversal::{FlowControl, Visitor, Walk},
ParseStream, SyntaxTree,
};
#[derive(Default)]
struct ReferenceCounter {
counts: HashMap<Substr, usize>,
}
impl Visitor for ReferenceCounter {
fn visit_var_decl(&mut self, decl: &VarDecl) -> FlowControl {
self.counts.insert(decl.name.lexeme(), 0);
if let Some(ref expr) = decl.assignment {
expr.walk(self);
}
FlowControl::Break
}
fn visit_ident_expr(&mut self, mut expr: &IdentExpr) {
if let IdentExpr::Leaf(name) = expr {
if let Some(count) = self.counts.get_mut(&name.lexeme()) {
*count += 1;
}
}
}
}
// Note: Not actually a robust implementation of a reference-counter,
// but good enough for this toy example
pub fn count_references(tree: &SyntaxTree) -> HashMap<Substr, usize> {
let mut visitor = ReferenceCounter::default();
tree.walk(&mut visitor);
visitor.counts
}
依赖关系
~1–6.5MB
~38K SLoC