├── .gitignore ├── tool ├── src │ ├── wasm-sysroot │ │ ├── stdio.h │ │ ├── stdlib.h │ │ ├── stdbool.h │ │ └── stdint.h │ ├── snapshots │ │ ├── rust_sitter_tool__tests__enum_transformed_fields.snap │ │ ├── rust_sitter_tool__tests__grammar_unboxed_field.snap │ │ ├── rust_sitter_tool__tests__grammar_with_extras.snap │ │ ├── rust_sitter_tool__tests__enum_with_unamed_vector.snap │ │ ├── rust_sitter_tool__tests__enum_with_named_field.snap │ │ ├── rust_sitter_tool__tests__enum_recursive.snap │ │ ├── rust_sitter_tool__tests__enum_prec_left.snap │ │ ├── rust_sitter_tool__tests__spanned_in_vec.snap │ │ ├── rust_sitter_tool__tests__grammar_repeat_no_delimiter.snap │ │ ├── rust_sitter_tool__tests__struct_optional.snap │ │ ├── rust_sitter_tool__tests__grammar_repeat1.snap │ │ └── rust_sitter_tool__tests__grammar_repeat.snap │ ├── expansion.rs │ └── lib.rs └── Cargo.toml ├── Cargo.toml ├── example ├── build.rs ├── src │ ├── snapshots │ │ ├── rust_sitter_example__words__tests__words_grammar-4.snap │ │ ├── rust_sitter_example__words__tests__words_grammar.snap │ │ ├── rust_sitter_example__words__tests__words_grammar-2.snap │ │ ├── rust_sitter_example__words__tests__words_grammar-3.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar2.snap │ │ ├── rust_sitter_example__arithmetic__tests__failed_parses-2.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar3.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-3.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-2.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-4.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-7.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-5.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-8.snap │ │ ├── rust_sitter_example__optionals__tests__optional_grammar-6.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar-2.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar2-2.snap │ │ ├── rust_sitter_example__arithmetic__tests__failed_parses-3.snap │ │ ├── rust_sitter_example__arithmetic__tests__failed_parses-4.snap │ │ ├── rust_sitter_example__arithmetic__tests__failed_parses.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar3-2.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar-3.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar2-3.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar3-3.snap │ │ ├── rust_sitter_example__repetitions__tests__repetitions_grammar3-4.snap │ │ └── rust_sitter_example__repetitions__tests__repetitions_grammar3-5.snap │ ├── words.rs │ ├── optionals.rs │ ├── repetitions.rs │ ├── main.rs │ └── arithmetic.rs └── Cargo.toml ├── release.toml ├── common ├── Cargo.toml └── src │ └── lib.rs ├── macro ├── Cargo.toml └── src │ ├── errors.rs │ ├── snapshots │ ├── rust_sitter_macro__tests__enum_transformed_fields.snap │ ├── rust_sitter_macro__tests__struct_optional.snap │ ├── rust_sitter_macro__tests__enum_with_unamed_vector.snap │ ├── rust_sitter_macro__tests__enum_with_named_field.snap │ ├── rust_sitter_macro__tests__grammar_unboxed_field.snap │ ├── rust_sitter_macro__tests__enum_recursive.snap │ ├── rust_sitter_macro__tests__struct_extra.snap │ ├── rust_sitter_macro__tests__struct_repeat.snap │ ├── rust_sitter_macro__tests__enum_prec_left.snap │ └── rust_sitter_macro__tests__spanned_in_vec.snap │ ├── expansion.rs │ └── lib.rs ├── runtime ├── Cargo.toml └── src │ ├── __private.rs │ └── lib.rs ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /tool/src/wasm-sysroot/stdio.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tool/src/wasm-sysroot/stdlib.h: -------------------------------------------------------------------------------- 1 | #define NULL ((void*)0) -------------------------------------------------------------------------------- /tool/src/wasm-sysroot/stdbool.h: -------------------------------------------------------------------------------- 1 | #define bool _Bool 2 | #define true 1 3 | #define false 0 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "macro", 5 | "runtime", 6 | "tool", 7 | "example", 8 | "common", 9 | ] 10 | -------------------------------------------------------------------------------- /example/build.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=src"); 5 | rust_sitter_tool::build_parsers(&PathBuf::from("src/main.rs")); 6 | } 7 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__words__tests__words_grammar-4.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/words.rs 3 | expression: "grammar::parse(\"if hello\")" 4 | --- 5 | Ok( 6 | Words { 7 | _keyword: (), 8 | _word: "hello", 9 | }, 10 | ) 11 | -------------------------------------------------------------------------------- /tool/src/wasm-sysroot/stdint.h: -------------------------------------------------------------------------------- 1 | typedef signed char int8_t; 2 | typedef short int16_t; 3 | typedef long int32_t; 4 | typedef long long int64_t; 5 | 6 | typedef unsigned char uint8_t; 7 | typedef unsigned short uint16_t; 8 | typedef unsigned long uint32_t; 9 | typedef unsigned long long uint64_t; 10 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-replacements = [ 2 | {file="../README.md", search="rust-sitter = .*", replace="rust-sitter = \"{{version}}\""}, 3 | {file="../README.md", search="rust-sitter-tool = .*", replace="rust-sitter-tool = \"{{version}}\""}, 4 | ] 5 | 6 | consolidate-commits = true 7 | shared-version = true 8 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__words__tests__words_grammar.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/words.rs 3 | expression: "grammar::parse(\"if\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [], 10 | ), 11 | start: 0, 12 | end: 2, 13 | }, 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__words__tests__words_grammar-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/words.rs 3 | expression: "grammar::parse(\"hello\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [], 10 | ), 11 | start: 0, 12 | end: 5, 13 | }, 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__words__tests__words_grammar-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/words.rs 3 | expression: "grammar::parse(\"ifhello\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [], 10 | ), 11 | start: 0, 12 | end: 7, 13 | }, 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar::parse(\"\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [], 10 | ), 11 | start: 0, 12 | end: 0, 13 | }, 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar2::parse(\"\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [], 9 | span: ( 10 | 0, 11 | 0, 12 | ), 13 | }, 14 | }, 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__arithmetic__tests__failed_parses-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/arithmetic.rs 3 | expression: "grammar::parse(\"1 - 2 -\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: MissingToken( 9 | "Expression_Number_0", 10 | ), 11 | start: 7, 12 | end: 7, 13 | }, 14 | ], 15 | ) 16 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar3::parse(\"\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [], 9 | span: ( 10 | 0, 11 | 0, 12 | ), 13 | }, 14 | metadata: 123, 15 | }, 16 | ) 17 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"_\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: None, 8 | _s: (), 9 | t: Spanned { 10 | value: None, 11 | span: ( 12 | 1, 13 | 1, 14 | ), 15 | }, 16 | _d: None, 17 | }, 18 | ) 19 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"1_\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: Some( 8 | 1, 9 | ), 10 | _s: (), 11 | t: Spanned { 12 | value: None, 13 | span: ( 14 | 2, 15 | 2, 16 | ), 17 | }, 18 | _d: None, 19 | }, 20 | ) 21 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"_.\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: None, 8 | _s: (), 9 | t: Spanned { 10 | value: None, 11 | span: ( 12 | 1, 13 | 1, 14 | ), 15 | }, 16 | _d: Some( 17 | (), 18 | ), 19 | }, 20 | ) 21 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-4.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"1_.\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: Some( 8 | 1, 9 | ), 10 | _s: (), 11 | t: Spanned { 12 | value: None, 13 | span: ( 14 | 2, 15 | 2, 16 | ), 17 | }, 18 | _d: Some( 19 | (), 20 | ), 21 | }, 22 | ) 23 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-7.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"_2\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: None, 8 | _s: (), 9 | t: Spanned { 10 | value: Some( 11 | Number { 12 | v: 2, 13 | }, 14 | ), 15 | span: ( 16 | 1, 17 | 2, 18 | ), 19 | }, 20 | _d: None, 21 | }, 22 | ) 23 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__enum_transformed_fields.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"}]},"Expression_Number_0":{"type":"PATTERN","value":"\\d+"},"Expression_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Number_0"}}]},"Expression":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sitter-common" 3 | description = "Shared logic for the Rust Sitter macro and tool" 4 | readme = "../README.md" 5 | repository = "https://github.com/hydro-project/rust-sitter" 6 | version = "0.4.5" 7 | authors = ["Shadaj Laddad "] 8 | edition = "2021" 9 | license = "MIT" 10 | keywords = ["parsing", "codegen"] 11 | categories = ["development-tools"] 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | 16 | [dependencies] 17 | syn = { version = "2", features = [ "full", "extra-traits" ] } 18 | quote = "1" 19 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-5.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"1_2\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: Some( 8 | 1, 9 | ), 10 | _s: (), 11 | t: Spanned { 12 | value: Some( 13 | Number { 14 | v: 2, 15 | }, 16 | ), 17 | span: ( 18 | 2, 19 | 3, 20 | ), 21 | }, 22 | _d: None, 23 | }, 24 | ) 25 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-8.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"_2.\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: None, 8 | _s: (), 9 | t: Spanned { 10 | value: Some( 11 | Number { 12 | v: 2, 13 | }, 14 | ), 15 | span: ( 16 | 1, 17 | 2, 18 | ), 19 | }, 20 | _d: Some( 21 | (), 22 | ), 23 | }, 24 | ) 25 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__optionals__tests__optional_grammar-6.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/optionals.rs 3 | expression: "grammar::parse(\"1_2.\")" 4 | --- 5 | Ok( 6 | Language { 7 | v: Some( 8 | 1, 9 | ), 10 | _s: (), 11 | t: Spanned { 12 | value: Some( 13 | Number { 14 | v: 2, 15 | }, 16 | ), 17 | span: ( 18 | 2, 19 | 3, 20 | ), 21 | }, 22 | _d: Some( 23 | (), 24 | ), 25 | }, 26 | ) 27 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar::parse(\"1\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: 1, 11 | span: ( 12 | 0, 13 | 1, 14 | ), 15 | }, 16 | ], 17 | span: ( 18 | 0, 19 | 1, 20 | ), 21 | }, 22 | }, 23 | ) 24 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar2-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar2::parse(\"1\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: 1, 11 | span: ( 12 | 0, 13 | 1, 14 | ), 15 | }, 16 | ], 17 | span: ( 18 | 0, 19 | 1, 20 | ), 21 | }, 22 | }, 23 | ) 24 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__arithmetic__tests__failed_parses-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/arithmetic.rs 3 | expression: "grammar::parse(\"a1\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [ 10 | ParseError { 11 | reason: UnexpectedToken( 12 | "a", 13 | ), 14 | start: 0, 15 | end: 1, 16 | }, 17 | ], 18 | ), 19 | start: 0, 20 | end: 1, 21 | }, 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__arithmetic__tests__failed_parses-4.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/arithmetic.rs 3 | expression: "grammar::parse(\"1a\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [ 10 | ParseError { 11 | reason: UnexpectedToken( 12 | "a", 13 | ), 14 | start: 1, 15 | end: 2, 16 | }, 17 | ], 18 | ), 19 | start: 1, 20 | end: 2, 21 | }, 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__arithmetic__tests__failed_parses.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/arithmetic.rs 3 | expression: "grammar::parse(\"1 + 2\")" 4 | --- 5 | Err( 6 | [ 7 | ParseError { 8 | reason: FailedNode( 9 | [ 10 | ParseError { 11 | reason: UnexpectedToken( 12 | "+", 13 | ), 14 | start: 2, 15 | end: 3, 16 | }, 17 | ], 18 | ), 19 | start: 0, 20 | end: 3, 21 | }, 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sitter-example" 3 | version = "0.4.5" 4 | authors = ["Shadaj Laddad "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [features] 9 | default = ["tree-sitter-c2rust"] 10 | tree-sitter-c2rust = ["rust-sitter/tree-sitter-c2rust"] 11 | tree-sitter-standard = ["rust-sitter/tree-sitter-standard"] 12 | 13 | [dependencies] 14 | rust-sitter = { path = "../runtime", default-features = false } 15 | codemap = "0.1.3" 16 | codemap-diagnostic = "0.1.1" 17 | 18 | [build-dependencies] 19 | rust-sitter-tool = { path = "../tool" } 20 | 21 | [dev-dependencies] 22 | insta = "1.39" 23 | wasm-bindgen-test = "0.3.0" 24 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__grammar_unboxed_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"FIELD","name":"e","content":{"type":"SYMBOL","name":"Expression"}}]},"Language":{"type":"SEQ","members":[{"type":"FIELD","name":"e","content":{"type":"SYMBOL","name":"Expression"}}]},"Expression_Number_0":{"type":"PATTERN","value":"\\d+"},"Expression_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Number_0"}}]},"Expression":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar3-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar3::parse(\"1,\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: Some( 11 | 1, 12 | ), 13 | span: ( 14 | 0, 15 | 1, 16 | ), 17 | }, 18 | ], 19 | span: ( 20 | 0, 21 | 2, 22 | ), 23 | }, 24 | metadata: 123, 25 | }, 26 | ) 27 | -------------------------------------------------------------------------------- /macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sitter-macro" 3 | description = "Procedural macros for Rust Sitter" 4 | readme = "../README.md" 5 | repository = "https://github.com/hydro-project/rust-sitter" 6 | version = "0.4.5" 7 | authors = ["Shadaj Laddad "] 8 | edition = "2021" 9 | license = "MIT" 10 | keywords = ["parsing", "codegen"] 11 | categories = ["development-tools"] 12 | 13 | [lib] 14 | proc-macro = true 15 | path = "src/lib.rs" 16 | 17 | [dependencies] 18 | syn = { version = "2", features = [ "full", "extra-traits" ] } 19 | quote = "1" 20 | proc-macro2 = "1" 21 | rust-sitter-common = { version= "0.4.5", path = "../common" } 22 | 23 | [dev-dependencies] 24 | insta = "1.39" 25 | tempfile = "3" 26 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__grammar_with_extras.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"}]},"Expression_Number_0":{"type":"PATTERN","value":"\\d+"},"Expression_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Number_0"}}]},"Expression":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"}]},"Whitespace__whitespace":{"type":"PATTERN","value":"\\s"},"Whitespace":{"type":"SEQ","members":[{"type":"FIELD","name":"_whitespace","content":{"type":"SYMBOL","name":"Whitespace__whitespace"}}]}},"extras":[{"type":"SYMBOL","name":"Whitespace"}]} 6 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__enum_with_unamed_vector.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expr_Numbers"}]},"Number_value":{"type":"PATTERN","value":"\\d+"},"Number":{"type":"SEQ","members":[{"type":"FIELD","name":"value","content":{"type":"SYMBOL","name":"Number_value"}}]},"Expr_Numbers_0_vec_contents":{"type":"REPEAT1","content":{"type":"FIELD","name":"Expr_Numbers_0_vec_element","content":{"type":"SYMBOL","name":"Number"}}},"Expr_Numbers":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expr_Numbers_0_vec_contents"}}]},"Expr":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expr_Numbers"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__enum_with_named_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expr_Number"},{"type":"SYMBOL","name":"Expr_Neg"}]},"Expr_Number_0":{"type":"PATTERN","value":"\\d+"},"Expr_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expr_Number_0"}}]},"Expr_Neg__bang":{"type":"STRING","value":"!"},"Expr_Neg":{"type":"SEQ","members":[{"type":"FIELD","name":"_bang","content":{"type":"SYMBOL","name":"Expr_Neg__bang"}},{"type":"FIELD","name":"value","content":{"type":"SYMBOL","name":"Expr"}}]},"Expr":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expr_Number"},{"type":"SYMBOL","name":"Expr_Neg"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar::parse(\"1, 2\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: 1, 11 | span: ( 12 | 0, 13 | 1, 14 | ), 15 | }, 16 | Spanned { 17 | value: 2, 18 | span: ( 19 | 3, 20 | 4, 21 | ), 22 | }, 23 | ], 24 | span: ( 25 | 0, 26 | 4, 27 | ), 28 | }, 29 | }, 30 | ) 31 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar2-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar2::parse(\"1 2\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: 1, 11 | span: ( 12 | 0, 13 | 1, 14 | ), 15 | }, 16 | Spanned { 17 | value: 2, 18 | span: ( 19 | 2, 20 | 3, 21 | ), 22 | }, 23 | ], 24 | span: ( 25 | 0, 26 | 3, 27 | ), 28 | }, 29 | }, 30 | ) 31 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__enum_recursive.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"},{"type":"SYMBOL","name":"Expression_Neg"}]},"Expression_Number_0":{"type":"PATTERN","value":"\\d+"},"Expression_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Number_0"}}]},"Expression_Neg_0":{"type":"STRING","value":"-"},"Expression_Neg":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Neg_0"}},{"type":"FIELD","name":"1","content":{"type":"SYMBOL","name":"Expression"}}]},"Expression":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"},{"type":"SYMBOL","name":"Expression_Neg"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /example/src/words.rs: -------------------------------------------------------------------------------- 1 | #[rust_sitter::grammar("words")] 2 | pub mod grammar { 3 | #[rust_sitter::language] 4 | #[derive(Debug)] 5 | pub struct Words { 6 | #[rust_sitter::leaf(text = r"if")] 7 | _keyword: (), 8 | #[rust_sitter::word] 9 | #[rust_sitter::leaf(pattern = r"[a-z_]+", transform = |v| v.to_string())] 10 | _word: String, 11 | } 12 | 13 | #[rust_sitter::extra] 14 | struct Whitespace { 15 | #[rust_sitter::leaf(pattern = r"\s")] 16 | _whitespace: (), 17 | } 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | 24 | #[test] 25 | fn words_grammar() { 26 | insta::assert_debug_snapshot!(grammar::parse("if")); 27 | insta::assert_debug_snapshot!(grammar::parse("hello")); 28 | insta::assert_debug_snapshot!(grammar::parse("ifhello")); 29 | insta::assert_debug_snapshot!(grammar::parse("if hello")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar3-3.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar3::parse(\"1, 2\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: Some( 11 | 1, 12 | ), 13 | span: ( 14 | 0, 15 | 1, 16 | ), 17 | }, 18 | Spanned { 19 | value: Some( 20 | 2, 21 | ), 22 | span: ( 23 | 3, 24 | 4, 25 | ), 26 | }, 27 | ], 28 | span: ( 29 | 0, 30 | 4, 31 | ), 32 | }, 33 | metadata: 123, 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar3-4.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar3::parse(\"1,, 2\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: Some( 11 | 1, 12 | ), 13 | span: ( 14 | 0, 15 | 1, 16 | ), 17 | }, 18 | Spanned { 19 | value: Some( 20 | 2, 21 | ), 22 | span: ( 23 | 4, 24 | 5, 25 | ), 26 | }, 27 | ], 28 | span: ( 29 | 0, 30 | 5, 31 | ), 32 | }, 33 | metadata: 123, 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /example/src/snapshots/rust_sitter_example__repetitions__tests__repetitions_grammar3-5.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: example/src/repetitions.rs 3 | expression: "grammar3::parse(\"1,, 2,\")" 4 | --- 5 | Ok( 6 | NumberList { 7 | numbers: Spanned { 8 | value: [ 9 | Spanned { 10 | value: Some( 11 | 1, 12 | ), 13 | span: ( 14 | 0, 15 | 1, 16 | ), 17 | }, 18 | Spanned { 19 | value: Some( 20 | 2, 21 | ), 22 | span: ( 23 | 4, 24 | 5, 25 | ), 26 | }, 27 | ], 28 | span: ( 29 | 0, 30 | 6, 31 | ), 32 | }, 33 | metadata: 123, 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__enum_prec_left.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: generate_grammar(&m) 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"},{"type":"SYMBOL","name":"Expression_Sub"}]},"Expression_Number_0":{"type":"PATTERN","value":"\\d+"},"Expression_Number":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression_Number_0"}}]},"Expression_Sub_1":{"type":"STRING","value":"-"},"Expression_Sub":{"type":"PREC_LEFT","value":1,"content":{"type":"SEQ","members":[{"type":"FIELD","name":"0","content":{"type":"SYMBOL","name":"Expression"}},{"type":"FIELD","name":"1","content":{"type":"SYMBOL","name":"Expression_Sub_1"}},{"type":"FIELD","name":"2","content":{"type":"SYMBOL","name":"Expression"}}]}},"Expression":{"type":"CHOICE","members":[{"type":"SYMBOL","name":"Expression_Number"},{"type":"SYMBOL","name":"Expression_Sub"}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__spanned_in_vec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"NumberList_numbers":{"type":"PATTERN","value":"\\d+"},"NumberList_numbers_vec_contents":{"type":"REPEAT1","content":{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"NumberList_numbers"}}},"NumberList":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"Whitespace__whitespace":{"type":"PATTERN","value":"\\s"},"Whitespace":{"type":"SEQ","members":[{"type":"FIELD","name":"_whitespace","content":{"type":"SYMBOL","name":"Whitespace__whitespace"}}]}},"extras":[{"type":"SYMBOL","name":"Whitespace"}]} 6 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sitter" 3 | description = "A package for defining tree-sitter grammars alongside Rust logic" 4 | readme = "../README.md" 5 | repository = "https://github.com/hydro-project/rust-sitter" 6 | version = "0.4.5" 7 | authors = ["Shadaj Laddad "] 8 | edition = "2021" 9 | license = "MIT" 10 | keywords = ["parsing", "codegen"] 11 | categories = ["development-tools"] 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | 16 | [features] 17 | default = ["tree-sitter-c2rust"] 18 | tree-sitter-c2rust = ["tree-sitter-runtime-c2rust"] 19 | tree-sitter-standard = ["tree-sitter-runtime-standard"] 20 | 21 | [dependencies] 22 | tree-sitter-runtime-c2rust = { package = "tree-sitter-c2rust", version = "0.25.2", optional = true } 23 | tree-sitter-runtime-standard = { package = "tree-sitter", version = "0.25.2", optional = true } 24 | rust-sitter-macro = { version = "0.4.5", path = "../macro" } 25 | 26 | [dev-dependencies] 27 | insta = "1.39" 28 | tempfile = "3.2.0" 29 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__grammar_repeat_no_delimiter.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"NumberList_numbers_vec_contents":{"type":"REPEAT1","content":{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"Number"}}},"NumberList":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"Number_v":{"type":"PATTERN","value":"\\d+"},"Number":{"type":"SEQ","members":[{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Number_v"}}]},"Whitespace__whitespace":{"type":"PATTERN","value":"\\s"},"Whitespace":{"type":"SEQ","members":[{"type":"FIELD","name":"_whitespace","content":{"type":"SYMBOL","name":"Whitespace__whitespace"}}]}},"extras":[{"type":"SYMBOL","name":"Whitespace"}]} 6 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__struct_optional.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Language_v"}}]},{"type":"FIELD","name":"space","content":{"type":"SYMBOL","name":"Language_space"}},{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"t","content":{"type":"SYMBOL","name":"Number"}}]}]},"Language_v":{"type":"PATTERN","value":"\\d+"},"Language_space":{"type":"PATTERN","value":" "},"Language":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Language_v"}}]},{"type":"FIELD","name":"space","content":{"type":"SYMBOL","name":"Language_space"}},{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"t","content":{"type":"SYMBOL","name":"Number"}}]}]},"Number_v":{"type":"PATTERN","value":"\\d+"},"Number":{"type":"SEQ","members":[{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Number_v"}}]}},"extras":[]} 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shadaj Laddad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sitter-tool" 3 | description = "The external tool for Rust Sitter that extracts grammars from Rust definitions" 4 | readme = "../README.md" 5 | repository = "https://github.com/hydro-project/rust-sitter" 6 | version = "0.4.5" 7 | authors = ["Shadaj Laddad "] 8 | license = "MIT" 9 | edition = "2021" 10 | keywords = ["parsing", "codegen"] 11 | categories = ["development-tools"] 12 | 13 | [features] 14 | default = ["build_parsers"] 15 | build_parsers = [ 16 | "dep:tempfile", 17 | "dep:tree-sitter", 18 | "dep:tree-sitter-generate", 19 | "dep:cc", 20 | ] 21 | 22 | [dependencies] 23 | syn = { version = "2", features = ["full", "extra-traits"] } 24 | syn-inline-mod = "0.6" 25 | serde = { version = "1", features = ["derive"] } 26 | serde_json = { version = "1", features = ["preserve_order"] } 27 | rust-sitter-common = { version = "0.4.5", path = "../common" } 28 | 29 | tempfile = { version = "3", optional = true } 30 | tree-sitter = { version = "0.25.2", optional = true } 31 | tree-sitter-generate = { version = "0.25.1", optional = true } 32 | cc = { version = "1", optional = true } 33 | 34 | [dev-dependencies] 35 | insta = "1.39" 36 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__grammar_repeat1.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]},"NumberList_numbers_vec_delimiter":{"type":"STRING","value":","},"NumberList_numbers_vec_contents":{"type":"SEQ","members":[{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"Number"}},{"type":"REPEAT","content":{"type":"SEQ","members":[{"type":"SYMBOL","name":"NumberList_numbers_vec_delimiter"},{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"Number"}}]}}]},"NumberList":{"type":"SEQ","members":[{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]},"Number_v":{"type":"PATTERN","value":"\\d+"},"Number":{"type":"SEQ","members":[{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Number_v"}}]},"Whitespace__whitespace":{"type":"PATTERN","value":"\\s"},"Whitespace":{"type":"SEQ","members":[{"type":"FIELD","name":"_whitespace","content":{"type":"SYMBOL","name":"Whitespace__whitespace"}}]}},"extras":[{"type":"SYMBOL","name":"Whitespace"}]} 6 | -------------------------------------------------------------------------------- /tool/src/snapshots/rust_sitter_tool__tests__grammar_repeat.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tool/src/lib.rs 3 | expression: grammar 4 | --- 5 | {"name":"test","word":null,"rules":{"source_file":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"NumberList_numbers_vec_delimiter":{"type":"STRING","value":","},"NumberList_numbers_vec_contents":{"type":"SEQ","members":[{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"Number"}},{"type":"REPEAT","content":{"type":"SEQ","members":[{"type":"SYMBOL","name":"NumberList_numbers_vec_delimiter"},{"type":"FIELD","name":"NumberList_numbers_vec_element","content":{"type":"SYMBOL","name":"Number"}}]}}]},"NumberList":{"type":"SEQ","members":[{"type":"CHOICE","members":[{"type":"BLANK"},{"type":"FIELD","name":"numbers","content":{"type":"SYMBOL","name":"NumberList_numbers_vec_contents"}}]}]},"Number_v":{"type":"PATTERN","value":"\\d+"},"Number":{"type":"SEQ","members":[{"type":"FIELD","name":"v","content":{"type":"SYMBOL","name":"Number_v"}}]},"Whitespace__whitespace":{"type":"PATTERN","value":"\\s"},"Whitespace":{"type":"SEQ","members":[{"type":"FIELD","name":"_whitespace","content":{"type":"SYMBOL","name":"Whitespace__whitespace"}}]}},"extras":[{"type":"SYMBOL","name":"Whitespace"}]} 6 | -------------------------------------------------------------------------------- /example/src/optionals.rs: -------------------------------------------------------------------------------- 1 | #[rust_sitter::grammar("optionals")] 2 | #[allow(dead_code)] 3 | mod grammar { 4 | use rust_sitter::Spanned; 5 | 6 | #[rust_sitter::language] 7 | #[derive(Debug)] 8 | pub struct Language { 9 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 10 | v: Option, 11 | #[rust_sitter::leaf(text = "_")] 12 | _s: (), 13 | t: Spanned>, 14 | #[rust_sitter::leaf(text = ".")] 15 | _d: Option<()>, 16 | } 17 | 18 | #[derive(Debug)] 19 | pub struct Number { 20 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 21 | v: i32, 22 | } 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | 29 | #[test] 30 | fn optional_grammar() { 31 | insta::assert_debug_snapshot!(grammar::parse("_")); 32 | insta::assert_debug_snapshot!(grammar::parse("_.")); 33 | insta::assert_debug_snapshot!(grammar::parse("1_")); 34 | insta::assert_debug_snapshot!(grammar::parse("1_.")); 35 | insta::assert_debug_snapshot!(grammar::parse("1_2")); 36 | insta::assert_debug_snapshot!(grammar::parse("1_2.")); 37 | insta::assert_debug_snapshot!(grammar::parse("_2")); 38 | insta::assert_debug_snapshot!(grammar::parse("_2.")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust Sitter CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Build 18 | run: cargo build 19 | - name: Run tests 20 | run: cargo test 21 | - name: Run macro tests 22 | run: cargo test -p rust-sitter-macro -- tests 23 | test-wasm: 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: Install wasm32 target 29 | uses: actions-rs/toolchain@v1 30 | with: 31 | toolchain: stable 32 | target: wasm32-unknown-unknown 33 | 34 | - name: Get wasm-bindgen version 35 | id: wasm-bindgen-version 36 | run: echo "VERSION=$(cargo pkgid wasm-bindgen-shared | cut -d '@' -f2)" >> "$GITHUB_OUTPUT" 37 | - name: Install WebAssembly test runner 38 | run: cargo install wasm-bindgen-cli@${{ steps.wasm-bindgen-version.outputs.VERSION }} 39 | 40 | - name: Run tests 41 | env: 42 | CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER: wasm-bindgen-test-runner 43 | run: cargo test -p rust-sitter-example --target wasm32-unknown-unknown --no-fail-fast 44 | lint: 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v2 48 | - name: Check rustfmt 49 | run: cargo fmt --all -- --check 50 | - name: Check clippy 51 | run: cargo clippy --all-targets -- -D warnings 52 | -------------------------------------------------------------------------------- /macro/src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | /// An iterator that maps [`Result`]s to their [`Ok`] values 4 | /// and stores combined errors within itself. 5 | struct CollectingShunt<'a, I, A> { 6 | iter: I, 7 | err: &'a mut Option, 8 | _marker: PhantomData A>, 9 | } 10 | 11 | impl Iterator for CollectingShunt<'_, I, A> 12 | where 13 | I: Iterator>, 14 | { 15 | type Item = A; 16 | 17 | fn next(&mut self) -> Option { 18 | match self.iter.next() { 19 | Some(Ok(x)) => Some(x), 20 | Some(Err(another)) => { 21 | match self.err { 22 | Some(x) => x.combine(another), 23 | ref mut x => **x = Some(another), 24 | } 25 | None 26 | } 27 | _ => None, 28 | } 29 | } 30 | 31 | fn size_hint(&self) -> (usize, Option) { 32 | let (_, upper) = self.iter.size_hint(); 33 | (0, upper) 34 | } 35 | } 36 | 37 | pub trait IteratorExt: Iterator> { 38 | /// Reduces an iterator with items of type [`syn::Result`] into one large collection, 39 | /// [combining] errors and [collecting] successes. 40 | /// 41 | /// [combining]: syn::Error::combine 42 | /// [collecting]: FromIterator 43 | fn sift(self) -> syn::Result 44 | where 45 | Self: Sized, 46 | T: FromIterator, 47 | { 48 | let mut err = None; 49 | let iter = CollectingShunt { 50 | iter: self, 51 | err: &mut err, 52 | _marker: PhantomData, 53 | }; 54 | let collection = iter.collect(); 55 | match err { 56 | Some(error) => Err(error), 57 | None => Ok(collection), 58 | } 59 | } 60 | } 61 | 62 | impl IteratorExt for T where T: Iterator> {} 63 | -------------------------------------------------------------------------------- /runtime/src/__private.rs: -------------------------------------------------------------------------------- 1 | //! # DO NOT USE THIS MODULE! 2 | //! 3 | //! This module contains functions for use in the expanded macros produced by rust-sitter. 4 | //! They need to be public so they can be accessed at all (\*cough\* macro hygiene), but 5 | //! they are not intended to actually be called in any other circumstance. 6 | 7 | use crate::{tree_sitter, Extract}; 8 | 9 | pub fn extract_struct_or_variant( 10 | node: tree_sitter::Node, 11 | construct_expr: impl Fn(&mut Option, &mut usize) -> T, 12 | ) -> T { 13 | let mut parent_cursor = node.walk(); 14 | construct_expr( 15 | &mut if parent_cursor.goto_first_child() { 16 | Some(parent_cursor) 17 | } else { 18 | None 19 | }, 20 | &mut node.start_byte(), 21 | ) 22 | } 23 | 24 | pub fn extract_field, T>( 25 | cursor_opt: &mut Option, 26 | source: &[u8], 27 | last_idx: &mut usize, 28 | field_name: &str, 29 | closure_ref: Option<<::LeafFn>, 30 | ) -> T { 31 | if let Some(cursor) = cursor_opt.as_mut() { 32 | loop { 33 | let n = cursor.node(); 34 | if let Some(name) = cursor.field_name() { 35 | if name == field_name { 36 | let out = LT::extract(Some(n), source, *last_idx, closure_ref); 37 | 38 | if !cursor.goto_next_sibling() { 39 | *cursor_opt = None; 40 | }; 41 | 42 | *last_idx = n.end_byte(); 43 | 44 | return out; 45 | } else { 46 | return LT::extract(None, source, *last_idx, closure_ref); 47 | } 48 | } else { 49 | *last_idx = n.end_byte(); 50 | } 51 | 52 | if !cursor.goto_next_sibling() { 53 | return LT::extract(None, source, *last_idx, closure_ref); 54 | } 55 | } 56 | } else { 57 | LT::extract(None, source, *last_idx, closure_ref) 58 | } 59 | } 60 | 61 | pub fn parse>( 62 | input: &str, 63 | language: impl Fn() -> tree_sitter::Language, 64 | ) -> core::result::Result> { 65 | let mut parser = crate::tree_sitter::Parser::new(); 66 | parser.set_language(&language()).unwrap(); 67 | let tree = parser.parse(input, None).unwrap(); 68 | let root_node = tree.root_node(); 69 | 70 | if root_node.has_error() { 71 | let mut errors = vec![]; 72 | crate::errors::collect_parsing_errors(&root_node, input.as_bytes(), &mut errors); 73 | 74 | Err(errors) 75 | } else { 76 | Ok(>::extract( 77 | Some(root_node), 78 | input.as_bytes(), 79 | 0, 80 | None, 81 | )) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /example/src/repetitions.rs: -------------------------------------------------------------------------------- 1 | #[rust_sitter::grammar("repetitions")] 2 | pub mod grammar { 3 | use rust_sitter::Spanned; 4 | 5 | #[rust_sitter::language] 6 | #[derive(Debug)] 7 | #[allow(dead_code)] 8 | pub struct NumberList { 9 | #[rust_sitter::repeat(non_empty = true)] 10 | #[rust_sitter::delimited( 11 | #[rust_sitter::leaf(text = ",")] 12 | () 13 | )] 14 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 15 | numbers: Spanned>>, 16 | } 17 | 18 | #[rust_sitter::extra] 19 | struct Whitespace { 20 | #[rust_sitter::leaf(pattern = r"\s")] 21 | _whitespace: (), 22 | } 23 | } 24 | 25 | #[rust_sitter::grammar("repetitions_without_delim")] 26 | pub mod grammar2 { 27 | use rust_sitter::Spanned; 28 | 29 | #[rust_sitter::language] 30 | #[derive(Debug)] 31 | #[allow(dead_code)] 32 | pub struct NumberList { 33 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 34 | numbers: Spanned>>, 35 | } 36 | 37 | #[rust_sitter::extra] 38 | struct Whitespace { 39 | #[rust_sitter::leaf(pattern = r"\s")] 40 | _whitespace: (), 41 | } 42 | } 43 | 44 | #[rust_sitter::grammar("repetitions_optional_elem")] 45 | pub mod grammar3 { 46 | use rust_sitter::Spanned; 47 | 48 | #[rust_sitter::language] 49 | #[derive(Debug)] 50 | #[allow(dead_code)] 51 | pub struct NumberList { 52 | #[rust_sitter::delimited( 53 | #[rust_sitter::leaf(text = ",")] 54 | () 55 | )] 56 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 57 | numbers: Spanned>>>, 58 | #[rust_sitter::skip(123)] 59 | metadata: u32, 60 | } 61 | 62 | #[rust_sitter::extra] 63 | struct Whitespace { 64 | #[rust_sitter::leaf(pattern = r"\s")] 65 | _whitespace: (), 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests { 71 | use super::*; 72 | 73 | #[test] 74 | fn repetitions_grammar() { 75 | insta::assert_debug_snapshot!(grammar::parse("")); 76 | insta::assert_debug_snapshot!(grammar::parse("1")); 77 | insta::assert_debug_snapshot!(grammar::parse("1, 2")); 78 | } 79 | 80 | #[test] 81 | fn repetitions_grammar2() { 82 | insta::assert_debug_snapshot!(grammar2::parse("")); 83 | insta::assert_debug_snapshot!(grammar2::parse("1")); 84 | insta::assert_debug_snapshot!(grammar2::parse("1 2")); 85 | } 86 | 87 | #[test] 88 | fn repetitions_grammar3() { 89 | insta::assert_debug_snapshot!(grammar3::parse("")); 90 | insta::assert_debug_snapshot!(grammar3::parse("1,")); 91 | insta::assert_debug_snapshot!(grammar3::parse("1, 2")); 92 | insta::assert_debug_snapshot!(grammar3::parse("1,, 2")); 93 | insta::assert_debug_snapshot!(grammar3::parse("1,, 2,")); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use codemap::CodeMap; 4 | use codemap_diagnostic::{ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle}; 5 | use rust_sitter::errors::{ParseError, ParseErrorReason}; 6 | 7 | mod arithmetic; 8 | mod optionals; 9 | mod repetitions; 10 | mod words; 11 | 12 | fn convert_parse_error_to_diagnostics( 13 | file_span: &codemap::Span, 14 | error: &ParseError, 15 | diagnostics: &mut Vec, 16 | ) { 17 | match &error.reason { 18 | ParseErrorReason::MissingToken(tok) => diagnostics.push(Diagnostic { 19 | level: Level::Error, 20 | message: format!("Missing token: \"{tok}\""), 21 | code: Some("S000".to_string()), 22 | spans: vec![SpanLabel { 23 | span: file_span.subspan(error.start as u64, error.end as u64), 24 | style: SpanStyle::Primary, 25 | label: Some(format!("missing \"{tok}\"")), 26 | }], 27 | }), 28 | 29 | ParseErrorReason::UnexpectedToken(tok) => diagnostics.push(Diagnostic { 30 | level: Level::Error, 31 | message: format!("Unexpected token: \"{tok}\""), 32 | code: Some("S000".to_string()), 33 | spans: vec![SpanLabel { 34 | span: file_span.subspan(error.start as u64, error.end as u64), 35 | style: SpanStyle::Primary, 36 | label: Some(format!("unexpected \"{tok}\"")), 37 | }], 38 | }), 39 | 40 | ParseErrorReason::FailedNode(errors) => { 41 | if errors.is_empty() { 42 | diagnostics.push(Diagnostic { 43 | level: Level::Error, 44 | message: "Failed to parse node".to_string(), 45 | code: Some("S000".to_string()), 46 | spans: vec![SpanLabel { 47 | span: file_span.subspan(error.start as u64, error.end as u64), 48 | style: SpanStyle::Primary, 49 | label: Some("failed".to_string()), 50 | }], 51 | }) 52 | } else { 53 | for error in errors { 54 | convert_parse_error_to_diagnostics(file_span, error, diagnostics); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | fn main() { 62 | let stdin = std::io::stdin(); 63 | 64 | loop { 65 | print!("> "); 66 | std::io::stdout().flush().unwrap(); 67 | 68 | let mut input = String::new(); 69 | stdin.read_line(&mut input).unwrap(); 70 | let input = input.trim(); 71 | if input.is_empty() { 72 | break; 73 | } 74 | 75 | match arithmetic::grammar::parse(input) { 76 | Ok(expr) => println!("{expr:?}"), 77 | Err(errs) => { 78 | let mut codemap = CodeMap::new(); 79 | let file_span = codemap.add_file("".to_string(), input.to_string()); 80 | let mut diagnostics = vec![]; 81 | for error in errs { 82 | convert_parse_error_to_diagnostics(&file_span.span, &error, &mut diagnostics); 83 | } 84 | 85 | let mut emitter = Emitter::stderr(ColorConfig::Always, Some(&codemap)); 86 | emitter.emit(&diagnostics); 87 | } 88 | }; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__enum_transformed_fields.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub enum Expression\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v | v.parse :: < i32 >\n ().unwrap())] i32),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub enum Expression { 7 | Number(i32), 8 | } 9 | impl ::rust_sitter::Extract for Expression { 10 | type LeafFn = (); 11 | #[allow(non_snake_case)] 12 | fn extract( 13 | node: Option<::rust_sitter::tree_sitter::Node>, 14 | source: &[u8], 15 | _last_idx: usize, 16 | _leaf_fn: Option<&Self::LeafFn>, 17 | ) -> Self { 18 | let node = node.unwrap(); 19 | let mut cursor = node.walk(); 20 | assert!( 21 | cursor.goto_first_child(), 22 | "Could not find a child corresponding to any enum branch" 23 | ); 24 | loop { 25 | let node = cursor.node(); 26 | match node.kind() { 27 | "Expression_Number" => { 28 | return ::rust_sitter::__private::extract_struct_or_variant( 29 | node, 30 | move |cursor, last_idx| { 31 | Expression::Number({ 32 | ::rust_sitter::__private::extract_field::< 33 | rust_sitter::WithLeaf, 34 | _, 35 | >( 36 | cursor, 37 | source, 38 | last_idx, 39 | "0", 40 | Some(&|v| v.parse::().unwrap()), 41 | ) 42 | }) 43 | }, 44 | ) 45 | } 46 | _ => { 47 | if !cursor.goto_next_sibling() { 48 | panic!("Could not find a child corresponding to any enum branch") 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | extern "C" { 56 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 57 | } 58 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 59 | unsafe { tree_sitter_test() } 60 | } 61 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 62 | #[doc = "[`Expression`]"] 63 | #[doc = r" instance containing the parsed structured data."] 64 | pub fn parse( 65 | input: &str, 66 | ) -> core::result::Result> { 67 | ::rust_sitter::__private::parse::(input, language) 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /example/src/arithmetic.rs: -------------------------------------------------------------------------------- 1 | #[rust_sitter::grammar("arithmetic")] 2 | pub mod grammar { 3 | #[rust_sitter::language] 4 | #[derive(PartialEq, Eq, Debug)] 5 | pub enum Expression { 6 | Number(#[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] i32), 7 | #[rust_sitter::prec_left(1)] 8 | Sub( 9 | Box, 10 | #[rust_sitter::leaf(text = "-")] (), 11 | Box, 12 | ), 13 | #[rust_sitter::prec_left(2)] 14 | Mul( 15 | Box, 16 | #[rust_sitter::leaf(text = "*")] (), 17 | Box, 18 | ), 19 | } 20 | 21 | #[rust_sitter::extra] 22 | struct Whitespace { 23 | #[rust_sitter::leaf(pattern = r"\s")] 24 | _whitespace: (), 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | use grammar::Expression; 32 | 33 | #[wasm_bindgen_test::wasm_bindgen_test] 34 | #[test] 35 | fn successful_parses() { 36 | assert_eq!(grammar::parse("1").unwrap(), Expression::Number(1)); 37 | 38 | assert_eq!(grammar::parse(" 1").unwrap(), Expression::Number(1)); 39 | 40 | assert_eq!( 41 | grammar::parse("1 - 2").unwrap(), 42 | Expression::Sub( 43 | Box::new(Expression::Number(1)), 44 | (), 45 | Box::new(Expression::Number(2)) 46 | ) 47 | ); 48 | 49 | assert_eq!( 50 | grammar::parse("1 - 2 - 3").unwrap(), 51 | Expression::Sub( 52 | Box::new(Expression::Sub( 53 | Box::new(Expression::Number(1)), 54 | (), 55 | Box::new(Expression::Number(2)) 56 | )), 57 | (), 58 | Box::new(Expression::Number(3)) 59 | ) 60 | ); 61 | 62 | assert_eq!( 63 | grammar::parse("1 - 2 * 3").unwrap(), 64 | Expression::Sub( 65 | Box::new(Expression::Number(1)), 66 | (), 67 | Box::new(Expression::Mul( 68 | Box::new(Expression::Number(2)), 69 | (), 70 | Box::new(Expression::Number(3)) 71 | )) 72 | ) 73 | ); 74 | 75 | assert_eq!( 76 | grammar::parse("1 * 2 * 3").unwrap(), 77 | Expression::Mul( 78 | Box::new(Expression::Mul( 79 | Box::new(Expression::Number(1)), 80 | (), 81 | Box::new(Expression::Number(2)) 82 | )), 83 | (), 84 | Box::new(Expression::Number(3)) 85 | ) 86 | ); 87 | 88 | assert_eq!( 89 | grammar::parse("1 * 2 - 3").unwrap(), 90 | Expression::Sub( 91 | Box::new(Expression::Mul( 92 | Box::new(Expression::Number(1)), 93 | (), 94 | Box::new(Expression::Number(2)) 95 | )), 96 | (), 97 | Box::new(Expression::Number(3)) 98 | ) 99 | ); 100 | } 101 | 102 | #[test] 103 | fn failed_parses() { 104 | insta::assert_debug_snapshot!(grammar::parse("1 + 2")); 105 | insta::assert_debug_snapshot!(grammar::parse("1 - 2 -")); 106 | insta::assert_debug_snapshot!(grammar::parse("a1")); 107 | insta::assert_debug_snapshot!(grammar::parse("1a")); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__struct_optional.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub struct Language\n {\n #[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] v : Option < i32 >, t : Option < Number\n >,\n } pub struct Number\n {\n #[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] v : i32\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub struct Language { 7 | v: Option, 8 | t: Option, 9 | } 10 | impl ::rust_sitter::Extract for Language { 11 | type LeafFn = (); 12 | #[allow(non_snake_case)] 13 | fn extract( 14 | node: Option<::rust_sitter::tree_sitter::Node>, 15 | source: &[u8], 16 | last_idx: usize, 17 | _leaf_fn: Option<&Self::LeafFn>, 18 | ) -> Self { 19 | let node = node.unwrap(); 20 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 21 | Language { 22 | v: { 23 | ::rust_sitter::__private::extract_field::< 24 | Option>, 25 | _, 26 | >( 27 | cursor, source, last_idx, "v", Some(&|v| v.parse().unwrap()) 28 | ) 29 | }, 30 | t: { 31 | ::rust_sitter::__private::extract_field::, _>( 32 | cursor, source, last_idx, "t", None, 33 | ) 34 | }, 35 | } 36 | }) 37 | } 38 | } 39 | pub struct Number { 40 | v: i32, 41 | } 42 | impl ::rust_sitter::Extract for Number { 43 | type LeafFn = (); 44 | #[allow(non_snake_case)] 45 | fn extract( 46 | node: Option<::rust_sitter::tree_sitter::Node>, 47 | source: &[u8], 48 | last_idx: usize, 49 | _leaf_fn: Option<&Self::LeafFn>, 50 | ) -> Self { 51 | let node = node.unwrap(); 52 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 53 | Number { 54 | v: { 55 | ::rust_sitter::__private::extract_field::, _>( 56 | cursor, 57 | source, 58 | last_idx, 59 | "v", 60 | Some(&|v| v.parse().unwrap()), 61 | ) 62 | }, 63 | } 64 | }) 65 | } 66 | } 67 | extern "C" { 68 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 69 | } 70 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 71 | unsafe { tree_sitter_test() } 72 | } 73 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 74 | #[doc = "[`Language`]"] 75 | #[doc = r" instance containing the parsed structured data."] 76 | pub fn parse( 77 | input: &str, 78 | ) -> core::result::Result> { 79 | ::rust_sitter::__private::parse::(input, language) 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__enum_with_unamed_vector.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n pub struct Number\n {\n #[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] value : u32\n } #[rust_sitter :: language] pub enum Expr\n {\n Numbers(#[rust_sitter :: repeat(non_empty = true)] Vec <\n Number >)\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub struct Number { 7 | value: u32, 8 | } 9 | impl ::rust_sitter::Extract for Number { 10 | type LeafFn = (); 11 | #[allow(non_snake_case)] 12 | fn extract( 13 | node: Option<::rust_sitter::tree_sitter::Node>, 14 | source: &[u8], 15 | last_idx: usize, 16 | _leaf_fn: Option<&Self::LeafFn>, 17 | ) -> Self { 18 | let node = node.unwrap(); 19 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 20 | Number { 21 | value: { 22 | ::rust_sitter::__private::extract_field::, _>( 23 | cursor, 24 | source, 25 | last_idx, 26 | "value", 27 | Some(&|v| v.parse().unwrap()), 28 | ) 29 | }, 30 | } 31 | }) 32 | } 33 | } 34 | pub enum Expr { 35 | Numbers(Vec), 36 | } 37 | impl ::rust_sitter::Extract for Expr { 38 | type LeafFn = (); 39 | #[allow(non_snake_case)] 40 | fn extract( 41 | node: Option<::rust_sitter::tree_sitter::Node>, 42 | source: &[u8], 43 | _last_idx: usize, 44 | _leaf_fn: Option<&Self::LeafFn>, 45 | ) -> Self { 46 | let node = node.unwrap(); 47 | let mut cursor = node.walk(); 48 | assert!( 49 | cursor.goto_first_child(), 50 | "Could not find a child corresponding to any enum branch" 51 | ); 52 | loop { 53 | let node = cursor.node(); 54 | match node.kind() { 55 | "Expr_Numbers" => { 56 | return ::rust_sitter::__private::extract_struct_or_variant( 57 | node, 58 | move |cursor, last_idx| { 59 | Expr::Numbers({ 60 | ::rust_sitter::__private::extract_field::, _>( 61 | cursor, source, last_idx, "0", None, 62 | ) 63 | }) 64 | }, 65 | ) 66 | } 67 | _ => { 68 | if !cursor.goto_next_sibling() { 69 | panic!("Could not find a child corresponding to any enum branch") 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | extern "C" { 77 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 78 | } 79 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 80 | unsafe { tree_sitter_test() } 81 | } 82 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 83 | #[doc = "[`Expr`]"] 84 | #[doc = r" instance containing the parsed structured data."] 85 | pub fn parse( 86 | input: &str, 87 | ) -> core::result::Result> { 88 | ::rust_sitter::__private::parse::(input, language) 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__enum_with_named_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub enum Expr\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] u32), Neg\n {\n #[rust_sitter :: leaf(text = \"!\")] _bang : (), value : Box <\n Expr >,\n }\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub enum Expr { 7 | Number(u32), 8 | Neg { _bang: (), value: Box }, 9 | } 10 | impl ::rust_sitter::Extract for Expr { 11 | type LeafFn = (); 12 | #[allow(non_snake_case)] 13 | fn extract( 14 | node: Option<::rust_sitter::tree_sitter::Node>, 15 | source: &[u8], 16 | _last_idx: usize, 17 | _leaf_fn: Option<&Self::LeafFn>, 18 | ) -> Self { 19 | let node = node.unwrap(); 20 | let mut cursor = node.walk(); 21 | assert!( 22 | cursor.goto_first_child(), 23 | "Could not find a child corresponding to any enum branch" 24 | ); 25 | loop { 26 | let node = cursor.node(); 27 | match node.kind() { 28 | "Expr_Number" => { 29 | return ::rust_sitter::__private::extract_struct_or_variant( 30 | node, 31 | move |cursor, last_idx| { 32 | Expr::Number({ 33 | ::rust_sitter::__private::extract_field::< 34 | rust_sitter::WithLeaf, 35 | _, 36 | >( 37 | cursor, source, last_idx, "0", Some(&|v| v.parse().unwrap()) 38 | ) 39 | }) 40 | }, 41 | ) 42 | } 43 | "Expr_Neg" => { 44 | return ::rust_sitter::__private::extract_struct_or_variant( 45 | node, 46 | move |cursor, last_idx| Expr::Neg { 47 | _bang: { 48 | ::rust_sitter::__private::extract_field::<(), _>( 49 | cursor, source, last_idx, "_bang", None, 50 | ) 51 | }, 52 | value: { 53 | ::rust_sitter::__private::extract_field::, _>( 54 | cursor, source, last_idx, "value", None, 55 | ) 56 | }, 57 | }, 58 | ) 59 | } 60 | _ => { 61 | if !cursor.goto_next_sibling() { 62 | panic!("Could not find a child corresponding to any enum branch") 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } 69 | extern "C" { 70 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 71 | } 72 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 73 | unsafe { tree_sitter_test() } 74 | } 75 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 76 | #[doc = "[`Expr`]"] 77 | #[doc = r" instance containing the parsed structured data."] 78 | pub fn parse( 79 | input: &str, 80 | ) -> core::result::Result> { 81 | ::rust_sitter::__private::parse::(input, language) 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__grammar_unboxed_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub struct Language\n { e : Expression, } pub enum Expression\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v : & str | v.parse ::\n < i32 > ().unwrap())] i32),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub struct Language { 7 | e: Expression, 8 | } 9 | impl ::rust_sitter::Extract for Language { 10 | type LeafFn = (); 11 | #[allow(non_snake_case)] 12 | fn extract( 13 | node: Option<::rust_sitter::tree_sitter::Node>, 14 | source: &[u8], 15 | last_idx: usize, 16 | _leaf_fn: Option<&Self::LeafFn>, 17 | ) -> Self { 18 | let node = node.unwrap(); 19 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 20 | Language { 21 | e: { 22 | ::rust_sitter::__private::extract_field::( 23 | cursor, source, last_idx, "e", None, 24 | ) 25 | }, 26 | } 27 | }) 28 | } 29 | } 30 | pub enum Expression { 31 | Number(i32), 32 | } 33 | impl ::rust_sitter::Extract for Expression { 34 | type LeafFn = (); 35 | #[allow(non_snake_case)] 36 | fn extract( 37 | node: Option<::rust_sitter::tree_sitter::Node>, 38 | source: &[u8], 39 | _last_idx: usize, 40 | _leaf_fn: Option<&Self::LeafFn>, 41 | ) -> Self { 42 | let node = node.unwrap(); 43 | let mut cursor = node.walk(); 44 | assert!( 45 | cursor.goto_first_child(), 46 | "Could not find a child corresponding to any enum branch" 47 | ); 48 | loop { 49 | let node = cursor.node(); 50 | match node.kind() { 51 | "Expression_Number" => { 52 | return ::rust_sitter::__private::extract_struct_or_variant( 53 | node, 54 | move |cursor, last_idx| { 55 | Expression::Number({ 56 | ::rust_sitter::__private::extract_field::< 57 | rust_sitter::WithLeaf, 58 | _, 59 | >( 60 | cursor, 61 | source, 62 | last_idx, 63 | "0", 64 | Some(&|v: &str| v.parse::().unwrap()), 65 | ) 66 | }) 67 | }, 68 | ) 69 | } 70 | _ => { 71 | if !cursor.goto_next_sibling() { 72 | panic!("Could not find a child corresponding to any enum branch") 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | extern "C" { 80 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 81 | } 82 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 83 | unsafe { tree_sitter_test() } 84 | } 85 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 86 | #[doc = "[`Language`]"] 87 | #[doc = r" instance containing the parsed structured data."] 88 | pub fn parse( 89 | input: &str, 90 | ) -> core::result::Result> { 91 | ::rust_sitter::__private::parse::(input, language) 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__enum_recursive.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub enum Expression\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] i32),\n Neg(#[rust_sitter :: leaf(text = \"-\")] (), Box < Expression\n >),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub enum Expression { 7 | Number(i32), 8 | Neg((), Box), 9 | } 10 | impl ::rust_sitter::Extract for Expression { 11 | type LeafFn = (); 12 | #[allow(non_snake_case)] 13 | fn extract( 14 | node: Option<::rust_sitter::tree_sitter::Node>, 15 | source: &[u8], 16 | _last_idx: usize, 17 | _leaf_fn: Option<&Self::LeafFn>, 18 | ) -> Self { 19 | let node = node.unwrap(); 20 | let mut cursor = node.walk(); 21 | assert!( 22 | cursor.goto_first_child(), 23 | "Could not find a child corresponding to any enum branch" 24 | ); 25 | loop { 26 | let node = cursor.node(); 27 | match node.kind() { 28 | "Expression_Number" => { 29 | return ::rust_sitter::__private::extract_struct_or_variant( 30 | node, 31 | move |cursor, last_idx| { 32 | Expression::Number({ 33 | ::rust_sitter::__private::extract_field::< 34 | rust_sitter::WithLeaf, 35 | _, 36 | >( 37 | cursor, source, last_idx, "0", Some(&|v| v.parse().unwrap()) 38 | ) 39 | }) 40 | }, 41 | ) 42 | } 43 | "Expression_Neg" => { 44 | return ::rust_sitter::__private::extract_struct_or_variant( 45 | node, 46 | move |cursor, last_idx| { 47 | Expression::Neg( 48 | { 49 | ::rust_sitter::__private::extract_field::<(), _>( 50 | cursor, source, last_idx, "0", None, 51 | ) 52 | }, 53 | { 54 | ::rust_sitter::__private::extract_field::, _>( 55 | cursor, source, last_idx, "1", None, 56 | ) 57 | }, 58 | ) 59 | }, 60 | ) 61 | } 62 | _ => { 63 | if !cursor.goto_next_sibling() { 64 | panic!("Could not find a child corresponding to any enum branch") 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | extern "C" { 72 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 73 | } 74 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 75 | unsafe { tree_sitter_test() } 76 | } 77 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 78 | #[doc = "[`Expression`]"] 79 | #[doc = r" instance containing the parsed structured data."] 80 | pub fn parse( 81 | input: &str, 82 | ) -> core::result::Result> { 83 | ::rust_sitter::__private::parse::(input, language) 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__struct_extra.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub enum Expression\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] i32,),\n } #[rust_sitter :: extra] struct Whitespace\n {\n #[rust_sitter :: leaf(pattern = r\"\\s\")] _whitespace : (),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub enum Expression { 7 | Number(i32), 8 | } 9 | impl ::rust_sitter::Extract for Expression { 10 | type LeafFn = (); 11 | #[allow(non_snake_case)] 12 | fn extract( 13 | node: Option<::rust_sitter::tree_sitter::Node>, 14 | source: &[u8], 15 | _last_idx: usize, 16 | _leaf_fn: Option<&Self::LeafFn>, 17 | ) -> Self { 18 | let node = node.unwrap(); 19 | let mut cursor = node.walk(); 20 | assert!( 21 | cursor.goto_first_child(), 22 | "Could not find a child corresponding to any enum branch" 23 | ); 24 | loop { 25 | let node = cursor.node(); 26 | match node.kind() { 27 | "Expression_Number" => { 28 | return ::rust_sitter::__private::extract_struct_or_variant( 29 | node, 30 | move |cursor, last_idx| { 31 | Expression::Number({ 32 | ::rust_sitter::__private::extract_field::< 33 | rust_sitter::WithLeaf, 34 | _, 35 | >( 36 | cursor, source, last_idx, "0", Some(&|v| v.parse().unwrap()) 37 | ) 38 | }) 39 | }, 40 | ) 41 | } 42 | _ => { 43 | if !cursor.goto_next_sibling() { 44 | panic!("Could not find a child corresponding to any enum branch") 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | struct Whitespace { 52 | _whitespace: (), 53 | } 54 | impl ::rust_sitter::Extract for Whitespace { 55 | type LeafFn = (); 56 | #[allow(non_snake_case)] 57 | fn extract( 58 | node: Option<::rust_sitter::tree_sitter::Node>, 59 | source: &[u8], 60 | last_idx: usize, 61 | _leaf_fn: Option<&Self::LeafFn>, 62 | ) -> Self { 63 | let node = node.unwrap(); 64 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 65 | Whitespace { 66 | _whitespace: { 67 | ::rust_sitter::__private::extract_field::<(), _>( 68 | cursor, 69 | source, 70 | last_idx, 71 | "_whitespace", 72 | None, 73 | ) 74 | }, 75 | } 76 | }) 77 | } 78 | } 79 | extern "C" { 80 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 81 | } 82 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 83 | unsafe { tree_sitter_test() } 84 | } 85 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 86 | #[doc = "[`Expression`]"] 87 | #[doc = r" instance containing the parsed structured data."] 88 | pub fn parse( 89 | input: &str, 90 | ) -> core::result::Result> { 91 | ::rust_sitter::__private::parse::(input, language) 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /common/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use syn::{ 4 | parse::{Parse, ParseStream}, 5 | punctuated::Punctuated, 6 | *, 7 | }; 8 | 9 | #[derive(Debug, Clone, PartialEq, Eq)] 10 | pub struct NameValueExpr { 11 | pub path: Ident, 12 | pub eq_token: Token![=], 13 | pub expr: Expr, 14 | } 15 | 16 | impl Parse for NameValueExpr { 17 | fn parse(input: ParseStream) -> syn::Result { 18 | Ok(NameValueExpr { 19 | path: input.parse()?, 20 | eq_token: input.parse()?, 21 | expr: input.parse()?, 22 | }) 23 | } 24 | } 25 | 26 | #[derive(Debug, Clone, PartialEq, Eq)] 27 | pub struct FieldThenParams { 28 | pub field: Field, 29 | pub comma: Option, 30 | pub params: Punctuated, 31 | } 32 | 33 | impl Parse for FieldThenParams { 34 | fn parse(input: ParseStream) -> syn::Result { 35 | let field = Field::parse_unnamed(input)?; 36 | let comma: Option = input.parse()?; 37 | let params = if comma.is_some() { 38 | Punctuated::parse_terminated_with(input, NameValueExpr::parse)? 39 | } else { 40 | Punctuated::new() 41 | }; 42 | 43 | Ok(FieldThenParams { 44 | field, 45 | comma, 46 | params, 47 | }) 48 | } 49 | } 50 | 51 | pub fn try_extract_inner_type( 52 | ty: &Type, 53 | inner_of: &str, 54 | skip_over: &HashSet<&str>, 55 | ) -> (Type, bool) { 56 | if let Type::Path(p) = &ty { 57 | let type_segment = p.path.segments.last().unwrap(); 58 | if type_segment.ident == inner_of { 59 | let leaf_type = if let PathArguments::AngleBracketed(p) = &type_segment.arguments { 60 | if let GenericArgument::Type(t) = p.args.first().unwrap().clone() { 61 | t 62 | } else { 63 | panic!("Argument in angle brackets must be a type") 64 | } 65 | } else { 66 | panic!("Expected angle bracketed path"); 67 | }; 68 | 69 | (leaf_type, true) 70 | } else if skip_over.contains(type_segment.ident.to_string().as_str()) { 71 | if let PathArguments::AngleBracketed(p) = &type_segment.arguments { 72 | if let GenericArgument::Type(t) = p.args.first().unwrap().clone() { 73 | try_extract_inner_type(&t, inner_of, skip_over) 74 | } else { 75 | panic!("Argument in angle brackets must be a type") 76 | } 77 | } else { 78 | panic!("Expected angle bracketed path"); 79 | } 80 | } else { 81 | (ty.clone(), false) 82 | } 83 | } else { 84 | (ty.clone(), false) 85 | } 86 | } 87 | 88 | pub fn filter_inner_type(ty: &Type, skip_over: &HashSet<&str>) -> Type { 89 | if let Type::Path(p) = &ty { 90 | let type_segment = p.path.segments.last().unwrap(); 91 | if skip_over.contains(type_segment.ident.to_string().as_str()) { 92 | if let PathArguments::AngleBracketed(p) = &type_segment.arguments { 93 | if let GenericArgument::Type(t) = p.args.first().unwrap().clone() { 94 | filter_inner_type(&t, skip_over) 95 | } else { 96 | panic!("Argument in angle brackets must be a type") 97 | } 98 | } else { 99 | panic!("Expected angle bracketed path"); 100 | } 101 | } else { 102 | ty.clone() 103 | } 104 | } else { 105 | ty.clone() 106 | } 107 | } 108 | 109 | pub fn wrap_leaf_type(ty: &Type, skip_over: &HashSet<&str>) -> Type { 110 | let mut ty = ty.clone(); 111 | if let Type::Path(p) = &mut ty { 112 | let type_segment = p.path.segments.last_mut().unwrap(); 113 | if skip_over.contains(type_segment.ident.to_string().as_str()) { 114 | if let PathArguments::AngleBracketed(args) = &mut type_segment.arguments { 115 | for a in args.args.iter_mut() { 116 | if let syn::GenericArgument::Type(t) = a { 117 | *t = wrap_leaf_type(t, skip_over); 118 | } 119 | } 120 | 121 | ty 122 | } else { 123 | panic!("Expected angle bracketed path"); 124 | } 125 | } else { 126 | parse_quote!(rust_sitter::WithLeaf<#ty>) 127 | } 128 | } else { 129 | parse_quote!(rust_sitter::WithLeaf<#ty>) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__struct_repeat.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub struct NumberList\n { numbers : Vec < Number >, } pub struct Number\n {\n #[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] v : i32\n } #[rust_sitter :: extra] struct Whitespace\n {\n #[rust_sitter :: leaf(pattern = r\"\\s\")] _whitespace : (),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub struct NumberList { 7 | numbers: Vec, 8 | } 9 | impl ::rust_sitter::Extract for NumberList { 10 | type LeafFn = (); 11 | #[allow(non_snake_case)] 12 | fn extract( 13 | node: Option<::rust_sitter::tree_sitter::Node>, 14 | source: &[u8], 15 | last_idx: usize, 16 | _leaf_fn: Option<&Self::LeafFn>, 17 | ) -> Self { 18 | let node = node.unwrap(); 19 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 20 | NumberList { 21 | numbers: { 22 | ::rust_sitter::__private::extract_field::, _>( 23 | cursor, source, last_idx, "numbers", None, 24 | ) 25 | }, 26 | } 27 | }) 28 | } 29 | } 30 | pub struct Number { 31 | v: i32, 32 | } 33 | impl ::rust_sitter::Extract for Number { 34 | type LeafFn = (); 35 | #[allow(non_snake_case)] 36 | fn extract( 37 | node: Option<::rust_sitter::tree_sitter::Node>, 38 | source: &[u8], 39 | last_idx: usize, 40 | _leaf_fn: Option<&Self::LeafFn>, 41 | ) -> Self { 42 | let node = node.unwrap(); 43 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 44 | Number { 45 | v: { 46 | ::rust_sitter::__private::extract_field::, _>( 47 | cursor, 48 | source, 49 | last_idx, 50 | "v", 51 | Some(&|v| v.parse().unwrap()), 52 | ) 53 | }, 54 | } 55 | }) 56 | } 57 | } 58 | struct Whitespace { 59 | _whitespace: (), 60 | } 61 | impl ::rust_sitter::Extract for Whitespace { 62 | type LeafFn = (); 63 | #[allow(non_snake_case)] 64 | fn extract( 65 | node: Option<::rust_sitter::tree_sitter::Node>, 66 | source: &[u8], 67 | last_idx: usize, 68 | _leaf_fn: Option<&Self::LeafFn>, 69 | ) -> Self { 70 | let node = node.unwrap(); 71 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 72 | Whitespace { 73 | _whitespace: { 74 | ::rust_sitter::__private::extract_field::<(), _>( 75 | cursor, 76 | source, 77 | last_idx, 78 | "_whitespace", 79 | None, 80 | ) 81 | }, 82 | } 83 | }) 84 | } 85 | } 86 | extern "C" { 87 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 88 | } 89 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 90 | unsafe { tree_sitter_test() } 91 | } 92 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 93 | #[doc = "[`NumberList`]"] 94 | #[doc = r" instance containing the parsed structured data."] 95 | pub fn parse( 96 | input: &str, 97 | ) -> core::result::Result> { 98 | ::rust_sitter::__private::parse::(input, language) 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__enum_prec_left.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n #[rust_sitter :: language] pub enum Expression\n {\n Number(#[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] i32), #[rust_sitter :: prec_left(1)]\n Sub(Box < Expression >, #[rust_sitter :: leaf(text = \"-\")]\n (), Box < Expression >),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | pub enum Expression { 7 | Number(i32), 8 | Sub(Box, (), Box), 9 | } 10 | impl ::rust_sitter::Extract for Expression { 11 | type LeafFn = (); 12 | #[allow(non_snake_case)] 13 | fn extract( 14 | node: Option<::rust_sitter::tree_sitter::Node>, 15 | source: &[u8], 16 | _last_idx: usize, 17 | _leaf_fn: Option<&Self::LeafFn>, 18 | ) -> Self { 19 | let node = node.unwrap(); 20 | let mut cursor = node.walk(); 21 | assert!( 22 | cursor.goto_first_child(), 23 | "Could not find a child corresponding to any enum branch" 24 | ); 25 | loop { 26 | let node = cursor.node(); 27 | match node.kind() { 28 | "Expression_Number" => { 29 | return ::rust_sitter::__private::extract_struct_or_variant( 30 | node, 31 | move |cursor, last_idx| { 32 | Expression::Number({ 33 | ::rust_sitter::__private::extract_field::< 34 | rust_sitter::WithLeaf, 35 | _, 36 | >( 37 | cursor, source, last_idx, "0", Some(&|v| v.parse().unwrap()) 38 | ) 39 | }) 40 | }, 41 | ) 42 | } 43 | "Expression_Sub" => { 44 | return ::rust_sitter::__private::extract_struct_or_variant( 45 | node, 46 | move |cursor, last_idx| { 47 | Expression::Sub( 48 | { 49 | ::rust_sitter::__private::extract_field::, _>( 50 | cursor, source, last_idx, "0", None, 51 | ) 52 | }, 53 | { 54 | ::rust_sitter::__private::extract_field::<(), _>( 55 | cursor, source, last_idx, "1", None, 56 | ) 57 | }, 58 | { 59 | ::rust_sitter::__private::extract_field::, _>( 60 | cursor, source, last_idx, "2", None, 61 | ) 62 | }, 63 | ) 64 | }, 65 | ) 66 | } 67 | _ => { 68 | if !cursor.goto_next_sibling() { 69 | panic!("Could not find a child corresponding to any enum branch") 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | extern "C" { 77 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 78 | } 79 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 80 | unsafe { tree_sitter_test() } 81 | } 82 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 83 | #[doc = "[`Expression`]"] 84 | #[doc = r" instance containing the parsed structured data."] 85 | pub fn parse( 86 | input: &str, 87 | ) -> core::result::Result> { 88 | ::rust_sitter::__private::parse::(input, language) 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /macro/src/snapshots/rust_sitter_macro__tests__spanned_in_vec.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/src/lib.rs 3 | expression: "rustfmt_code(&expand_grammar(parse_quote! {\n #[rust_sitter :: grammar(\"test\")] mod grammar\n {\n use rust_sitter :: Spanned ; #[rust_sitter :: language] pub\n struct NumberList { numbers : Vec < Spanned < Number >>, }\n pub struct Number\n {\n #[rust_sitter ::\n leaf(pattern = r\"\\d+\", transform = | v |\n v.parse().unwrap())] v : i32\n } #[rust_sitter :: extra] struct Whitespace\n {\n #[rust_sitter :: leaf(pattern = r\"\\s\")] _whitespace : (),\n }\n }\n })?.to_token_stream().to_string())" 4 | --- 5 | mod grammar { 6 | use rust_sitter::Spanned; 7 | pub struct NumberList { 8 | numbers: Vec>, 9 | } 10 | impl ::rust_sitter::Extract for NumberList { 11 | type LeafFn = (); 12 | #[allow(non_snake_case)] 13 | fn extract( 14 | node: Option<::rust_sitter::tree_sitter::Node>, 15 | source: &[u8], 16 | last_idx: usize, 17 | _leaf_fn: Option<&Self::LeafFn>, 18 | ) -> Self { 19 | let node = node.unwrap(); 20 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 21 | NumberList { 22 | numbers: { 23 | ::rust_sitter::__private::extract_field::>, _>( 24 | cursor, source, last_idx, "numbers", None, 25 | ) 26 | }, 27 | } 28 | }) 29 | } 30 | } 31 | pub struct Number { 32 | v: i32, 33 | } 34 | impl ::rust_sitter::Extract for Number { 35 | type LeafFn = (); 36 | #[allow(non_snake_case)] 37 | fn extract( 38 | node: Option<::rust_sitter::tree_sitter::Node>, 39 | source: &[u8], 40 | last_idx: usize, 41 | _leaf_fn: Option<&Self::LeafFn>, 42 | ) -> Self { 43 | let node = node.unwrap(); 44 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 45 | Number { 46 | v: { 47 | ::rust_sitter::__private::extract_field::, _>( 48 | cursor, 49 | source, 50 | last_idx, 51 | "v", 52 | Some(&|v| v.parse().unwrap()), 53 | ) 54 | }, 55 | } 56 | }) 57 | } 58 | } 59 | struct Whitespace { 60 | _whitespace: (), 61 | } 62 | impl ::rust_sitter::Extract for Whitespace { 63 | type LeafFn = (); 64 | #[allow(non_snake_case)] 65 | fn extract( 66 | node: Option<::rust_sitter::tree_sitter::Node>, 67 | source: &[u8], 68 | last_idx: usize, 69 | _leaf_fn: Option<&Self::LeafFn>, 70 | ) -> Self { 71 | let node = node.unwrap(); 72 | ::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| { 73 | Whitespace { 74 | _whitespace: { 75 | ::rust_sitter::__private::extract_field::<(), _>( 76 | cursor, 77 | source, 78 | last_idx, 79 | "_whitespace", 80 | None, 81 | ) 82 | }, 83 | } 84 | }) 85 | } 86 | } 87 | extern "C" { 88 | fn tree_sitter_test() -> ::rust_sitter::tree_sitter::Language; 89 | } 90 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 91 | unsafe { tree_sitter_test() } 92 | } 93 | #[doc = r" Parse an input string according to the grammar. Returns either any parsing errors that happened, or a"] 94 | #[doc = "[`NumberList`]"] 95 | #[doc = r" instance containing the parsed structured data."] 96 | pub fn parse( 97 | input: &str, 98 | ) -> core::result::Result> { 99 | ::rust_sitter::__private::parse::(input, language) 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod __private; 2 | 3 | use std::ops::Deref; 4 | 5 | pub use rust_sitter_macro::*; 6 | 7 | #[cfg(feature = "tree-sitter-standard")] 8 | pub use tree_sitter_runtime_standard as tree_sitter; 9 | 10 | #[cfg(feature = "tree-sitter-c2rust")] 11 | pub use tree_sitter_runtime_c2rust as tree_sitter; 12 | 13 | /// Defines the logic used to convert a node in a Tree Sitter tree to 14 | /// the corresponding Rust type. 15 | pub trait Extract { 16 | type LeafFn: ?Sized; 17 | fn extract( 18 | node: Option, 19 | source: &[u8], 20 | last_idx: usize, 21 | leaf_fn: Option<&Self::LeafFn>, 22 | ) -> Output; 23 | } 24 | 25 | pub struct WithLeaf { 26 | _phantom: std::marker::PhantomData, 27 | } 28 | 29 | impl Extract for WithLeaf { 30 | type LeafFn = dyn Fn(&str) -> L; 31 | 32 | fn extract( 33 | node: Option, 34 | source: &[u8], 35 | _last_idx: usize, 36 | leaf_fn: Option<&Self::LeafFn>, 37 | ) -> L { 38 | node.and_then(|n| n.utf8_text(source).ok()) 39 | .map(|s| leaf_fn.unwrap()(s)) 40 | .unwrap() 41 | } 42 | } 43 | 44 | impl Extract<()> for () { 45 | type LeafFn = (); 46 | fn extract( 47 | _node: Option, 48 | _source: &[u8], 49 | _last_idx: usize, 50 | _leaf_fn: Option<&Self::LeafFn>, 51 | ) { 52 | } 53 | } 54 | 55 | impl, U> Extract> for Option { 56 | type LeafFn = T::LeafFn; 57 | fn extract( 58 | node: Option, 59 | source: &[u8], 60 | last_idx: usize, 61 | leaf_fn: Option<&Self::LeafFn>, 62 | ) -> Option { 63 | node.map(|n| T::extract(Some(n), source, last_idx, leaf_fn)) 64 | } 65 | } 66 | 67 | impl, U> Extract> for Box { 68 | type LeafFn = T::LeafFn; 69 | fn extract( 70 | node: Option, 71 | source: &[u8], 72 | last_idx: usize, 73 | leaf_fn: Option<&Self::LeafFn>, 74 | ) -> Box { 75 | Box::new(T::extract(node, source, last_idx, leaf_fn)) 76 | } 77 | } 78 | 79 | impl, U> Extract> for Vec { 80 | type LeafFn = T::LeafFn; 81 | fn extract( 82 | node: Option, 83 | source: &[u8], 84 | mut last_idx: usize, 85 | leaf_fn: Option<&Self::LeafFn>, 86 | ) -> Vec { 87 | node.map(|node| { 88 | let mut cursor = node.walk(); 89 | let mut out = vec![]; 90 | if cursor.goto_first_child() { 91 | loop { 92 | let n = cursor.node(); 93 | if cursor.field_name().is_some() { 94 | out.push(T::extract(Some(n), source, last_idx, leaf_fn)); 95 | } 96 | 97 | last_idx = n.end_byte(); 98 | 99 | if !cursor.goto_next_sibling() { 100 | break; 101 | } 102 | } 103 | } 104 | 105 | out 106 | }) 107 | .unwrap_or_default() 108 | } 109 | } 110 | 111 | #[derive(Clone, Debug)] 112 | /// A wrapper around a value that also contains the span of the value in the source. 113 | pub struct Spanned { 114 | /// The underlying parsed node. 115 | pub value: T, 116 | /// The span of the node in the source. The first value is the inclusive start 117 | /// of the span, and the second value is the exclusive end of the span. 118 | pub span: (usize, usize), 119 | } 120 | 121 | impl Deref for Spanned { 122 | type Target = T; 123 | 124 | fn deref(&self) -> &T { 125 | &self.value 126 | } 127 | } 128 | 129 | impl, U> Extract> for Spanned { 130 | type LeafFn = T::LeafFn; 131 | fn extract( 132 | node: Option, 133 | source: &[u8], 134 | last_idx: usize, 135 | leaf_fn: Option<&Self::LeafFn>, 136 | ) -> Spanned { 137 | Spanned { 138 | value: T::extract(node, source, last_idx, leaf_fn), 139 | span: node 140 | .map(|n| (n.start_byte(), n.end_byte())) 141 | .unwrap_or((last_idx, last_idx)), 142 | } 143 | } 144 | } 145 | 146 | pub mod errors { 147 | #[cfg(feature = "tree-sitter-standard")] 148 | use tree_sitter_runtime_standard as tree_sitter; 149 | 150 | #[cfg(feature = "tree-sitter-c2rust")] 151 | use tree_sitter_runtime_c2rust as tree_sitter; 152 | 153 | #[derive(Debug)] 154 | /// An explanation for an error that occurred during parsing. 155 | pub enum ParseErrorReason { 156 | /// The parser did not expect to see some token. 157 | UnexpectedToken(String), 158 | /// Tree Sitter failed to parse a specific intermediate node. 159 | /// The underlying failures are in the vector. 160 | FailedNode(Vec), 161 | /// The parser expected a specific token, but it was not found. 162 | MissingToken(String), 163 | } 164 | 165 | #[derive(Debug)] 166 | /// An error that occurred during parsing. 167 | pub struct ParseError { 168 | pub reason: ParseErrorReason, 169 | /// Inclusive start of the error. 170 | pub start: usize, 171 | /// Exclusive end of the error. 172 | pub end: usize, 173 | } 174 | 175 | /// Given the root node of a Tree Sitter parsing result, accumulates all 176 | /// errors that were emitted. 177 | pub fn collect_parsing_errors( 178 | node: &tree_sitter::Node, 179 | source: &[u8], 180 | errors: &mut Vec, 181 | ) { 182 | if node.is_error() { 183 | if node.child(0).is_some() { 184 | // we managed to parse some children, so collect underlying errors for this node 185 | let mut inner_errors = vec![]; 186 | let mut cursor = node.walk(); 187 | node.children(&mut cursor) 188 | .for_each(|c| collect_parsing_errors(&c, source, &mut inner_errors)); 189 | 190 | errors.push(ParseError { 191 | reason: ParseErrorReason::FailedNode(inner_errors), 192 | start: node.start_byte(), 193 | end: node.end_byte(), 194 | }) 195 | } else { 196 | let contents = node.utf8_text(source).unwrap(); 197 | if !contents.is_empty() { 198 | errors.push(ParseError { 199 | reason: ParseErrorReason::UnexpectedToken(contents.to_string()), 200 | start: node.start_byte(), 201 | end: node.end_byte(), 202 | }) 203 | } else { 204 | errors.push(ParseError { 205 | reason: ParseErrorReason::FailedNode(vec![]), 206 | start: node.start_byte(), 207 | end: node.end_byte(), 208 | }) 209 | } 210 | } 211 | } else if node.is_missing() { 212 | errors.push(ParseError { 213 | reason: ParseErrorReason::MissingToken(node.kind().to_string()), 214 | start: node.start_byte(), 215 | end: node.end_byte(), 216 | }) 217 | } else if node.has_error() { 218 | let mut cursor = node.walk(); 219 | node.children(&mut cursor) 220 | .for_each(|c| collect_parsing_errors(&c, source, errors)); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Sitter 2 | [![Crates.io](https://img.shields.io/crates/v/rust-sitter)](https://crates.io/crates/rust-sitter) 3 | 4 | Rust Sitter makes it easy to create efficient parsers in Rust by leveraging the [Tree Sitter](https://tree-sitter.github.io/tree-sitter/) parser generator. With Rust Sitter, you can define your entire grammar with annotations on idiomatic Rust code, and let macros generate the parser and type-safe bindings for you! 5 | 6 | ## Installation 7 | First, add Rust/Tree Sitter to your `Cargo.toml`: 8 | ```toml 9 | [dependencies] 10 | rust-sitter = "0.4.5" 11 | 12 | [build-dependencies] 13 | rust-sitter-tool = "0.4.5" 14 | ``` 15 | 16 | _Note: By default, Rust Sitter uses a fork of Tree Sitter with a pure-Rust runtime to support `wasm32-unknown-unknown`. To use the standard C runtime instead, disable default features and enable the `tree-sitter-standard` feature_ 17 | 18 | The first step is to configure your `build.rs` to compile and link the generated Tree Sitter parser: 19 | 20 | ```rust 21 | use std::path::PathBuf; 22 | 23 | fn main() { 24 | println!("cargo:rerun-if-changed=src"); 25 | rust_sitter_tool::build_parsers(&PathBuf::from("src/main.rs")); 26 | } 27 | ``` 28 | 29 | ## Defining a Grammar 30 | Now that we have Rust Sitter added to our project, we can define our grammar. Rust Sitter grammars are defined in annotated Rust modules. First, we define the module that will contain our grammar 31 | 32 | ```rust 33 | #[rust_sitter::grammar("arithmetic")] 34 | mod grammar { 35 | 36 | } 37 | ``` 38 | 39 | Then, inside the module, we can define individual AST nodes. For this simple example, we'll define an expression that can be used in a mathematical expression. Note that we annotate this type as `#[rust_sitter::language]` to indicate that it is the root AST type. 40 | 41 | ```rust 42 | #[rust_sitter::language] 43 | pub enum Expr { 44 | Number(u32), 45 | Add(Box, Box) 46 | } 47 | ``` 48 | 49 | Now that we have the type defined, we must annotate the enum variants to describe how to identify them in the text being parsed. First, we can apply `rust_sitter::leaf` to use a regular expression to match digits corresponding to a number, and define a transformation that parses the resulting string into a `u32`. 50 | 51 | ```rust 52 | Number( 53 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 54 | u32, 55 | ) 56 | ``` 57 | 58 | For the `Add` variant, things are a bit more complicated. First, we add an extra field corresponding to the `+` that must sit between the two sub-expressions. This can be achieved with `text` parameter of `rust_sitter::leaf`, which instructs the parser to match a specific string. Because we are parsing to `()`, we do not need to provide a transformation. 59 | 60 | ```rust 61 | Add( 62 | Box, 63 | #[rust_sitter::leaf(text = "+")] (), 64 | Box, 65 | ) 66 | ``` 67 | 68 | If we try to compile this grammar, however, we will see ane error due to conflicting parse trees for expressions like `1 + 2 + 3`, which could be parsed as `(1 + 2) + 3` or `1 + (2 + 3)`. We want the former, so we can add a further annotation specifying that we want left-associativity for this rule. 69 | 70 | ```rust 71 | #[rust_sitter::prec_left(1)] 72 | Add( 73 | Box, 74 | #[rust_sitter::leaf(text = "+")] (), 75 | Box, 76 | ) 77 | ``` 78 | 79 | All together, our grammar looks like this: 80 | 81 | ```rust 82 | #[rust_sitter::grammar("arithmetic")] 83 | mod grammar { 84 | #[rust_sitter::language] 85 | pub enum Expr { 86 | Number( 87 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 88 | u32, 89 | ), 90 | #[rust_sitter::prec_left(1)] 91 | Add( 92 | Box, 93 | #[rust_sitter::leaf(text = "+")] (), 94 | Box, 95 | ) 96 | } 97 | } 98 | ``` 99 | 100 | We can then parse text using this grammar: 101 | 102 | ```rust 103 | dbg!(grammar::parse("1+2+3")); 104 | /* 105 | grammar::parse("1+2+3") = Ok(Add( 106 | Add( 107 | Number( 108 | 1, 109 | ), 110 | (), 111 | Number( 112 | 2, 113 | ), 114 | ), 115 | (), 116 | Number( 117 | 3, 118 | ), 119 | )) 120 | */ 121 | ``` 122 | 123 | ## Type Annotations 124 | Rust Sitter supports a number of annotations that can be applied to type and fields in your grammar. These annotations can be used to control how the parser behaves, and how the resulting AST is constructed. 125 | 126 | ### `#[rust_sitter::language]` 127 | This annotation marks the entrypoint for parsing, and determines which AST type will be returned from parsing. Only one type in the grammar can be marked as the entrypoint. 128 | 129 | ```rust 130 | #[rust_sitter::language] 131 | struct Code { 132 | ... 133 | } 134 | ```` 135 | 136 | ### `#[rust_sitter::extra]` 137 | This annotation marks a node as extra and can safely be skipped while parsing. This is useful for handling whitespace/newlines/comments. 138 | 139 | ```rust 140 | #[rust_sitter::extra] 141 | struct Whitespace { 142 | #[rust_sitter::leaf(pattern = r"\s")] 143 | _whitespace: (), 144 | } 145 | ``` 146 | 147 | ## Field Annotations 148 | ### `#[rust_sitter::leaf(...)]` 149 | The `#[rust_sitter::leaf(...)]` annotation can be used to define a leaf node in the AST. This annotation takes a number of parameters that control how the parser behaves: 150 | - the `pattern` parameter takes a regular expression that is used to match the text of the leaf node. This parameter is required. 151 | - the `text` parameter takes a string that is used to match the text of the leaf node. This parameter is mutually exclusive with `pattern`. 152 | - the `transform` parameter takes a function that is used to transform the matched text (an `&str`) into the desired type. This parameter is optional if the target type is `()`. 153 | 154 | `leaf` can either be applied to a field in a struct / enum variant (as seen above), or directly on a type with no fields: 155 | 156 | ```rust 157 | #[rust_sitter::leaf(text = "9")] 158 | struct BigDigit; 159 | 160 | enum SmallDigit { 161 | #[rust_sitter::leaf(text = "0")] 162 | Zero, 163 | #[rust_sitter::leaf(text = "1")] 164 | One, 165 | } 166 | ``` 167 | 168 | ### `#[rust_sitter::prec(...)]` / `#[rust_sitter::prec_left(...)]` / `#[rust_sitter::prec_right(...)]` 169 | This annotation can be used to define a non/left/right-associative operator. This annotation takes a single parameter, which is the precedence level of the operator (higher binds more tightly). 170 | 171 | ### `#[rust_sitter::skip(...)]` 172 | This annotation can be used to define a field that does not correspond to anything in the input string, such as some metadata. This annotation takes a single parameter, which is the value that should be used to populate that field at runtime. 173 | 174 | ### `#[rust_sitter::word]` 175 | This annotation marks the field as a Tree Sitter [word](https://tree-sitter.github.io/tree-sitter/creating-parsers#keywords), which is useful when handling errors involving keywords. Only one field in the grammar can be marked as a word. 176 | 177 | ## Special Types 178 | Rust Sitter has a few special types that can be used to define more complex grammars. 179 | 180 | ### `Vec` 181 | To parse repeating structures, you can use a `Vec` to parse a list of `T`s. Note that the `Vec` type **cannot** be wrapped in another `Vec` (create additional structs if this is necessary). There are two special attributes that can be applied to a `Vec` field to control the parsing behavior. 182 | 183 | The `#[rust_sitter::delimited(...)]` attribute can be used to specify a separator between elements of the list, and takes a parameter of the same format as an unnamed field. For example, we can define a grammar that parses a comma-separated list of expressions: 184 | 185 | ```rust 186 | pub struct CommaSeparatedExprs { 187 | #[rust_sitter::delimited( 188 | #[rust_sitter::leaf(text = ",")] 189 | () 190 | )] 191 | numbers: Vec, 192 | } 193 | ``` 194 | 195 | The `#[rust_sitter::repeat(...)]` attribute can be used to specify additional configuration for the parser. Currently, there is only one available parameter: `non_empty`, which takes a boolean that specifies if the list must contain at least one element. For example, we can define a grammar that parses a non-empty comma-separated list of numbers: 196 | 197 | ```rust 198 | pub struct CommaSeparatedExprs { 199 | #[rust_sitter::repeat(non_empty = true)] 200 | #[rust_sitter::delimited( 201 | #[rust_sitter::leaf(text = ",")] 202 | () 203 | )] 204 | numbers: Vec, 205 | } 206 | ``` 207 | 208 | ### `Option` 209 | To parse optional structures, you can use an `Option` to parse a single `T` or nothing. Like `Vec`, the `Option` type **cannot** be wrapped in another `Option` (create additional structs if this is necessary). For example, we can make the list elements in the previous example optional so we can parse strings like `1,,2`: 210 | 211 | ```rust 212 | pub struct CommaSeparatedExprs { 213 | #[rust_sitter::repeat(non_empty = true)] 214 | #[rust_sitter::delimited( 215 | #[rust_sitter::leaf(text = ",")] 216 | () 217 | )] 218 | numbers: Vec>, 219 | } 220 | ``` 221 | 222 | ### `rust_sitter::Spanned` 223 | When using Rust Sitter to power diagnostic tools, it can be helpful to access spans marking the sections of text corresponding to a parsed node. To do this, you can use the `Spanned` type, which captures the underlying parsed `T` and a pair of indices for the start (inclusive) and end (exclusive) of the corresponding substring. `Spanned` types can be used anywhere, and do not affect the parsing logic. For example, we could capture the spans of the expressions in our previous example: 224 | 225 | ```rust 226 | pub struct CommaSeparatedExprs { 227 | #[rust_sitter::repeat(non_empty = true)] 228 | #[rust_sitter::delimited( 229 | #[rust_sitter::leaf(text = ",")] 230 | () 231 | )] 232 | numbers: Vec>>, 233 | } 234 | ``` 235 | 236 | ### `Box` 237 | Boxes are automatically constructed around the inner type when parsing, but Rust Sitter doesn't do anything extra beyond that. 238 | 239 | ## Debugging 240 | 241 | To view the generated grammar, you can set the `RUST_SITTER_EMIT_ARTIFACTS` environment variable to `true`. This will cause the generated grammar to be written to wherever cargo sets `OUT_DIR` (usually `target/debug/build/-/out`). 242 | -------------------------------------------------------------------------------- /macro/src/expansion.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use crate::errors::IteratorExt as _; 4 | use proc_macro2::Span; 5 | use quote::{quote, ToTokens}; 6 | use rust_sitter_common::*; 7 | use syn::{parse::Parse, punctuated::Punctuated, *}; 8 | 9 | fn is_sitter_attr(attr: &Attribute) -> bool { 10 | attr.path() 11 | .segments 12 | .iter() 13 | .next() 14 | .map(|segment| segment.ident == "rust_sitter") 15 | .unwrap_or(false) 16 | } 17 | 18 | pub enum ParamOrField { 19 | Param(Expr), 20 | Field(FieldValue), 21 | } 22 | 23 | impl ToTokens for ParamOrField { 24 | fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { 25 | match self { 26 | ParamOrField::Param(expr) => expr.to_tokens(tokens), 27 | ParamOrField::Field(field) => field.to_tokens(tokens), 28 | } 29 | } 30 | } 31 | 32 | fn gen_field(ident_str: String, leaf: Field) -> Expr { 33 | let leaf_type = leaf.ty; 34 | 35 | let leaf_attr = leaf 36 | .attrs 37 | .iter() 38 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::leaf)); 39 | 40 | let leaf_params = leaf_attr.and_then(|a| { 41 | a.parse_args_with(Punctuated::::parse_terminated) 42 | .ok() 43 | }); 44 | 45 | let transform_param = leaf_params.as_ref().and_then(|p| { 46 | p.iter() 47 | .find(|param| param.path == "transform") 48 | .map(|p| p.expr.clone()) 49 | }); 50 | 51 | let (leaf_type, closure_expr): (Type, Expr) = match transform_param { 52 | Some(closure) => { 53 | let mut non_leaf = HashSet::new(); 54 | non_leaf.insert("Spanned"); 55 | non_leaf.insert("Box"); 56 | non_leaf.insert("Option"); 57 | non_leaf.insert("Vec"); 58 | let wrapped_leaf_type = wrap_leaf_type(&leaf_type, &non_leaf); 59 | (wrapped_leaf_type, syn::parse_quote!(Some(&#closure))) 60 | } 61 | None => (leaf_type, syn::parse_quote!(None)), 62 | }; 63 | 64 | syn::parse_quote!({ 65 | ::rust_sitter::__private::extract_field::<#leaf_type,_>(cursor, source, last_idx, #ident_str, #closure_expr) 66 | }) 67 | } 68 | 69 | fn gen_struct_or_variant( 70 | fields: Fields, 71 | variant_ident: Option, 72 | containing_type: Ident, 73 | container_attrs: Vec, 74 | ) -> Result { 75 | let children_parsed = if fields == Fields::Unit { 76 | let expr = { 77 | let dummy_field = Field { 78 | attrs: container_attrs, 79 | vis: Visibility::Inherited, 80 | mutability: FieldMutability::None, 81 | ident: None, 82 | colon_token: None, 83 | ty: Type::Verbatim(quote!(())), // unit type. 84 | }; 85 | 86 | gen_field("unit".to_string(), dummy_field) 87 | }; 88 | vec![ParamOrField::Param(expr)] 89 | } else { 90 | fields 91 | .iter() 92 | .enumerate() 93 | .map(|(i, field)| { 94 | let expr = if let Some(skip_attrs) = field 95 | .attrs 96 | .iter() 97 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::skip)) 98 | { 99 | skip_attrs.parse_args::()? 100 | } else { 101 | let ident_str = field 102 | .ident 103 | .as_ref() 104 | .map(|v| v.to_string()) 105 | .unwrap_or(format!("{i}")); 106 | 107 | gen_field(ident_str, field.clone()) 108 | }; 109 | 110 | let field = if let Some(field_name) = &field.ident { 111 | ParamOrField::Field(FieldValue { 112 | attrs: vec![], 113 | member: Member::Named(field_name.clone()), 114 | colon_token: Some(Token![:](Span::call_site())), 115 | expr, 116 | }) 117 | } else { 118 | ParamOrField::Param(expr) 119 | }; 120 | Ok(field) 121 | }) 122 | .sift::>()? 123 | }; 124 | 125 | let construct_name = match variant_ident { 126 | Some(ident) => quote! { 127 | #containing_type::#ident 128 | }, 129 | None => quote! { 130 | #containing_type 131 | }, 132 | }; 133 | 134 | let construct_expr = { 135 | match &fields { 136 | Fields::Unit => { 137 | let ParamOrField::Param(ref expr) = children_parsed[0] else { 138 | unreachable!() 139 | }; 140 | 141 | quote! { 142 | { 143 | #expr; 144 | #construct_name 145 | } 146 | } 147 | } 148 | Fields::Named(_) => quote! { 149 | #construct_name { 150 | #(#children_parsed),* 151 | } 152 | }, 153 | Fields::Unnamed(_) => quote! { 154 | #construct_name( 155 | #(#children_parsed),* 156 | ) 157 | }, 158 | } 159 | }; 160 | 161 | Ok( 162 | syn::parse_quote!(::rust_sitter::__private::extract_struct_or_variant(node, move |cursor, last_idx| #construct_expr)), 163 | ) 164 | } 165 | 166 | pub fn expand_grammar(input: ItemMod) -> Result { 167 | let grammar_name = input 168 | .attrs 169 | .iter() 170 | .find_map(|a| { 171 | if a.path() == &syn::parse_quote!(rust_sitter::grammar) { 172 | let grammar_name_expr = a.parse_args_with(Expr::parse).ok(); 173 | if let Some(Expr::Lit(ExprLit { 174 | attrs: _, 175 | lit: Lit::Str(s), 176 | })) = grammar_name_expr 177 | { 178 | Some(Ok(s.value())) 179 | } else { 180 | Some(Err(syn::Error::new( 181 | Span::call_site(), 182 | "Expected a string literal grammar name", 183 | ))) 184 | } 185 | } else { 186 | None 187 | } 188 | }) 189 | .transpose()? 190 | .ok_or_else(|| syn::Error::new(Span::call_site(), "Each grammar must have a name"))?; 191 | 192 | let (brace, new_contents) = input.content.ok_or_else(|| { 193 | syn::Error::new( 194 | Span::call_site(), 195 | "Expected the module to have inline contents (`mod my_module { .. }` syntax)", 196 | ) 197 | })?; 198 | 199 | let root_type = new_contents 200 | .iter() 201 | .find_map(|item| match item { 202 | Item::Enum(ItemEnum { ident, attrs, .. }) 203 | | Item::Struct(ItemStruct { ident, attrs, .. }) => { 204 | if attrs 205 | .iter() 206 | .any(|attr| attr.path() == &syn::parse_quote!(rust_sitter::language)) 207 | { 208 | Some(ident.clone()) 209 | } else { 210 | None 211 | } 212 | } 213 | _ => None, 214 | }) 215 | .ok_or_else(|| { 216 | syn::Error::new( 217 | Span::call_site(), 218 | "Each parser must have the root type annotated with `#[rust_sitter::language]`", 219 | ) 220 | })?; 221 | 222 | let mut transformed: Vec = new_contents 223 | .iter() 224 | .cloned() 225 | .map(|c| match c { 226 | Item::Enum(mut e) => { 227 | let match_cases: Vec = e.variants.iter().map(|v| { 228 | let variant_path = format!("{}_{}", e.ident, v.ident); 229 | 230 | let extract_expr = gen_struct_or_variant( 231 | v.fields.clone(), 232 | Some(v.ident.clone()), 233 | e.ident.clone(), 234 | v.attrs.clone(), 235 | )?; 236 | Ok(syn::parse_quote! { 237 | #variant_path => return #extract_expr 238 | }) 239 | }).sift::>()?; 240 | 241 | e.attrs.retain(|a| !is_sitter_attr(a)); 242 | e.variants.iter_mut().for_each(|v| { 243 | v.attrs.retain(|a| !is_sitter_attr(a)); 244 | v.fields.iter_mut().for_each(|f| { 245 | f.attrs.retain(|a| !is_sitter_attr(a)); 246 | }); 247 | }); 248 | 249 | let enum_name = &e.ident; 250 | let extract_impl: Item = syn::parse_quote! { 251 | impl ::rust_sitter::Extract<#enum_name> for #enum_name { 252 | type LeafFn = (); 253 | 254 | #[allow(non_snake_case)] 255 | fn extract(node: Option<::rust_sitter::tree_sitter::Node>, source: &[u8], _last_idx: usize, _leaf_fn: Option<&Self::LeafFn>) -> Self { 256 | let node = node.unwrap(); 257 | 258 | let mut cursor = node.walk(); 259 | assert!(cursor.goto_first_child(), "Could not find a child corresponding to any enum branch"); 260 | loop { 261 | let node = cursor.node(); 262 | match node.kind() { 263 | #(#match_cases),*, 264 | _ => if !cursor.goto_next_sibling() { 265 | panic!("Could not find a child corresponding to any enum branch") 266 | } 267 | } 268 | } 269 | } 270 | } 271 | }; 272 | Ok(vec![Item::Enum(e), extract_impl]) 273 | } 274 | 275 | Item::Struct(mut s) => { 276 | let struct_name = &s.ident; 277 | let extract_expr = gen_struct_or_variant( 278 | s.fields.clone(), 279 | None, 280 | s.ident.clone(), 281 | s.attrs.clone(), 282 | )?; 283 | 284 | s.attrs.retain(|a| !is_sitter_attr(a)); 285 | s.fields.iter_mut().for_each(|f| { 286 | f.attrs.retain(|a| !is_sitter_attr(a)); 287 | }); 288 | 289 | 290 | let extract_impl: Item = syn::parse_quote! { 291 | impl ::rust_sitter::Extract<#struct_name> for #struct_name { 292 | type LeafFn = (); 293 | 294 | #[allow(non_snake_case)] 295 | fn extract(node: Option<::rust_sitter::tree_sitter::Node>, source: &[u8], last_idx: usize, _leaf_fn: Option<&Self::LeafFn>) -> Self { 296 | let node = node.unwrap(); 297 | #extract_expr 298 | } 299 | } 300 | }; 301 | 302 | Ok(vec![Item::Struct(s), extract_impl]) 303 | } 304 | 305 | o => Ok(vec![o]), 306 | }) 307 | .sift::>()?.into_iter().flatten().collect(); 308 | 309 | let tree_sitter_ident = Ident::new(&format!("tree_sitter_{grammar_name}"), Span::call_site()); 310 | 311 | transformed.push(syn::parse_quote! { 312 | extern "C" { 313 | fn #tree_sitter_ident() -> ::rust_sitter::tree_sitter::Language; 314 | } 315 | }); 316 | 317 | transformed.push(syn::parse_quote! { 318 | pub fn language() -> ::rust_sitter::tree_sitter::Language { 319 | unsafe { #tree_sitter_ident() } 320 | } 321 | }); 322 | 323 | let root_type_docstr = format!("[`{root_type}`]"); 324 | transformed.push(syn::parse_quote! { 325 | /// Parse an input string according to the grammar. Returns either any parsing errors that happened, or a 326 | #[doc = #root_type_docstr] 327 | /// instance containing the parsed structured data. 328 | pub fn parse(input: &str) -> core::result::Result<#root_type, Vec<::rust_sitter::errors::ParseError>> { 329 | ::rust_sitter::__private::parse::<#root_type>(input, language) 330 | } 331 | }); 332 | 333 | let mut filtered_attrs = input.attrs; 334 | filtered_attrs.retain(|a| !is_sitter_attr(a)); 335 | Ok(ItemMod { 336 | attrs: filtered_attrs, 337 | vis: input.vis, 338 | unsafety: None, 339 | mod_token: input.mod_token, 340 | ident: input.ident, 341 | content: Some((brace, transformed)), 342 | semi: input.semi, 343 | }) 344 | } 345 | -------------------------------------------------------------------------------- /tool/src/expansion.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use rust_sitter_common::*; 4 | use serde_json::{json, Map, Value}; 5 | use syn::{parse::Parse, punctuated::Punctuated, *}; 6 | 7 | fn gen_field( 8 | path: String, 9 | leaf_type: Type, 10 | leaf_attrs: Vec, 11 | word_rule: &mut Option, 12 | out: &mut Map, 13 | ) -> (Value, bool) { 14 | let leaf_attr = leaf_attrs 15 | .iter() 16 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::leaf)); 17 | 18 | if leaf_attrs 19 | .iter() 20 | .any(|attr| attr.path() == &syn::parse_quote!(rust_sitter::word)) 21 | { 22 | if word_rule.is_some() { 23 | panic!("Multiple `word` rules specified"); 24 | } 25 | 26 | *word_rule = Some(path.clone()); 27 | } 28 | 29 | let leaf_params = leaf_attr.and_then(|a| { 30 | a.parse_args_with(Punctuated::::parse_terminated) 31 | .ok() 32 | }); 33 | 34 | let pattern_param = leaf_params.as_ref().and_then(|p| { 35 | p.iter() 36 | .find(|param| param.path == "pattern") 37 | .map(|p| p.expr.clone()) 38 | }); 39 | 40 | let text_param = leaf_params.as_ref().and_then(|p| { 41 | p.iter() 42 | .find(|param| param.path == "text") 43 | .map(|p| p.expr.clone()) 44 | }); 45 | 46 | let mut skip_over = HashSet::new(); 47 | skip_over.insert("Spanned"); 48 | skip_over.insert("Box"); 49 | 50 | let (inner_type_vec, is_vec) = try_extract_inner_type(&leaf_type, "Vec", &skip_over); 51 | let (inner_type_option, is_option) = try_extract_inner_type(&leaf_type, "Option", &skip_over); 52 | 53 | if !is_vec && !is_option { 54 | if let Some(Expr::Lit(lit)) = pattern_param { 55 | if let Lit::Str(s) = &lit.lit { 56 | out.insert( 57 | path.clone(), 58 | json!({ 59 | "type": "PATTERN", 60 | "value": s.value(), 61 | }), 62 | ); 63 | 64 | ( 65 | json!({ 66 | "type": "SYMBOL", 67 | "name": path 68 | }), 69 | is_option, 70 | ) 71 | } else { 72 | panic!("Expected string literal for pattern"); 73 | } 74 | } else if let Some(Expr::Lit(lit)) = text_param { 75 | if let Lit::Str(s) = &lit.lit { 76 | out.insert( 77 | path.clone(), 78 | json!({ 79 | "type": "STRING", 80 | "value": s.value(), 81 | }), 82 | ); 83 | 84 | ( 85 | json!({ 86 | "type": "SYMBOL", 87 | "name": path 88 | }), 89 | is_option, 90 | ) 91 | } else { 92 | panic!("Expected string literal for text"); 93 | } 94 | } else { 95 | let symbol_name = if let Type::Path(p) = filter_inner_type(&leaf_type, &skip_over) { 96 | if p.path.segments.len() == 1 { 97 | p.path.segments[0].ident.to_string() 98 | } else { 99 | panic!("Expected a single segment path"); 100 | } 101 | } else { 102 | panic!("Expected a path"); 103 | }; 104 | 105 | ( 106 | json!({ 107 | "type": "SYMBOL", 108 | "name": symbol_name, 109 | }), 110 | false, 111 | ) 112 | } 113 | } else if is_vec { 114 | let (field_json, field_optional) = gen_field( 115 | path.clone(), 116 | inner_type_vec, 117 | leaf_attr.iter().cloned().cloned().collect(), 118 | word_rule, 119 | out, 120 | ); 121 | 122 | let delimited_attr = leaf_attrs 123 | .iter() 124 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::delimited)); 125 | 126 | let delimited_params = 127 | delimited_attr.and_then(|a| a.parse_args_with(FieldThenParams::parse).ok()); 128 | 129 | let delimiter_json = delimited_params.map(|p| { 130 | gen_field( 131 | format!("{path}_vec_delimiter"), 132 | p.field.ty, 133 | p.field.attrs, 134 | word_rule, 135 | out, 136 | ) 137 | }); 138 | 139 | let repeat_attr = leaf_attrs 140 | .iter() 141 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::repeat)); 142 | 143 | let repeat_params = repeat_attr.and_then(|a| { 144 | a.parse_args_with(Punctuated::::parse_terminated) 145 | .ok() 146 | }); 147 | 148 | let repeat_non_empty = repeat_params 149 | .and_then(|p| { 150 | p.iter() 151 | .find(|param| param.path == "non_empty") 152 | .map(|p| p.expr.clone()) 153 | }) 154 | .map(|e| e == syn::parse_quote!(true)) 155 | .unwrap_or(false); 156 | 157 | let field_rule_non_optional = json!({ 158 | "type": "FIELD", 159 | "name": format!("{path}_vec_element"), 160 | "content": field_json 161 | }); 162 | 163 | let field_rule = if field_optional { 164 | json!({ 165 | "type": "CHOICE", 166 | "members": [ 167 | { 168 | "type": "BLANK" 169 | }, 170 | field_rule_non_optional 171 | ] 172 | }) 173 | } else { 174 | field_rule_non_optional 175 | }; 176 | 177 | let vec_contents = if let Some((delimiter_json, delimiter_optional)) = delimiter_json { 178 | let delim_made_optional = if delimiter_optional { 179 | json!({ 180 | "type": "CHOICE", 181 | "members": [ 182 | { 183 | "type": "BLANK" 184 | }, 185 | delimiter_json 186 | ] 187 | }) 188 | } else { 189 | delimiter_json 190 | }; 191 | 192 | json!({ 193 | "type": "SEQ", 194 | "members": [ 195 | field_rule, 196 | { 197 | "type": if field_optional { 198 | "REPEAT1" 199 | } else { 200 | "REPEAT" 201 | }, 202 | "content": { 203 | "type": "SEQ", 204 | "members": [ 205 | delim_made_optional, 206 | field_rule, 207 | ] 208 | } 209 | } 210 | ] 211 | }) 212 | } else { 213 | json!({ 214 | "type": "REPEAT1", 215 | "content": field_rule 216 | }) 217 | }; 218 | 219 | let contents_ident = format!("{path}_vec_contents"); 220 | out.insert(contents_ident.clone(), vec_contents); 221 | 222 | ( 223 | json!({ 224 | "type": "SYMBOL", 225 | "name": contents_ident, 226 | }), 227 | !repeat_non_empty, 228 | ) 229 | } else { 230 | // is_option 231 | let (field_json, field_optional) = 232 | gen_field(path, inner_type_option, leaf_attrs, word_rule, out); 233 | 234 | if field_optional { 235 | panic!("Option> is not supported"); 236 | } 237 | 238 | (field_json, true) 239 | } 240 | } 241 | 242 | fn gen_struct_or_variant( 243 | path: String, 244 | attrs: Vec, 245 | fields: Fields, 246 | out: &mut Map, 247 | word_rule: &mut Option, 248 | ) { 249 | fn gen_field_optional( 250 | path: &str, 251 | field: &Field, 252 | word_rule: &mut Option, 253 | out: &mut Map, 254 | ident_str: String, 255 | ) -> Value { 256 | let (field_contents, is_option) = gen_field( 257 | format!("{path}_{ident_str}"), 258 | field.ty.clone(), 259 | field.attrs.clone(), 260 | word_rule, 261 | out, 262 | ); 263 | 264 | let core = json!({ 265 | "type": "FIELD", 266 | "name": ident_str, 267 | "content": field_contents 268 | }); 269 | 270 | if is_option { 271 | json!({ 272 | "type": "CHOICE", 273 | "members": [ 274 | { 275 | "type": "BLANK" 276 | }, 277 | core 278 | ] 279 | }) 280 | } else { 281 | core 282 | } 283 | } 284 | 285 | let children = fields 286 | .iter() 287 | .enumerate() 288 | .filter_map(|(i, field)| { 289 | if field 290 | .attrs 291 | .iter() 292 | .any(|attr| attr.path() == &syn::parse_quote!(rust_sitter::skip)) 293 | { 294 | None 295 | } else { 296 | let ident_str = field 297 | .ident 298 | .as_ref() 299 | .map(|v| v.to_string()) 300 | .unwrap_or(format!("{i}")); 301 | 302 | Some(gen_field_optional(&path, field, word_rule, out, ident_str)) 303 | } 304 | }) 305 | .collect::>(); 306 | 307 | let prec_attr = attrs 308 | .iter() 309 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::prec)); 310 | 311 | let prec_param = prec_attr.and_then(|a| a.parse_args_with(Expr::parse).ok()); 312 | 313 | let prec_left_attr = attrs 314 | .iter() 315 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::prec_left)); 316 | 317 | let prec_left_param = prec_left_attr.and_then(|a| a.parse_args_with(Expr::parse).ok()); 318 | 319 | let prec_right_attr = attrs 320 | .iter() 321 | .find(|attr| attr.path() == &syn::parse_quote!(rust_sitter::prec_right)); 322 | 323 | let prec_right_param = prec_right_attr.and_then(|a| a.parse_args_with(Expr::parse).ok()); 324 | 325 | let base_rule = match fields { 326 | Fields::Unit => { 327 | let dummy_field = Field { 328 | attrs: attrs.clone(), 329 | vis: Visibility::Inherited, 330 | mutability: FieldMutability::None, 331 | ident: None, 332 | colon_token: None, 333 | ty: Type::Tuple(TypeTuple { 334 | paren_token: Default::default(), 335 | elems: Punctuated::new(), 336 | }), 337 | }; 338 | gen_field_optional(&path, &dummy_field, word_rule, out, "unit".to_owned()) 339 | } 340 | _ => json!({ 341 | "type": "SEQ", 342 | "members": children 343 | }), 344 | }; 345 | 346 | let rule = if let Some(Expr::Lit(lit)) = prec_param { 347 | if prec_left_attr.is_some() || prec_right_attr.is_some() { 348 | panic!("only one of prec, prec_left, and prec_right can be specified"); 349 | } 350 | 351 | if let Lit::Int(i) = &lit.lit { 352 | json!({ 353 | "type": "PREC", 354 | "value": i.base10_parse::().unwrap(), 355 | "content": base_rule 356 | }) 357 | } else { 358 | panic!("Expected integer literal for precedence"); 359 | } 360 | } else if let Some(Expr::Lit(lit)) = prec_left_param { 361 | if prec_right_attr.is_some() { 362 | panic!("only one of prec, prec_left, and prec_right can be specified"); 363 | } 364 | 365 | if let Lit::Int(i) = &lit.lit { 366 | json!({ 367 | "type": "PREC_LEFT", 368 | "value": i.base10_parse::().unwrap(), 369 | "content": base_rule 370 | }) 371 | } else { 372 | panic!("Expected integer literal for precedence"); 373 | } 374 | } else if let Some(Expr::Lit(lit)) = prec_right_param { 375 | if let Lit::Int(i) = &lit.lit { 376 | json!({ 377 | "type": "PREC_RIGHT", 378 | "value": i.base10_parse::().unwrap(), 379 | "content": base_rule 380 | }) 381 | } else { 382 | panic!("Expected integer literal for precedence"); 383 | } 384 | } else { 385 | base_rule 386 | }; 387 | 388 | out.insert(path, rule); 389 | } 390 | 391 | pub fn generate_grammar(module: &ItemMod) -> Value { 392 | let mut rules_map = Map::new(); 393 | // for some reason, source_file must be the first key for things to work 394 | rules_map.insert("source_file".to_string(), json!({})); 395 | 396 | let mut extras_list = vec![]; 397 | 398 | let grammar_name = module 399 | .attrs 400 | .iter() 401 | .find_map(|a| { 402 | if a.path() == &syn::parse_quote!(rust_sitter::grammar) { 403 | let grammar_name_expr = a.parse_args_with(Expr::parse).ok(); 404 | if let Some(Expr::Lit(ExprLit { 405 | attrs: _, 406 | lit: Lit::Str(s), 407 | })) = grammar_name_expr 408 | { 409 | Some(s.value()) 410 | } else { 411 | panic!("Expected string literal for grammar name"); 412 | } 413 | } else { 414 | None 415 | } 416 | }) 417 | .expect("Each grammar must have a name"); 418 | 419 | let (_, contents) = module.content.as_ref().unwrap(); 420 | 421 | let root_type = contents 422 | .iter() 423 | .find_map(|item| match item { 424 | Item::Enum(ItemEnum { ident, attrs, .. }) 425 | | Item::Struct(ItemStruct { ident, attrs, .. }) => { 426 | if attrs 427 | .iter() 428 | .any(|attr| attr.path() == &syn::parse_quote!(rust_sitter::language)) 429 | { 430 | Some(ident.clone()) 431 | } else { 432 | None 433 | } 434 | } 435 | _ => None, 436 | }) 437 | .expect("Each parser must have the root type annotated with `#[rust_sitter::language]`") 438 | .to_string(); 439 | 440 | // Optionally locate the rule annotated with `#[rust_sitter::word]`. 441 | let mut word_rule = None; 442 | contents.iter().for_each(|c| { 443 | let (symbol, attrs) = match c { 444 | Item::Enum(e) => { 445 | e.variants.iter().for_each(|v| { 446 | gen_struct_or_variant( 447 | format!("{}_{}", e.ident, v.ident), 448 | v.attrs.clone(), 449 | v.fields.clone(), 450 | &mut rules_map, 451 | &mut word_rule, 452 | ) 453 | }); 454 | 455 | let mut members: Vec = vec![]; 456 | e.variants.iter().for_each(|v| { 457 | let variant_path = format!("{}_{}", e.ident.clone(), v.ident); 458 | members.push(json!({ 459 | "type": "SYMBOL", 460 | "name": variant_path 461 | })) 462 | }); 463 | 464 | let rule = json!({ 465 | "type": "CHOICE", 466 | "members": members 467 | }); 468 | 469 | rules_map.insert(e.ident.to_string(), rule); 470 | 471 | (e.ident.to_string(), e.attrs.clone()) 472 | } 473 | 474 | Item::Struct(s) => { 475 | gen_struct_or_variant( 476 | s.ident.to_string(), 477 | s.attrs.clone(), 478 | s.fields.clone(), 479 | &mut rules_map, 480 | &mut word_rule, 481 | ); 482 | 483 | (s.ident.to_string(), s.attrs.clone()) 484 | } 485 | 486 | _ => return, 487 | }; 488 | 489 | if attrs 490 | .iter() 491 | .any(|a| a.path() == &syn::parse_quote!(rust_sitter::extra)) 492 | { 493 | extras_list.push(json!({ 494 | "type": "SYMBOL", 495 | "name": symbol 496 | })); 497 | } 498 | }); 499 | 500 | rules_map.insert( 501 | "source_file".to_string(), 502 | rules_map.get(&root_type).unwrap().clone(), 503 | ); 504 | 505 | json!({ 506 | "name": grammar_name, 507 | "word": word_rule, 508 | "rules": rules_map, 509 | "extras": extras_list 510 | }) 511 | } 512 | -------------------------------------------------------------------------------- /macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::ToTokens; 2 | use syn::{parse_macro_input, ItemMod}; 3 | 4 | mod errors; 5 | mod expansion; 6 | use expansion::*; 7 | 8 | #[proc_macro_attribute] 9 | /// Marks the top level AST node where parsing should start. 10 | /// 11 | /// ## Example 12 | /// ```ignore 13 | /// #[rust_sitter::language] 14 | /// pub struct Code { 15 | /// ... 16 | /// } 17 | /// ``` 18 | pub fn language( 19 | _attr: proc_macro::TokenStream, 20 | item: proc_macro::TokenStream, 21 | ) -> proc_macro::TokenStream { 22 | item 23 | } 24 | 25 | #[proc_macro_attribute] 26 | /// This annotation marks a node as extra, which can safely be skipped while parsing. 27 | /// This is useful for handling whitespace/newlines/comments. 28 | /// 29 | /// ## Example 30 | /// ```ignore 31 | /// #[rust_sitter::extra] 32 | /// struct Whitespace { 33 | /// #[rust_sitter::leaf(pattern = r"\s")] 34 | /// _whitespace: (), 35 | /// } 36 | /// ``` 37 | pub fn extra( 38 | _attr: proc_macro::TokenStream, 39 | item: proc_macro::TokenStream, 40 | ) -> proc_macro::TokenStream { 41 | item 42 | } 43 | 44 | #[proc_macro_attribute] 45 | /// Defines a field which matches a specific token in the source string. 46 | /// The token can be defined by passing one of two arguments 47 | /// - `text`: a string literal that will be exactly matched 48 | /// - `pattern`: a regular expression that will be matched against the source string 49 | /// 50 | /// If the resulting token needs to be converted into a richer type at runtime, 51 | /// such as a number, then the `transform` argument can be used to specify a function 52 | /// that will be called with the token's text. 53 | /// 54 | /// The attribute can also be applied to a struct or enum variant with no fields. 55 | /// 56 | /// ## Examples 57 | /// 58 | /// Using the `leaf` attribute on a field: 59 | /// ```ignore 60 | /// Number( 61 | /// #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 62 | /// u32 63 | /// ) 64 | /// ``` 65 | /// 66 | /// Using the attribute on a unit struct or unit enum variant: 67 | /// ```ignore 68 | /// #[rust_sitter::leaf(text = "9")] 69 | /// struct BigDigit; 70 | /// 71 | /// enum SmallDigit { 72 | /// #[rust_sitter::leaf(text = "0")] 73 | /// Zero, 74 | /// #[rust_sitter::leaf(text = "1")] 75 | /// One, 76 | /// } 77 | /// ``` 78 | /// 79 | pub fn leaf( 80 | _attr: proc_macro::TokenStream, 81 | item: proc_macro::TokenStream, 82 | ) -> proc_macro::TokenStream { 83 | item 84 | } 85 | 86 | #[proc_macro_attribute] 87 | /// Defines a field that does not correspond to anything in the input string, 88 | /// such as some metadata. Takes a single, unnamed argument, which is the value 89 | /// used to populate the field at runtime. 90 | /// 91 | /// ## Example 92 | /// ```ignore 93 | /// struct MyNode { 94 | /// ..., 95 | /// #[rust_sitter::skip(false)] 96 | /// node_visited: bool 97 | /// } 98 | /// ``` 99 | pub fn skip( 100 | _attr: proc_macro::TokenStream, 101 | item: proc_macro::TokenStream, 102 | ) -> proc_macro::TokenStream { 103 | item 104 | } 105 | 106 | #[proc_macro_attribute] 107 | /// Defines a precedence level for a non-terminal that has no associativity. 108 | /// 109 | /// This annotation takes a single, unnamed parameter, which specifies the precedence level. 110 | /// This is used to resolve conflicts with other non-terminals, so that the one with the higher 111 | /// precedence will bind more tightly (appear lower in the parse tree). 112 | /// 113 | /// ## Example 114 | /// ```ignore 115 | /// #[rust_sitter::prec(1)] 116 | /// PriorityExpr(Box, Box) 117 | /// ``` 118 | pub fn prec( 119 | _attr: proc_macro::TokenStream, 120 | item: proc_macro::TokenStream, 121 | ) -> proc_macro::TokenStream { 122 | item 123 | } 124 | 125 | #[proc_macro_attribute] 126 | /// Defines a precedence level for a non-terminal that should be left-associative. 127 | /// For example, with subtraction we expect 1 - 2 - 3 to be parsed as (1 - 2) - 3, 128 | /// which corresponds to a left-associativity. 129 | /// 130 | /// This annotation takes a single, unnamed parameter, which specifies the precedence level. 131 | /// This is used to resolve conflicts with other non-terminals, so that the one with the higher 132 | /// precedence will bind more tightly (appear lower in the parse tree). 133 | /// 134 | /// ## Example 135 | /// ```ignore 136 | /// #[rust_sitter::prec_left(1)] 137 | /// Subtract(Box, Box) 138 | /// ``` 139 | pub fn prec_left( 140 | _attr: proc_macro::TokenStream, 141 | item: proc_macro::TokenStream, 142 | ) -> proc_macro::TokenStream { 143 | item 144 | } 145 | 146 | #[proc_macro_attribute] 147 | /// Defines a precedence level for a non-terminal that should be right-associative. 148 | /// For example, with cons we could have 1 :: 2 :: 3 to be parsed as 1 :: (2 :: 3), 149 | /// which corresponds to a right-associativity. 150 | /// 151 | /// This annotation takes a single, unnamed parameter, which specifies the precedence level. 152 | /// This is used to resolve conflicts with other non-terminals, so that the one with the higher 153 | /// precedence will bind more tightly (appear lower in the parse tree). 154 | /// 155 | /// ## Example 156 | /// ```ignore 157 | /// #[rust_sitter::prec_right(1)] 158 | /// Cons(Box, Box) 159 | /// ``` 160 | pub fn prec_right( 161 | _attr: proc_macro::TokenStream, 162 | item: proc_macro::TokenStream, 163 | ) -> proc_macro::TokenStream { 164 | item 165 | } 166 | 167 | #[proc_macro_attribute] 168 | /// On `Vec<_>` typed fields, specifies a non-terminal that should be parsed in between the elements. 169 | /// The [`rust_sitter::repeat`] annotation must be used on the field as well. 170 | /// 171 | /// This annotation takes a single, unnamed argument, which specifies a field type to parse. This can 172 | /// either be a reference to another type, or can be defined as a `leaf` field. Generally, the argument 173 | /// is parsed using the same rules as an unnamed field of an enum variant. 174 | /// 175 | /// ## Example 176 | /// ```ignore 177 | /// #[rust_sitter::delimited( 178 | /// #[rust_sitter::leaf(text = ",")] 179 | /// () 180 | /// )] 181 | /// numbers: Vec 182 | /// ``` 183 | pub fn delimited( 184 | _attr: proc_macro::TokenStream, 185 | item: proc_macro::TokenStream, 186 | ) -> proc_macro::TokenStream { 187 | item 188 | } 189 | 190 | #[proc_macro_attribute] 191 | /// On `Vec<_>` typed fields, specifies additional config for how the repeated elements should 192 | /// be parsed. In particular, this annotation takes the following named arguments: 193 | /// - `non_empty` - if this argument is `true`, then there must be at least one element parsed 194 | /// 195 | /// ## Example 196 | /// ```ignore 197 | /// #[rust_sitter::repeat(non_empty = true)] 198 | /// numbers: Vec 199 | /// ``` 200 | pub fn repeat( 201 | _attr: proc_macro::TokenStream, 202 | item: proc_macro::TokenStream, 203 | ) -> proc_macro::TokenStream { 204 | item 205 | } 206 | 207 | /// Mark a module to be analyzed for a Rust Sitter grammar. Takes a single, unnamed argument, which 208 | /// specifies the name of the grammar. This name must be unique across all Rust Sitter grammars within 209 | /// a compilation unit. 210 | #[proc_macro_attribute] 211 | pub fn grammar( 212 | attr: proc_macro::TokenStream, 213 | input: proc_macro::TokenStream, 214 | ) -> proc_macro::TokenStream { 215 | let attr_tokens: proc_macro2::TokenStream = attr.into(); 216 | let module: ItemMod = parse_macro_input!(input); 217 | let expanded = expand_grammar(syn::parse_quote! { 218 | #[rust_sitter::grammar[#attr_tokens]] 219 | #module 220 | }) 221 | .map(ToTokens::into_token_stream) 222 | .unwrap_or_else(syn::Error::into_compile_error); 223 | proc_macro::TokenStream::from(expanded) 224 | } 225 | 226 | #[cfg(test)] 227 | mod tests { 228 | use std::fs::File; 229 | use std::io::{Read, Write}; 230 | use std::process::Command; 231 | 232 | use quote::ToTokens; 233 | use syn::{parse_quote, Result}; 234 | use tempfile::tempdir; 235 | 236 | use super::expand_grammar; 237 | 238 | fn rustfmt_code(code: &str) -> String { 239 | let dir = tempdir().unwrap(); 240 | let file_path = dir.path().join("temp.rs"); 241 | let mut file = File::create(file_path.clone()).unwrap(); 242 | 243 | writeln!(file, "{code}").unwrap(); 244 | drop(file); 245 | 246 | Command::new("rustfmt") 247 | .arg(file_path.to_str().unwrap()) 248 | .spawn() 249 | .unwrap() 250 | .wait() 251 | .unwrap(); 252 | 253 | let mut file = File::open(file_path).unwrap(); 254 | let mut data = String::new(); 255 | file.read_to_string(&mut data).unwrap(); 256 | drop(file); 257 | dir.close().unwrap(); 258 | data 259 | } 260 | 261 | #[test] 262 | fn enum_transformed_fields() -> Result<()> { 263 | insta::assert_snapshot!(rustfmt_code( 264 | &expand_grammar(parse_quote! { 265 | #[rust_sitter::grammar("test")] 266 | mod grammar { 267 | #[rust_sitter::language] 268 | pub enum Expression { 269 | Number( 270 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse::().unwrap())] 271 | i32 272 | ), 273 | } 274 | } 275 | })? 276 | .to_token_stream() 277 | .to_string() 278 | )); 279 | 280 | Ok(()) 281 | } 282 | 283 | #[test] 284 | fn enum_recursive() -> Result<()> { 285 | insta::assert_snapshot!(rustfmt_code( 286 | &expand_grammar(parse_quote! { 287 | #[rust_sitter::grammar("test")] 288 | mod grammar { 289 | #[rust_sitter::language] 290 | pub enum Expression { 291 | Number( 292 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 293 | i32 294 | ), 295 | Neg( 296 | #[rust_sitter::leaf(text = "-")] 297 | (), 298 | Box 299 | ), 300 | } 301 | } 302 | })? 303 | .to_token_stream() 304 | .to_string() 305 | )); 306 | 307 | Ok(()) 308 | } 309 | 310 | #[test] 311 | fn enum_prec_left() -> Result<()> { 312 | insta::assert_snapshot!(rustfmt_code( 313 | &expand_grammar(parse_quote! { 314 | #[rust_sitter::grammar("test")] 315 | mod grammar { 316 | #[rust_sitter::language] 317 | pub enum Expression { 318 | Number( 319 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 320 | i32 321 | ), 322 | #[rust_sitter::prec_left(1)] 323 | Sub( 324 | Box, 325 | #[rust_sitter::leaf(text = "-")] 326 | (), 327 | Box 328 | ), 329 | } 330 | } 331 | })? 332 | .to_token_stream() 333 | .to_string() 334 | )); 335 | 336 | Ok(()) 337 | } 338 | 339 | #[test] 340 | fn struct_extra() -> Result<()> { 341 | insta::assert_snapshot!(rustfmt_code( 342 | &expand_grammar(parse_quote! { 343 | #[rust_sitter::grammar("test")] 344 | mod grammar { 345 | #[rust_sitter::language] 346 | pub enum Expression { 347 | Number( 348 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] i32, 349 | ), 350 | } 351 | 352 | #[rust_sitter::extra] 353 | struct Whitespace { 354 | #[rust_sitter::leaf(pattern = r"\s")] 355 | _whitespace: (), 356 | } 357 | } 358 | })? 359 | .to_token_stream() 360 | .to_string() 361 | )); 362 | 363 | Ok(()) 364 | } 365 | 366 | #[test] 367 | fn grammar_unboxed_field() -> Result<()> { 368 | insta::assert_snapshot!(rustfmt_code( 369 | &expand_grammar(parse_quote! { 370 | #[rust_sitter::grammar("test")] 371 | mod grammar { 372 | #[rust_sitter::language] 373 | pub struct Language { 374 | e: Expression, 375 | } 376 | 377 | pub enum Expression { 378 | Number( 379 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 380 | i32 381 | ), 382 | } 383 | } 384 | })? 385 | .to_token_stream() 386 | .to_string() 387 | )); 388 | 389 | Ok(()) 390 | } 391 | 392 | #[test] 393 | fn struct_repeat() -> Result<()> { 394 | insta::assert_snapshot!(rustfmt_code( 395 | &expand_grammar(parse_quote! { 396 | #[rust_sitter::grammar("test")] 397 | mod grammar { 398 | #[rust_sitter::language] 399 | pub struct NumberList { 400 | numbers: Vec, 401 | } 402 | 403 | pub struct Number { 404 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 405 | v: i32 406 | } 407 | 408 | #[rust_sitter::extra] 409 | struct Whitespace { 410 | #[rust_sitter::leaf(pattern = r"\s")] 411 | _whitespace: (), 412 | } 413 | } 414 | })? 415 | .to_token_stream() 416 | .to_string() 417 | )); 418 | 419 | Ok(()) 420 | } 421 | 422 | #[test] 423 | fn struct_optional() -> Result<()> { 424 | insta::assert_snapshot!(rustfmt_code( 425 | &expand_grammar(parse_quote! { 426 | #[rust_sitter::grammar("test")] 427 | mod grammar { 428 | #[rust_sitter::language] 429 | pub struct Language { 430 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 431 | v: Option, 432 | t: Option, 433 | } 434 | 435 | pub struct Number { 436 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 437 | v: i32 438 | } 439 | } 440 | })? 441 | .to_token_stream() 442 | .to_string() 443 | )); 444 | 445 | Ok(()) 446 | } 447 | 448 | #[test] 449 | fn enum_with_unamed_vector() -> Result<()> { 450 | insta::assert_snapshot!(rustfmt_code( 451 | &expand_grammar(parse_quote! { 452 | #[rust_sitter::grammar("test")] 453 | mod grammar { 454 | pub struct Number { 455 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 456 | value: u32 457 | } 458 | 459 | #[rust_sitter::language] 460 | pub enum Expr { 461 | Numbers( 462 | #[rust_sitter::repeat(non_empty = true)] 463 | Vec 464 | ) 465 | } 466 | } 467 | })? 468 | .to_token_stream() 469 | .to_string() 470 | )); 471 | 472 | Ok(()) 473 | } 474 | 475 | #[test] 476 | fn enum_with_named_field() -> Result<()> { 477 | insta::assert_snapshot!(rustfmt_code( 478 | &expand_grammar(parse_quote! { 479 | #[rust_sitter::grammar("test")] 480 | mod grammar { 481 | #[rust_sitter::language] 482 | pub enum Expr { 483 | Number( 484 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 485 | u32 486 | ), 487 | Neg { 488 | #[rust_sitter::leaf(text = "!")] 489 | _bang: (), 490 | value: Box, 491 | } 492 | } 493 | } 494 | })? 495 | .to_token_stream() 496 | .to_string() 497 | )); 498 | 499 | Ok(()) 500 | } 501 | 502 | #[test] 503 | fn spanned_in_vec() -> Result<()> { 504 | insta::assert_snapshot!(rustfmt_code( 505 | &expand_grammar(parse_quote! { 506 | #[rust_sitter::grammar("test")] 507 | mod grammar { 508 | use rust_sitter::Spanned; 509 | 510 | #[rust_sitter::language] 511 | pub struct NumberList { 512 | numbers: Vec>, 513 | } 514 | 515 | pub struct Number { 516 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 517 | v: i32 518 | } 519 | 520 | #[rust_sitter::extra] 521 | struct Whitespace { 522 | #[rust_sitter::leaf(pattern = r"\s")] 523 | _whitespace: (), 524 | } 525 | } 526 | })? 527 | .to_token_stream() 528 | .to_string() 529 | )); 530 | 531 | Ok(()) 532 | } 533 | } 534 | -------------------------------------------------------------------------------- /tool/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | use syn::{parse_quote, Item}; 3 | 4 | mod expansion; 5 | use expansion::*; 6 | 7 | const GENERATED_SEMANTIC_VERSION: Option<(u8, u8, u8)> = Some((0, 25, 2)); 8 | 9 | /// Generates JSON strings defining Tree Sitter grammars for every Rust Sitter 10 | /// grammar found in the given module and recursive submodules. 11 | pub fn generate_grammars(root_file: &Path) -> Vec { 12 | let root_file = syn_inline_mod::parse_and_inline_modules(root_file).items; 13 | let mut out = vec![]; 14 | root_file 15 | .iter() 16 | .for_each(|i| generate_all_grammars(i, &mut out)); 17 | out 18 | } 19 | 20 | fn generate_all_grammars(item: &Item, out: &mut Vec) { 21 | if let Item::Mod(m) = item { 22 | m.content 23 | .iter() 24 | .for_each(|(_, items)| items.iter().for_each(|i| generate_all_grammars(i, out))); 25 | 26 | if m.attrs 27 | .iter() 28 | .any(|a| a.path() == &parse_quote!(rust_sitter::grammar)) 29 | { 30 | out.push(generate_grammar(m)) 31 | } 32 | } 33 | } 34 | 35 | #[cfg(feature = "build_parsers")] 36 | use std::io::Write; 37 | use std::path::Path; 38 | 39 | #[cfg(feature = "build_parsers")] 40 | use tree_sitter_generate::generate_parser_for_grammar; 41 | 42 | #[cfg(feature = "build_parsers")] 43 | /// Using the `cc` crate, generates and compiles a C parser with Tree Sitter 44 | /// for every Rust Sitter grammar found in the given module and recursive 45 | /// submodules. 46 | pub fn build_parsers(root_file: &Path) { 47 | use std::env; 48 | let out_dir = env::var("OUT_DIR").unwrap(); 49 | let emit_artifacts: bool = env::var("RUST_SITTER_EMIT_ARTIFACTS") 50 | .map(|s| s.parse().unwrap_or(false)) 51 | .unwrap_or(false); 52 | generate_grammars(root_file).iter().for_each(|grammar| { 53 | let (grammar_name, grammar_c) = 54 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 55 | let tempfile = tempfile::Builder::new() 56 | .prefix("grammar") 57 | .tempdir() 58 | .unwrap(); 59 | 60 | let dir = if emit_artifacts { 61 | let grammar_dir = Path::new(out_dir.as_str()).join(format!("grammar_{grammar_name}",)); 62 | if grammar_dir.is_dir() { 63 | std::fs::remove_dir_all(&grammar_dir).expect("Couldn't clear old artifacts"); 64 | } 65 | std::fs::DirBuilder::new() 66 | .recursive(true) 67 | .create(grammar_dir.clone()) 68 | .expect("Couldn't create grammar JSON directory"); 69 | grammar_dir 70 | } else { 71 | tempfile.path().into() 72 | }; 73 | 74 | let grammar_file = dir.join("parser.c"); 75 | let mut f = std::fs::File::create(grammar_file).unwrap(); 76 | 77 | f.write_all(grammar_c.as_bytes()).unwrap(); 78 | drop(f); 79 | 80 | // emit grammar into the build out_dir 81 | let mut grammar_json_file = 82 | std::fs::File::create(dir.join(format!("{grammar_name}.json"))).unwrap(); 83 | grammar_json_file 84 | .write_all(serde_json::to_string_pretty(grammar).unwrap().as_bytes()) 85 | .unwrap(); 86 | drop(grammar_json_file); 87 | 88 | let header_dir = dir.join("tree_sitter"); 89 | std::fs::create_dir(&header_dir).unwrap(); 90 | let mut parser_file = std::fs::File::create(header_dir.join("parser.h")).unwrap(); 91 | parser_file 92 | .write_all(tree_sitter::PARSER_HEADER.as_bytes()) 93 | .unwrap(); 94 | drop(parser_file); 95 | 96 | let sysroot_dir = dir.join("sysroot"); 97 | if env::var("TARGET").unwrap().starts_with("wasm32") { 98 | std::fs::create_dir(&sysroot_dir).unwrap(); 99 | let mut stdint = std::fs::File::create(sysroot_dir.join("stdint.h")).unwrap(); 100 | stdint 101 | .write_all(include_bytes!("wasm-sysroot/stdint.h")) 102 | .unwrap(); 103 | drop(stdint); 104 | 105 | let mut stdlib = std::fs::File::create(sysroot_dir.join("stdlib.h")).unwrap(); 106 | stdlib 107 | .write_all(include_bytes!("wasm-sysroot/stdlib.h")) 108 | .unwrap(); 109 | drop(stdlib); 110 | 111 | let mut stdio = std::fs::File::create(sysroot_dir.join("stdio.h")).unwrap(); 112 | stdio 113 | .write_all(include_bytes!("wasm-sysroot/stdio.h")) 114 | .unwrap(); 115 | drop(stdio); 116 | 117 | let mut stdbool = std::fs::File::create(sysroot_dir.join("stdbool.h")).unwrap(); 118 | stdbool 119 | .write_all(include_bytes!("wasm-sysroot/stdbool.h")) 120 | .unwrap(); 121 | drop(stdbool); 122 | } 123 | 124 | let mut c_config = cc::Build::new(); 125 | c_config.std("c11").include(&dir).include(&sysroot_dir); 126 | c_config 127 | .flag_if_supported("-Wno-unused-label") 128 | .flag_if_supported("-Wno-unused-parameter") 129 | .flag_if_supported("-Wno-unused-but-set-variable") 130 | .flag_if_supported("-Wno-trigraphs") 131 | .flag_if_supported("-Wno-everything"); 132 | c_config.file(dir.join("parser.c")); 133 | 134 | c_config.compile(&grammar_name); 135 | }); 136 | } 137 | 138 | #[cfg(test)] 139 | mod tests { 140 | use syn::parse_quote; 141 | 142 | use super::{generate_grammar, GENERATED_SEMANTIC_VERSION}; 143 | use tree_sitter_generate::generate_parser_for_grammar; 144 | 145 | #[test] 146 | fn enum_with_named_field() { 147 | let m = if let syn::Item::Mod(m) = parse_quote! { 148 | #[rust_sitter::grammar("test")] 149 | mod grammar { 150 | #[rust_sitter::language] 151 | pub enum Expr { 152 | Number( 153 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 154 | u32 155 | ), 156 | Neg { 157 | #[rust_sitter::leaf(text = "!")] 158 | _bang: (), 159 | value: Box, 160 | } 161 | } 162 | } 163 | } { 164 | m 165 | } else { 166 | panic!() 167 | }; 168 | 169 | let grammar = generate_grammar(&m); 170 | insta::assert_snapshot!(grammar); 171 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 172 | } 173 | 174 | #[test] 175 | fn enum_transformed_fields() { 176 | let m = if let syn::Item::Mod(m) = parse_quote! { 177 | #[rust_sitter::grammar("test")] 178 | mod grammar { 179 | #[rust_sitter::language] 180 | pub enum Expression { 181 | Number( 182 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 183 | i32 184 | ), 185 | } 186 | } 187 | } { 188 | m 189 | } else { 190 | panic!() 191 | }; 192 | 193 | let grammar = generate_grammar(&m); 194 | insta::assert_snapshot!(grammar); 195 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 196 | } 197 | 198 | #[test] 199 | fn enum_recursive() { 200 | let m = if let syn::Item::Mod(m) = parse_quote! { 201 | #[rust_sitter::grammar("test")] 202 | mod grammar { 203 | #[rust_sitter::language] 204 | pub enum Expression { 205 | Number( 206 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 207 | i32 208 | ), 209 | Neg( 210 | #[rust_sitter::leaf(text = "-", transform = |v| ())] 211 | (), 212 | Box 213 | ), 214 | } 215 | } 216 | } { 217 | m 218 | } else { 219 | panic!() 220 | }; 221 | 222 | let grammar = generate_grammar(&m); 223 | insta::assert_snapshot!(grammar); 224 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 225 | } 226 | 227 | #[test] 228 | fn enum_prec_left() { 229 | let m = if let syn::Item::Mod(m) = parse_quote! { 230 | #[rust_sitter::grammar("test")] 231 | mod grammar { 232 | #[rust_sitter::language] 233 | pub enum Expression { 234 | Number( 235 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 236 | i32 237 | ), 238 | #[rust_sitter::prec_left(1)] 239 | Sub( 240 | Box, 241 | #[rust_sitter::leaf(text = "-", transform = |v| ())] 242 | (), 243 | Box 244 | ), 245 | } 246 | } 247 | } { 248 | m 249 | } else { 250 | panic!() 251 | }; 252 | 253 | let grammar = generate_grammar(&m); 254 | insta::assert_snapshot!(grammar); 255 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 256 | } 257 | 258 | #[test] 259 | fn grammar_with_extras() { 260 | let m = if let syn::Item::Mod(m) = parse_quote! { 261 | #[rust_sitter::grammar("test")] 262 | mod grammar { 263 | #[rust_sitter::language] 264 | pub enum Expression { 265 | Number( 266 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 267 | i32 268 | ), 269 | } 270 | 271 | #[rust_sitter::extra] 272 | struct Whitespace { 273 | #[rust_sitter::leaf(pattern = r"\s", transform = |_v| ())] 274 | _whitespace: (), 275 | } 276 | } 277 | } { 278 | m 279 | } else { 280 | panic!() 281 | }; 282 | 283 | let grammar = generate_grammar(&m); 284 | insta::assert_snapshot!(grammar); 285 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 286 | } 287 | 288 | #[test] 289 | fn grammar_unboxed_field() { 290 | let m = if let syn::Item::Mod(m) = parse_quote! { 291 | #[rust_sitter::grammar("test")] 292 | mod grammar { 293 | #[rust_sitter::language] 294 | pub struct Language { 295 | e: Expression, 296 | } 297 | 298 | pub enum Expression { 299 | Number( 300 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v: &str| v.parse::().unwrap())] 301 | i32 302 | ), 303 | } 304 | } 305 | } { 306 | m 307 | } else { 308 | panic!() 309 | }; 310 | 311 | let grammar = generate_grammar(&m); 312 | insta::assert_snapshot!(grammar); 313 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 314 | } 315 | 316 | #[test] 317 | fn grammar_repeat() { 318 | let m = if let syn::Item::Mod(m) = parse_quote! { 319 | #[rust_sitter::grammar("test")] 320 | pub mod grammar { 321 | #[rust_sitter::language] 322 | pub struct NumberList { 323 | #[rust_sitter::delimited( 324 | #[rust_sitter::leaf(text = ",")] 325 | () 326 | )] 327 | numbers: Vec, 328 | } 329 | 330 | pub struct Number { 331 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 332 | v: i32, 333 | } 334 | 335 | #[rust_sitter::extra] 336 | struct Whitespace { 337 | #[rust_sitter::leaf(pattern = r"\s")] 338 | _whitespace: (), 339 | } 340 | } 341 | } { 342 | m 343 | } else { 344 | panic!() 345 | }; 346 | 347 | let grammar = generate_grammar(&m); 348 | insta::assert_snapshot!(grammar); 349 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 350 | } 351 | 352 | #[test] 353 | fn grammar_repeat_no_delimiter() { 354 | let m = if let syn::Item::Mod(m) = parse_quote! { 355 | #[rust_sitter::grammar("test")] 356 | pub mod grammar { 357 | #[rust_sitter::language] 358 | pub struct NumberList { 359 | numbers: Vec, 360 | } 361 | 362 | pub struct Number { 363 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 364 | v: i32, 365 | } 366 | 367 | #[rust_sitter::extra] 368 | struct Whitespace { 369 | #[rust_sitter::leaf(pattern = r"\s")] 370 | _whitespace: (), 371 | } 372 | } 373 | } { 374 | m 375 | } else { 376 | panic!() 377 | }; 378 | 379 | let grammar = generate_grammar(&m); 380 | insta::assert_snapshot!(grammar); 381 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 382 | } 383 | 384 | #[test] 385 | fn grammar_repeat1() { 386 | let m = if let syn::Item::Mod(m) = parse_quote! { 387 | #[rust_sitter::grammar("test")] 388 | pub mod grammar { 389 | #[rust_sitter::language] 390 | pub struct NumberList { 391 | #[rust_sitter::repeat(non_empty = true)] 392 | #[rust_sitter::delimited( 393 | #[rust_sitter::leaf(text = ",")] 394 | () 395 | )] 396 | numbers: Vec, 397 | } 398 | 399 | pub struct Number { 400 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 401 | v: i32, 402 | } 403 | 404 | #[rust_sitter::extra] 405 | struct Whitespace { 406 | #[rust_sitter::leaf(pattern = r"\s")] 407 | _whitespace: (), 408 | } 409 | } 410 | } { 411 | m 412 | } else { 413 | panic!() 414 | }; 415 | 416 | let grammar = generate_grammar(&m); 417 | insta::assert_snapshot!(grammar); 418 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 419 | } 420 | 421 | #[test] 422 | fn struct_optional() { 423 | let m = if let syn::Item::Mod(m) = parse_quote! { 424 | #[rust_sitter::grammar("test")] 425 | mod grammar { 426 | #[rust_sitter::language] 427 | pub struct Language { 428 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 429 | v: Option, 430 | #[rust_sitter::leaf(pattern = r" ", transform = |v| ())] 431 | space: (), 432 | t: Option, 433 | } 434 | 435 | pub struct Number { 436 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 437 | v: i32 438 | } 439 | } 440 | } { 441 | m 442 | } else { 443 | panic!() 444 | }; 445 | 446 | let grammar = generate_grammar(&m); 447 | insta::assert_snapshot!(grammar); 448 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 449 | } 450 | 451 | #[test] 452 | fn enum_with_unamed_vector() { 453 | let m = if let syn::Item::Mod(m) = parse_quote! { 454 | #[rust_sitter::grammar("test")] 455 | mod grammar { 456 | pub struct Number { 457 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 458 | value: u32 459 | } 460 | 461 | #[rust_sitter::language] 462 | pub enum Expr { 463 | Numbers( 464 | #[rust_sitter::repeat(non_empty = true)] 465 | Vec 466 | ) 467 | } 468 | } 469 | } { 470 | m 471 | } else { 472 | panic!() 473 | }; 474 | 475 | let grammar = generate_grammar(&m); 476 | insta::assert_snapshot!(grammar); 477 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 478 | } 479 | 480 | #[test] 481 | fn spanned_in_vec() { 482 | let m = if let syn::Item::Mod(m) = parse_quote! { 483 | #[rust_sitter::grammar("test")] 484 | mod grammar { 485 | use rust_sitter::Spanned; 486 | 487 | #[rust_sitter::language] 488 | pub struct NumberList { 489 | #[rust_sitter::leaf(pattern = r"\d+", transform = |v| v.parse().unwrap())] 490 | numbers: Vec>, 491 | } 492 | 493 | #[rust_sitter::extra] 494 | struct Whitespace { 495 | #[rust_sitter::leaf(pattern = r"\s")] 496 | _whitespace: (), 497 | } 498 | } 499 | } { 500 | m 501 | } else { 502 | panic!() 503 | }; 504 | 505 | let grammar = generate_grammar(&m); 506 | insta::assert_snapshot!(grammar); 507 | generate_parser_for_grammar(&grammar.to_string(), GENERATED_SEMANTIC_VERSION).unwrap(); 508 | } 509 | } 510 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.96" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" 19 | 20 | [[package]] 21 | name = "bitflags" 22 | version = "2.8.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 25 | 26 | [[package]] 27 | name = "bumpalo" 28 | version = "3.17.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 31 | 32 | [[package]] 33 | name = "c2rust-bitfields" 34 | version = "0.3.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "eb34f0c0ace43530b2df7f18bc69ee0c4082158aa451ece29602f8c841e73764" 37 | dependencies = [ 38 | "c2rust-bitfields-derive", 39 | ] 40 | 41 | [[package]] 42 | name = "c2rust-bitfields-derive" 43 | version = "0.2.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "3dd1601a7b828ab874d890e5a895563ca8ad485bdd3d2a359f148c8b72537241" 46 | dependencies = [ 47 | "proc-macro2", 48 | "quote", 49 | "syn 1.0.109", 50 | ] 51 | 52 | [[package]] 53 | name = "cc" 54 | version = "1.2.14" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" 57 | dependencies = [ 58 | "shlex", 59 | ] 60 | 61 | [[package]] 62 | name = "cfg-if" 63 | version = "1.0.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 66 | 67 | [[package]] 68 | name = "codemap" 69 | version = "0.1.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" 72 | 73 | [[package]] 74 | name = "codemap-diagnostic" 75 | version = "0.1.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "cc20770be05b566a963bf91505e60412c4a2d016d1ef95c5512823bb085a8122" 78 | dependencies = [ 79 | "codemap", 80 | "termcolor", 81 | ] 82 | 83 | [[package]] 84 | name = "console" 85 | version = "0.15.10" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" 88 | dependencies = [ 89 | "encode_unicode", 90 | "libc", 91 | "once_cell", 92 | "windows-sys", 93 | ] 94 | 95 | [[package]] 96 | name = "displaydoc" 97 | version = "0.2.5" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 100 | dependencies = [ 101 | "proc-macro2", 102 | "quote", 103 | "syn 2.0.98", 104 | ] 105 | 106 | [[package]] 107 | name = "encode_unicode" 108 | version = "1.0.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 111 | 112 | [[package]] 113 | name = "equivalent" 114 | version = "1.0.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 117 | 118 | [[package]] 119 | name = "errno" 120 | version = "0.3.10" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 123 | dependencies = [ 124 | "libc", 125 | "windows-sys", 126 | ] 127 | 128 | [[package]] 129 | name = "fastrand" 130 | version = "2.3.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 133 | 134 | [[package]] 135 | name = "form_urlencoded" 136 | version = "1.2.1" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 139 | dependencies = [ 140 | "percent-encoding", 141 | ] 142 | 143 | [[package]] 144 | name = "getrandom" 145 | version = "0.3.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" 148 | dependencies = [ 149 | "cfg-if", 150 | "libc", 151 | "wasi", 152 | "windows-targets", 153 | ] 154 | 155 | [[package]] 156 | name = "hashbrown" 157 | version = "0.15.2" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 160 | 161 | [[package]] 162 | name = "heck" 163 | version = "0.5.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 166 | 167 | [[package]] 168 | name = "icu_collections" 169 | version = "1.5.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 172 | dependencies = [ 173 | "displaydoc", 174 | "yoke", 175 | "zerofrom", 176 | "zerovec", 177 | ] 178 | 179 | [[package]] 180 | name = "icu_locid" 181 | version = "1.5.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 184 | dependencies = [ 185 | "displaydoc", 186 | "litemap", 187 | "tinystr", 188 | "writeable", 189 | "zerovec", 190 | ] 191 | 192 | [[package]] 193 | name = "icu_locid_transform" 194 | version = "1.5.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 197 | dependencies = [ 198 | "displaydoc", 199 | "icu_locid", 200 | "icu_locid_transform_data", 201 | "icu_provider", 202 | "tinystr", 203 | "zerovec", 204 | ] 205 | 206 | [[package]] 207 | name = "icu_locid_transform_data" 208 | version = "1.5.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 211 | 212 | [[package]] 213 | name = "icu_normalizer" 214 | version = "1.5.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 217 | dependencies = [ 218 | "displaydoc", 219 | "icu_collections", 220 | "icu_normalizer_data", 221 | "icu_properties", 222 | "icu_provider", 223 | "smallvec", 224 | "utf16_iter", 225 | "utf8_iter", 226 | "write16", 227 | "zerovec", 228 | ] 229 | 230 | [[package]] 231 | name = "icu_normalizer_data" 232 | version = "1.5.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 235 | 236 | [[package]] 237 | name = "icu_properties" 238 | version = "1.5.1" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 241 | dependencies = [ 242 | "displaydoc", 243 | "icu_collections", 244 | "icu_locid_transform", 245 | "icu_properties_data", 246 | "icu_provider", 247 | "tinystr", 248 | "zerovec", 249 | ] 250 | 251 | [[package]] 252 | name = "icu_properties_data" 253 | version = "1.5.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 256 | 257 | [[package]] 258 | name = "icu_provider" 259 | version = "1.5.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 262 | dependencies = [ 263 | "displaydoc", 264 | "icu_locid", 265 | "icu_provider_macros", 266 | "stable_deref_trait", 267 | "tinystr", 268 | "writeable", 269 | "yoke", 270 | "zerofrom", 271 | "zerovec", 272 | ] 273 | 274 | [[package]] 275 | name = "icu_provider_macros" 276 | version = "1.5.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 279 | dependencies = [ 280 | "proc-macro2", 281 | "quote", 282 | "syn 2.0.98", 283 | ] 284 | 285 | [[package]] 286 | name = "idna" 287 | version = "1.0.3" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 290 | dependencies = [ 291 | "idna_adapter", 292 | "smallvec", 293 | "utf8_iter", 294 | ] 295 | 296 | [[package]] 297 | name = "idna_adapter" 298 | version = "1.2.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 301 | dependencies = [ 302 | "icu_normalizer", 303 | "icu_properties", 304 | ] 305 | 306 | [[package]] 307 | name = "indexmap" 308 | version = "2.7.1" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 311 | dependencies = [ 312 | "equivalent", 313 | "hashbrown", 314 | ] 315 | 316 | [[package]] 317 | name = "indoc" 318 | version = "2.0.5" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 321 | 322 | [[package]] 323 | name = "insta" 324 | version = "1.42.1" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" 327 | dependencies = [ 328 | "console", 329 | "linked-hash-map", 330 | "once_cell", 331 | "pin-project", 332 | "similar", 333 | ] 334 | 335 | [[package]] 336 | name = "itoa" 337 | version = "1.0.14" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 340 | 341 | [[package]] 342 | name = "js-sys" 343 | version = "0.3.77" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 346 | dependencies = [ 347 | "once_cell", 348 | "wasm-bindgen", 349 | ] 350 | 351 | [[package]] 352 | name = "libc" 353 | version = "0.2.169" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 356 | 357 | [[package]] 358 | name = "linked-hash-map" 359 | version = "0.5.6" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 362 | 363 | [[package]] 364 | name = "linux-raw-sys" 365 | version = "0.4.15" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 368 | 369 | [[package]] 370 | name = "litemap" 371 | version = "0.7.4" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" 374 | 375 | [[package]] 376 | name = "log" 377 | version = "0.4.25" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 380 | 381 | [[package]] 382 | name = "memchr" 383 | version = "2.7.4" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 386 | 387 | [[package]] 388 | name = "minicov" 389 | version = "0.3.7" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" 392 | dependencies = [ 393 | "cc", 394 | "walkdir", 395 | ] 396 | 397 | [[package]] 398 | name = "once_cell" 399 | version = "1.20.3" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 402 | 403 | [[package]] 404 | name = "percent-encoding" 405 | version = "2.3.1" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 408 | 409 | [[package]] 410 | name = "pin-project" 411 | version = "1.1.9" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" 414 | dependencies = [ 415 | "pin-project-internal", 416 | ] 417 | 418 | [[package]] 419 | name = "pin-project-internal" 420 | version = "1.1.9" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" 423 | dependencies = [ 424 | "proc-macro2", 425 | "quote", 426 | "syn 2.0.98", 427 | ] 428 | 429 | [[package]] 430 | name = "proc-macro2" 431 | version = "1.0.93" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 434 | dependencies = [ 435 | "unicode-ident", 436 | ] 437 | 438 | [[package]] 439 | name = "quote" 440 | version = "1.0.38" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 443 | dependencies = [ 444 | "proc-macro2", 445 | ] 446 | 447 | [[package]] 448 | name = "regex" 449 | version = "1.11.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 452 | dependencies = [ 453 | "aho-corasick", 454 | "memchr", 455 | "regex-automata", 456 | "regex-syntax", 457 | ] 458 | 459 | [[package]] 460 | name = "regex-automata" 461 | version = "0.4.9" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 464 | dependencies = [ 465 | "aho-corasick", 466 | "memchr", 467 | "regex-syntax", 468 | ] 469 | 470 | [[package]] 471 | name = "regex-syntax" 472 | version = "0.8.5" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 475 | 476 | [[package]] 477 | name = "rust-sitter" 478 | version = "0.4.5" 479 | dependencies = [ 480 | "insta", 481 | "rust-sitter-macro", 482 | "tempfile", 483 | "tree-sitter", 484 | "tree-sitter-c2rust", 485 | ] 486 | 487 | [[package]] 488 | name = "rust-sitter-common" 489 | version = "0.4.5" 490 | dependencies = [ 491 | "quote", 492 | "syn 2.0.98", 493 | ] 494 | 495 | [[package]] 496 | name = "rust-sitter-example" 497 | version = "0.4.5" 498 | dependencies = [ 499 | "codemap", 500 | "codemap-diagnostic", 501 | "insta", 502 | "rust-sitter", 503 | "rust-sitter-tool", 504 | "wasm-bindgen-test", 505 | ] 506 | 507 | [[package]] 508 | name = "rust-sitter-macro" 509 | version = "0.4.5" 510 | dependencies = [ 511 | "insta", 512 | "proc-macro2", 513 | "quote", 514 | "rust-sitter-common", 515 | "syn 2.0.98", 516 | "tempfile", 517 | ] 518 | 519 | [[package]] 520 | name = "rust-sitter-tool" 521 | version = "0.4.5" 522 | dependencies = [ 523 | "cc", 524 | "insta", 525 | "rust-sitter-common", 526 | "serde", 527 | "serde_json", 528 | "syn 2.0.98", 529 | "syn-inline-mod", 530 | "tempfile", 531 | "tree-sitter", 532 | "tree-sitter-generate", 533 | ] 534 | 535 | [[package]] 536 | name = "rustc-hash" 537 | version = "2.1.1" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 540 | 541 | [[package]] 542 | name = "rustix" 543 | version = "0.38.44" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 546 | dependencies = [ 547 | "bitflags", 548 | "errno", 549 | "libc", 550 | "linux-raw-sys", 551 | "windows-sys", 552 | ] 553 | 554 | [[package]] 555 | name = "ryu" 556 | version = "1.0.19" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 559 | 560 | [[package]] 561 | name = "same-file" 562 | version = "1.0.6" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 565 | dependencies = [ 566 | "winapi-util", 567 | ] 568 | 569 | [[package]] 570 | name = "semver" 571 | version = "1.0.25" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" 574 | dependencies = [ 575 | "serde", 576 | ] 577 | 578 | [[package]] 579 | name = "serde" 580 | version = "1.0.218" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" 583 | dependencies = [ 584 | "serde_derive", 585 | ] 586 | 587 | [[package]] 588 | name = "serde_derive" 589 | version = "1.0.218" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" 592 | dependencies = [ 593 | "proc-macro2", 594 | "quote", 595 | "syn 2.0.98", 596 | ] 597 | 598 | [[package]] 599 | name = "serde_json" 600 | version = "1.0.139" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" 603 | dependencies = [ 604 | "indexmap", 605 | "itoa", 606 | "memchr", 607 | "ryu", 608 | "serde", 609 | ] 610 | 611 | [[package]] 612 | name = "shlex" 613 | version = "1.3.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 616 | 617 | [[package]] 618 | name = "similar" 619 | version = "2.7.0" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" 622 | 623 | [[package]] 624 | name = "smallbitvec" 625 | version = "2.6.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "d31d263dd118560e1a492922182ab6ca6dc1d03a3bf54e7699993f31a4150e3f" 628 | 629 | [[package]] 630 | name = "smallvec" 631 | version = "1.14.0" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 634 | 635 | [[package]] 636 | name = "stable_deref_trait" 637 | version = "1.2.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 640 | 641 | [[package]] 642 | name = "streaming-iterator" 643 | version = "0.1.9" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" 646 | 647 | [[package]] 648 | name = "syn" 649 | version = "1.0.109" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 652 | dependencies = [ 653 | "proc-macro2", 654 | "quote", 655 | "unicode-ident", 656 | ] 657 | 658 | [[package]] 659 | name = "syn" 660 | version = "2.0.98" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 663 | dependencies = [ 664 | "proc-macro2", 665 | "quote", 666 | "unicode-ident", 667 | ] 668 | 669 | [[package]] 670 | name = "syn-inline-mod" 671 | version = "0.6.0" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "2fa6dca1fdb7b2ed46dd534a326725419d4fb10f23d8c85a8b2860e5eb25d0f9" 674 | dependencies = [ 675 | "proc-macro2", 676 | "syn 2.0.98", 677 | ] 678 | 679 | [[package]] 680 | name = "synstructure" 681 | version = "0.13.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 684 | dependencies = [ 685 | "proc-macro2", 686 | "quote", 687 | "syn 2.0.98", 688 | ] 689 | 690 | [[package]] 691 | name = "tempfile" 692 | version = "3.17.1" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" 695 | dependencies = [ 696 | "cfg-if", 697 | "fastrand", 698 | "getrandom", 699 | "once_cell", 700 | "rustix", 701 | "windows-sys", 702 | ] 703 | 704 | [[package]] 705 | name = "termcolor" 706 | version = "1.4.1" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 709 | dependencies = [ 710 | "winapi-util", 711 | ] 712 | 713 | [[package]] 714 | name = "thiserror" 715 | version = "2.0.11" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" 718 | dependencies = [ 719 | "thiserror-impl", 720 | ] 721 | 722 | [[package]] 723 | name = "thiserror-impl" 724 | version = "2.0.11" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" 727 | dependencies = [ 728 | "proc-macro2", 729 | "quote", 730 | "syn 2.0.98", 731 | ] 732 | 733 | [[package]] 734 | name = "tinystr" 735 | version = "0.7.6" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 738 | dependencies = [ 739 | "displaydoc", 740 | "zerovec", 741 | ] 742 | 743 | [[package]] 744 | name = "tree-sitter" 745 | version = "0.25.2" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "5168a515fe492af54c5cc8800ff8c840be09fa5168de45838afaecd3e008bce4" 748 | dependencies = [ 749 | "cc", 750 | "regex", 751 | "regex-syntax", 752 | "serde_json", 753 | "streaming-iterator", 754 | "tree-sitter-language", 755 | ] 756 | 757 | [[package]] 758 | name = "tree-sitter-c2rust" 759 | version = "0.25.2" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "6e1e4909668d7a5e0eb8d66bd9fe2e789106066626056d3bead80dc6b9d5aeee" 762 | dependencies = [ 763 | "c2rust-bitfields", 764 | "once_cell", 765 | "regex", 766 | "regex-syntax", 767 | "streaming-iterator", 768 | "tree-sitter-language", 769 | ] 770 | 771 | [[package]] 772 | name = "tree-sitter-generate" 773 | version = "0.25.1" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "05beaa8023e08df928535c282cf684c4ca5151475348e73457161f76a50778e8" 776 | dependencies = [ 777 | "anyhow", 778 | "heck", 779 | "indexmap", 780 | "indoc", 781 | "log", 782 | "regex", 783 | "regex-syntax", 784 | "rustc-hash", 785 | "semver", 786 | "serde", 787 | "serde_json", 788 | "smallbitvec", 789 | "thiserror", 790 | "tree-sitter", 791 | "url", 792 | ] 793 | 794 | [[package]] 795 | name = "tree-sitter-language" 796 | version = "0.1.5" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" 799 | 800 | [[package]] 801 | name = "unicode-ident" 802 | version = "1.0.17" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 805 | 806 | [[package]] 807 | name = "url" 808 | version = "2.5.4" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 811 | dependencies = [ 812 | "form_urlencoded", 813 | "idna", 814 | "percent-encoding", 815 | "serde", 816 | ] 817 | 818 | [[package]] 819 | name = "utf16_iter" 820 | version = "1.0.5" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 823 | 824 | [[package]] 825 | name = "utf8_iter" 826 | version = "1.0.4" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 829 | 830 | [[package]] 831 | name = "walkdir" 832 | version = "2.5.0" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 835 | dependencies = [ 836 | "same-file", 837 | "winapi-util", 838 | ] 839 | 840 | [[package]] 841 | name = "wasi" 842 | version = "0.13.3+wasi-0.2.2" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 845 | dependencies = [ 846 | "wit-bindgen-rt", 847 | ] 848 | 849 | [[package]] 850 | name = "wasm-bindgen" 851 | version = "0.2.100" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 854 | dependencies = [ 855 | "cfg-if", 856 | "once_cell", 857 | "wasm-bindgen-macro", 858 | ] 859 | 860 | [[package]] 861 | name = "wasm-bindgen-backend" 862 | version = "0.2.100" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 865 | dependencies = [ 866 | "bumpalo", 867 | "log", 868 | "proc-macro2", 869 | "quote", 870 | "syn 2.0.98", 871 | "wasm-bindgen-shared", 872 | ] 873 | 874 | [[package]] 875 | name = "wasm-bindgen-futures" 876 | version = "0.4.50" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 879 | dependencies = [ 880 | "cfg-if", 881 | "js-sys", 882 | "once_cell", 883 | "wasm-bindgen", 884 | "web-sys", 885 | ] 886 | 887 | [[package]] 888 | name = "wasm-bindgen-macro" 889 | version = "0.2.100" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 892 | dependencies = [ 893 | "quote", 894 | "wasm-bindgen-macro-support", 895 | ] 896 | 897 | [[package]] 898 | name = "wasm-bindgen-macro-support" 899 | version = "0.2.100" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 902 | dependencies = [ 903 | "proc-macro2", 904 | "quote", 905 | "syn 2.0.98", 906 | "wasm-bindgen-backend", 907 | "wasm-bindgen-shared", 908 | ] 909 | 910 | [[package]] 911 | name = "wasm-bindgen-shared" 912 | version = "0.2.100" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 915 | dependencies = [ 916 | "unicode-ident", 917 | ] 918 | 919 | [[package]] 920 | name = "wasm-bindgen-test" 921 | version = "0.3.50" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" 924 | dependencies = [ 925 | "js-sys", 926 | "minicov", 927 | "wasm-bindgen", 928 | "wasm-bindgen-futures", 929 | "wasm-bindgen-test-macro", 930 | ] 931 | 932 | [[package]] 933 | name = "wasm-bindgen-test-macro" 934 | version = "0.3.50" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" 937 | dependencies = [ 938 | "proc-macro2", 939 | "quote", 940 | "syn 2.0.98", 941 | ] 942 | 943 | [[package]] 944 | name = "web-sys" 945 | version = "0.3.77" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 948 | dependencies = [ 949 | "js-sys", 950 | "wasm-bindgen", 951 | ] 952 | 953 | [[package]] 954 | name = "winapi-util" 955 | version = "0.1.9" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 958 | dependencies = [ 959 | "windows-sys", 960 | ] 961 | 962 | [[package]] 963 | name = "windows-sys" 964 | version = "0.59.0" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 967 | dependencies = [ 968 | "windows-targets", 969 | ] 970 | 971 | [[package]] 972 | name = "windows-targets" 973 | version = "0.52.6" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 976 | dependencies = [ 977 | "windows_aarch64_gnullvm", 978 | "windows_aarch64_msvc", 979 | "windows_i686_gnu", 980 | "windows_i686_gnullvm", 981 | "windows_i686_msvc", 982 | "windows_x86_64_gnu", 983 | "windows_x86_64_gnullvm", 984 | "windows_x86_64_msvc", 985 | ] 986 | 987 | [[package]] 988 | name = "windows_aarch64_gnullvm" 989 | version = "0.52.6" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 992 | 993 | [[package]] 994 | name = "windows_aarch64_msvc" 995 | version = "0.52.6" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 998 | 999 | [[package]] 1000 | name = "windows_i686_gnu" 1001 | version = "0.52.6" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1004 | 1005 | [[package]] 1006 | name = "windows_i686_gnullvm" 1007 | version = "0.52.6" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1010 | 1011 | [[package]] 1012 | name = "windows_i686_msvc" 1013 | version = "0.52.6" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1016 | 1017 | [[package]] 1018 | name = "windows_x86_64_gnu" 1019 | version = "0.52.6" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1022 | 1023 | [[package]] 1024 | name = "windows_x86_64_gnullvm" 1025 | version = "0.52.6" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1028 | 1029 | [[package]] 1030 | name = "windows_x86_64_msvc" 1031 | version = "0.52.6" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1034 | 1035 | [[package]] 1036 | name = "wit-bindgen-rt" 1037 | version = "0.33.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 1040 | dependencies = [ 1041 | "bitflags", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "write16" 1046 | version = "1.0.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1049 | 1050 | [[package]] 1051 | name = "writeable" 1052 | version = "0.5.5" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1055 | 1056 | [[package]] 1057 | name = "yoke" 1058 | version = "0.7.5" 1059 | source = "registry+https://github.com/rust-lang/crates.io-index" 1060 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 1061 | dependencies = [ 1062 | "serde", 1063 | "stable_deref_trait", 1064 | "yoke-derive", 1065 | "zerofrom", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "yoke-derive" 1070 | version = "0.7.5" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 1073 | dependencies = [ 1074 | "proc-macro2", 1075 | "quote", 1076 | "syn 2.0.98", 1077 | "synstructure", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "zerofrom" 1082 | version = "0.1.5" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" 1085 | dependencies = [ 1086 | "zerofrom-derive", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "zerofrom-derive" 1091 | version = "0.1.5" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" 1094 | dependencies = [ 1095 | "proc-macro2", 1096 | "quote", 1097 | "syn 2.0.98", 1098 | "synstructure", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "zerovec" 1103 | version = "0.10.4" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1106 | dependencies = [ 1107 | "yoke", 1108 | "zerofrom", 1109 | "zerovec-derive", 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "zerovec-derive" 1114 | version = "0.10.3" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1117 | dependencies = [ 1118 | "proc-macro2", 1119 | "quote", 1120 | "syn 2.0.98", 1121 | ] 1122 | --------------------------------------------------------------------------------