Skip to main content

treesitter_types/codegen/
grammar_ir.rs

1use serde::Deserialize;
2use std::collections::BTreeMap;
3
4/// A single type reference: a node kind + whether it's named.
5#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
6pub struct TypeRef {
7    #[serde(rename = "type")]
8    pub type_name: String,
9    pub named: bool,
10}
11
12/// Describes a field or children slot: multiplicity, requiredness, and possible types.
13#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
14pub struct FieldInfo {
15    pub multiple: bool,
16    pub required: bool,
17    pub types: Vec<TypeRef>,
18}
19
20/// A single entry from `node-types.json`.
21#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
22pub struct NodeType {
23    #[serde(rename = "type")]
24    pub type_name: String,
25    pub named: bool,
26    /// Named fields (only present on named nodes with fields).
27    #[serde(default)]
28    pub fields: BTreeMap<String, FieldInfo>,
29    /// Unnamed children (present when the node has non-field children).
30    pub children: Option<FieldInfo>,
31    /// Subtypes (present on supertype/abstract nodes like `_expression`).
32    pub subtypes: Option<Vec<TypeRef>>,
33}
34
35/// Parses the contents of a `node-types.json` file.
36pub fn parse_node_types(json: &str) -> Result<Vec<NodeType>, serde_json::Error> {
37    serde_json::from_str(json)
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_parse_leaf_node() {
46        let json = r#"[{"type": "identifier", "named": true}]"#;
47        let nodes = parse_node_types(json).unwrap();
48        assert_eq!(nodes.len(), 1);
49        assert_eq!(nodes[0].type_name, "identifier");
50        assert!(nodes[0].named);
51        assert!(nodes[0].fields.is_empty());
52        assert!(nodes[0].children.is_none());
53        assert!(nodes[0].subtypes.is_none());
54    }
55
56    #[test]
57    fn test_parse_anonymous_node() {
58        let json = r#"[{"type": ".", "named": false}]"#;
59        let nodes = parse_node_types(json).unwrap();
60        assert_eq!(nodes.len(), 1);
61        assert_eq!(nodes[0].type_name, ".");
62        assert!(!nodes[0].named);
63    }
64
65    #[test]
66    fn test_parse_node_with_fields() {
67        let json = r#"[{
68            "type": "import_spec",
69            "named": true,
70            "fields": {
71                "name": {
72                    "multiple": false,
73                    "required": false,
74                    "types": [
75                        {"type": ".", "named": false},
76                        {"type": "identifier", "named": true}
77                    ]
78                },
79                "path": {
80                    "multiple": false,
81                    "required": true,
82                    "types": [
83                        {"type": "interpreted_string_literal", "named": true}
84                    ]
85                }
86            }
87        }]"#;
88        let nodes = parse_node_types(json).unwrap();
89        assert_eq!(nodes.len(), 1);
90        let node = &nodes[0];
91        assert_eq!(node.type_name, "import_spec");
92        assert!(node.named);
93        assert_eq!(node.fields.len(), 2);
94
95        let name_field = &node.fields["name"];
96        assert!(!name_field.multiple);
97        assert!(!name_field.required);
98        assert_eq!(name_field.types.len(), 2);
99        assert_eq!(name_field.types[0].type_name, ".");
100        assert!(!name_field.types[0].named);
101        assert_eq!(name_field.types[1].type_name, "identifier");
102        assert!(name_field.types[1].named);
103
104        let path_field = &node.fields["path"];
105        assert!(!path_field.multiple);
106        assert!(path_field.required);
107        assert_eq!(path_field.types.len(), 1);
108    }
109
110    #[test]
111    fn test_parse_node_with_children() {
112        let json = r#"[{
113            "type": "import_spec_list",
114            "named": true,
115            "fields": {},
116            "children": {
117                "multiple": true,
118                "required": false,
119                "types": [
120                    {"type": "import_spec", "named": true}
121                ]
122            }
123        }]"#;
124        let nodes = parse_node_types(json).unwrap();
125        let node = &nodes[0];
126        let children = node.children.as_ref().unwrap();
127        assert!(children.multiple);
128        assert!(!children.required);
129        assert_eq!(children.types.len(), 1);
130        assert_eq!(children.types[0].type_name, "import_spec");
131    }
132
133    #[test]
134    fn test_parse_supertype_node() {
135        let json = r#"[{
136            "type": "_expression",
137            "named": true,
138            "subtypes": [
139                {"type": "binary_expression", "named": true},
140                {"type": "call_expression", "named": true},
141                {"type": "identifier", "named": true}
142            ]
143        }]"#;
144        let nodes = parse_node_types(json).unwrap();
145        let node = &nodes[0];
146        assert_eq!(node.type_name, "_expression");
147        let subtypes = node.subtypes.as_ref().unwrap();
148        assert_eq!(subtypes.len(), 3);
149        assert_eq!(subtypes[0].type_name, "binary_expression");
150        assert_eq!(subtypes[1].type_name, "call_expression");
151        assert_eq!(subtypes[2].type_name, "identifier");
152    }
153
154    #[test]
155    fn test_parse_multiple_nodes() {
156        let json = r#"[
157            {"type": "identifier", "named": true},
158            {"type": ".", "named": false},
159            {"type": "source_file", "named": true, "fields": {}, "children": {
160                "multiple": true, "required": false,
161                "types": [{"type": "identifier", "named": true}]
162            }}
163        ]"#;
164        let nodes = parse_node_types(json).unwrap();
165        assert_eq!(nodes.len(), 3);
166    }
167}