Skip to main content

trident/syntax/grammar/
mod.rs

1mod dsl;
2#[cfg(test)]
3mod tests;
4mod trident;
5
6pub use dsl::*;
7pub use trident::trident_grammar;
8
9/// Top-level grammar matching tree-sitter's grammar.json schema.
10pub struct Grammar {
11    pub name: &'static str,
12    pub word: &'static str,
13    pub rules: Vec<(&'static str, Node)>,
14    pub extras: Vec<Node>,
15}
16
17/// A node in a grammar rule tree.
18/// Maps 1:1 to tree-sitter's 13 JSON node types.
19pub enum Node {
20    Seq(Vec<Node>),
21    Choice(Vec<Node>),
22    Repeat(Box<Node>),
23    Repeat1(Box<Node>),
24    Str(&'static str),
25    Symbol(&'static str),
26    Pattern(&'static str),
27    Field {
28        name: &'static str,
29        content: Box<Node>,
30    },
31    Prec {
32        value: i32,
33        content: Box<Node>,
34    },
35    PrecLeft {
36        value: i32,
37        content: Box<Node>,
38    },
39    Alias {
40        content: Box<Node>,
41        value: &'static str,
42        named: bool,
43    },
44    Token(Box<Node>),
45    Blank,
46}
47
48impl Grammar {
49    pub fn to_json(&self) -> String {
50        let mut out = String::with_capacity(64 * 1024);
51        out.push_str("{\n");
52        out.push_str("  \"$schema\": \"https://tree-sitter.github.io/tree-sitter/assets/schemas/grammar.schema.json\",\n");
53        write_kv(&mut out, "name", self.name, 2);
54        out.push_str(",\n");
55        write_kv(&mut out, "word", self.word, 2);
56        out.push_str(",\n");
57
58        // rules (ordered object)
59        out.push_str("  \"rules\": {\n");
60        for (i, (name, node)) in self.rules.iter().enumerate() {
61            out.push_str("    \"");
62            out.push_str(name);
63            out.push_str("\": ");
64            node.write_json(&mut out, 4);
65            if i + 1 < self.rules.len() {
66                out.push(',');
67            }
68            out.push('\n');
69        }
70        out.push_str("  },\n");
71
72        // extras
73        out.push_str("  \"extras\": [\n");
74        for (i, node) in self.extras.iter().enumerate() {
75            out.push_str("    ");
76            node.write_json(&mut out, 4);
77            if i + 1 < self.extras.len() {
78                out.push(',');
79            }
80            out.push('\n');
81        }
82        out.push_str("  ],\n");
83
84        out.push_str("  \"conflicts\": [],\n");
85        out.push_str("  \"precedences\": [],\n");
86        out.push_str("  \"externals\": [],\n");
87        out.push_str("  \"inline\": [],\n");
88        out.push_str("  \"supertypes\": []\n");
89        out.push_str("}\n");
90        out
91    }
92}
93
94fn write_kv(out: &mut String, key: &str, val: &str, indent: usize) {
95    write_indent(out, indent);
96    out.push('"');
97    out.push_str(key);
98    out.push_str("\": \"");
99    json_escape(out, val);
100    out.push('"');
101}
102
103fn json_escape(out: &mut String, s: &str) {
104    for ch in s.chars() {
105        match ch {
106            '\\' => out.push_str("\\\\"),
107            '"' => out.push_str("\\\""),
108            '\n' => out.push_str("\\n"),
109            '\r' => out.push_str("\\r"),
110            '\t' => out.push_str("\\t"),
111            _ => out.push(ch),
112        }
113    }
114}
115
116fn write_indent(out: &mut String, n: usize) {
117    for _ in 0..n {
118        out.push(' ');
119    }
120}
121
122impl Node {
123    fn write_json(&self, out: &mut String, indent: usize) {
124        match self {
125            Node::Seq(members) => {
126                write_obj_open(out, "SEQ", indent);
127                write_members(out, "members", members, indent + 2);
128                out.push('\n');
129                write_indent(out, indent);
130                out.push('}');
131            }
132            Node::Choice(members) => {
133                write_obj_open(out, "CHOICE", indent);
134                write_members(out, "members", members, indent + 2);
135                out.push('\n');
136                write_indent(out, indent);
137                out.push('}');
138            }
139            Node::Repeat(content) => {
140                write_obj_open(out, "REPEAT", indent);
141                write_content(out, content, indent + 2);
142                out.push('\n');
143                write_indent(out, indent);
144                out.push('}');
145            }
146            Node::Repeat1(content) => {
147                write_obj_open(out, "REPEAT1", indent);
148                write_content(out, content, indent + 2);
149                out.push('\n');
150                write_indent(out, indent);
151                out.push('}');
152            }
153            Node::Str(value) => {
154                out.push_str("{\n");
155                write_indent(out, indent + 2);
156                out.push_str("\"type\": \"STRING\",\n");
157                write_kv(out, "value", value, indent + 2);
158                out.push('\n');
159                write_indent(out, indent);
160                out.push('}');
161            }
162            Node::Symbol(name) => {
163                out.push_str("{\n");
164                write_indent(out, indent + 2);
165                out.push_str("\"type\": \"SYMBOL\",\n");
166                write_kv(out, "name", name, indent + 2);
167                out.push('\n');
168                write_indent(out, indent);
169                out.push('}');
170            }
171            Node::Pattern(value) => {
172                out.push_str("{\n");
173                write_indent(out, indent + 2);
174                out.push_str("\"type\": \"PATTERN\",\n");
175                write_kv(out, "value", value, indent + 2);
176                out.push('\n');
177                write_indent(out, indent);
178                out.push('}');
179            }
180            Node::Field { name, content } => {
181                out.push_str("{\n");
182                write_indent(out, indent + 2);
183                out.push_str("\"type\": \"FIELD\",\n");
184                write_kv(out, "name", name, indent + 2);
185                out.push_str(",\n");
186                write_content(out, content, indent + 2);
187                out.push('\n');
188                write_indent(out, indent);
189                out.push('}');
190            }
191            Node::Prec { value, content } => {
192                out.push_str("{\n");
193                write_indent(out, indent + 2);
194                out.push_str("\"type\": \"PREC\",\n");
195                write_indent(out, indent + 2);
196                out.push_str("\"value\": ");
197                out.push_str(&value.to_string());
198                out.push_str(",\n");
199                write_content(out, content, indent + 2);
200                out.push('\n');
201                write_indent(out, indent);
202                out.push('}');
203            }
204            Node::PrecLeft { value, content } => {
205                out.push_str("{\n");
206                write_indent(out, indent + 2);
207                out.push_str("\"type\": \"PREC_LEFT\",\n");
208                write_indent(out, indent + 2);
209                out.push_str("\"value\": ");
210                out.push_str(&value.to_string());
211                out.push_str(",\n");
212                write_content(out, content, indent + 2);
213                out.push('\n');
214                write_indent(out, indent);
215                out.push('}');
216            }
217            Node::Alias {
218                content,
219                value,
220                named,
221            } => {
222                out.push_str("{\n");
223                write_indent(out, indent + 2);
224                out.push_str("\"type\": \"ALIAS\",\n");
225                write_content(out, content, indent + 2);
226                out.push_str(",\n");
227                write_indent(out, indent + 2);
228                out.push_str("\"named\": ");
229                out.push_str(if *named { "true" } else { "false" });
230                out.push_str(",\n");
231                write_kv(out, "value", value, indent + 2);
232                out.push('\n');
233                write_indent(out, indent);
234                out.push('}');
235            }
236            Node::Token(content) => {
237                out.push_str("{\n");
238                write_indent(out, indent + 2);
239                out.push_str("\"type\": \"TOKEN\",\n");
240                write_content(out, content, indent + 2);
241                out.push('\n');
242                write_indent(out, indent);
243                out.push('}');
244            }
245            Node::Blank => {
246                out.push_str("{\n");
247                write_indent(out, indent + 2);
248                out.push_str("\"type\": \"BLANK\"\n");
249                write_indent(out, indent);
250                out.push('}');
251            }
252        }
253    }
254}
255
256fn write_obj_open(out: &mut String, ty: &str, indent: usize) {
257    out.push_str("{\n");
258    write_indent(out, indent + 2);
259    out.push_str("\"type\": \"");
260    out.push_str(ty);
261    out.push_str("\",\n");
262}
263
264fn write_members(out: &mut String, key: &str, nodes: &[Node], indent: usize) {
265    write_indent(out, indent);
266    out.push('"');
267    out.push_str(key);
268    out.push_str("\": [\n");
269    for (i, node) in nodes.iter().enumerate() {
270        write_indent(out, indent + 2);
271        node.write_json(out, indent + 2);
272        if i + 1 < nodes.len() {
273            out.push(',');
274        }
275        out.push('\n');
276    }
277    write_indent(out, indent);
278    out.push(']');
279}
280
281fn write_content(out: &mut String, node: &Node, indent: usize) {
282    write_indent(out, indent);
283    out.push_str("\"content\": ");
284    node.write_json(out, indent);
285}