1use alloc::{string::String, vec::Vec};
19
20use crate::{
21 Grammar,
22 check::Checked,
23 rule::{Capture, Captures, Include, Kind, Rule},
24};
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
44pub enum Style {
45 #[default]
48 Pretty,
49 Compact,
52}
53
54pub(crate) fn push_decimal(out: &mut String, mut value: u64) {
56 let mut digits = [0u8; 20];
57 let mut at = digits.len();
58 loop {
59 at -= 1;
60 digits[at] = b'0' + (value % 10) as u8;
62 value /= 10;
63 if value == 0 {
64 break;
65 }
66 }
67 for &d in digits.get(at..).unwrap_or(&[]) {
68 out.push(char::from(d));
69 }
70}
71
72pub(crate) fn json_string(out: &mut String, parts: &[&str]) {
81 out.push('"');
82 for text in parts {
83 let bytes = text.as_bytes();
84 let mut from = 0;
85 let mut at = 0;
86 while let Some(&b) = bytes.get(at) {
87 let (escape, width): (&str, usize) = match b {
88 b'"' => ("\\\"", 1),
89 b'\\' => ("\\\\", 1),
90 b'\n' => ("\\n", 1),
91 b'\r' => ("\\r", 1),
92 b'\t' => ("\\t", 1),
93 0x08 => ("\\b", 1),
94 0x0C => ("\\f", 1),
95 0x00..=0x1F => {
96 out.push_str(text.get(from..at).unwrap_or(""));
97 out.push_str("\\u00");
98 out.push(hex(b >> 4));
99 out.push(hex(b & 0x0F));
100 at += 1;
101 from = at;
102 continue;
103 }
104 0xE2 => match bytes.get(at + 1..at + 3) {
105 Some([0x80, 0xA8]) => ("\\u2028", 3),
106 Some([0x80, 0xA9]) => ("\\u2029", 3),
107 _ => {
108 at += 1;
109 continue;
110 }
111 },
112 _ => {
113 at += 1;
114 continue;
115 }
116 };
117 out.push_str(text.get(from..at).unwrap_or(""));
118 out.push_str(escape);
119 at += width;
120 from = at;
121 }
122 out.push_str(text.get(from..).unwrap_or(""));
123 }
124 out.push('"');
125}
126
127fn hex(nibble: u8) -> char {
128 char::from(b"0123456789abcdef"[usize::from(nibble & 0x0F)])
129}
130
131struct Writer<'o> {
134 out: &'o mut String,
135 pretty: bool,
136 level: usize,
137 empty: bool,
138}
139
140impl Writer<'_> {
141 fn newline(&mut self) {
142 if self.pretty {
143 self.out.push('\n');
144 for _ in 0..self.level {
145 self.out.push_str(" ");
146 }
147 }
148 }
149
150 fn open(&mut self, bracket: char) {
151 self.out.push(bracket);
152 self.level += 1;
153 self.empty = true;
154 }
155
156 fn close(&mut self, bracket: char) {
159 self.level = self.level.saturating_sub(1);
160 if !self.empty {
161 self.newline();
162 }
163 self.out.push(bracket);
164 self.empty = false;
165 }
166
167 fn next(&mut self) {
169 if !self.empty {
170 self.out.push(',');
171 }
172 self.empty = false;
173 self.newline();
174 }
175
176 fn key(&mut self, parts: &[&str]) {
177 self.next();
178 json_string(self.out, parts);
179 self.out.push_str(if self.pretty { ": " } else { ":" });
180 }
181
182 fn string_member(&mut self, key: &str, value: &[&str]) {
183 self.key(&[key]);
184 json_string(self.out, value);
185 }
186
187 fn group_key(&mut self, group: u32) {
188 self.next();
189 self.out.push('"');
190 push_decimal(self.out, u64::from(group));
191 self.out.push_str(if self.pretty { "\": " } else { "\":" });
192 }
193}
194
195enum Task<'g> {
197 Rule(&'g Rule),
198 Capture(&'g Capture),
199 Str(&'static str, Option<&'g str>),
201 Rules(&'static str, &'g [Rule]),
203 Captures(&'static str, &'g Captures),
205 Flag(&'static str, bool),
207 Key(&'g str),
209 Group(u32),
210 Element,
212 Close(char),
213}
214
215pub(crate) fn write(grammar: &Grammar, checked: &Checked<'_>, out: &mut String, pretty: bool) {
217 out.reserve(256 + 160 * (grammar.patterns.len() + checked.repository.len()));
218 let mut emitter = Emitter {
219 w: Writer {
220 out,
221 pretty,
222 level: 0,
223 empty: true,
224 },
225 work: Vec::new(),
226 sorted: Vec::new(),
227 };
228 emitter.grammar(grammar, checked);
229 if pretty {
230 emitter.w.out.push('\n');
231 }
232}
233
234struct Emitter<'o, 'g> {
235 w: Writer<'o>,
236 work: Vec<Task<'g>>,
237 sorted: Vec<&'g (u32, Capture)>,
239}
240
241impl<'g> Emitter<'_, 'g> {
242 fn grammar(&mut self, g: &'g Grammar, checked: &Checked<'g>) {
243 self.w.open('{');
244 if let Some(name) = &g.name {
245 self.w.string_member("name", &[name]);
246 }
247 self.w.string_member("scopeName", &[&g.scope_name]);
248 if !g.file_types.is_empty() {
249 self.w.key(&["fileTypes"]);
250 self.w.open('[');
251 for file_type in &g.file_types {
252 self.w.next();
253 json_string(self.w.out, &[file_type]);
254 }
255 self.w.close(']');
256 }
257 if let Some(regex) = &g.first_line_match {
258 self.w.string_member("firstLineMatch", &[regex]);
259 }
260 if let Some(selector) = &g.injection_selector {
261 self.w.string_member("injectionSelector", &[selector]);
262 }
263 self.w.key(&["patterns"]);
266 self.w.open('[');
267 self.push_elements(&g.patterns);
268 self.run();
269 self.w.close(']');
270 for (key, entries) in [
271 ("repository", &checked.repository),
272 ("injections", &checked.injections),
273 ] {
274 if entries.is_empty() {
275 continue;
276 }
277 self.w.key(&[key]);
278 self.w.open('{');
279 self.work.push(Task::Close('}'));
280 for (name, rule) in entries.iter().rev() {
281 self.work.push(Task::Rule(rule));
282 self.work.push(Task::Key(name));
283 }
284 self.run();
285 }
286 self.w.close('}');
287 }
288
289 fn push_elements(&mut self, rules: &'g [Rule]) {
291 for rule in rules.iter().rev() {
292 self.work.push(Task::Rule(rule));
293 self.work.push(Task::Element);
294 }
295 }
296
297 fn run(&mut self) {
298 while let Some(task) = self.work.pop() {
299 match task {
300 Task::Rule(rule) => self.rule(rule),
301 Task::Capture(capture) => {
302 self.w.open('{');
303 self.work.push(Task::Close('}'));
304 self.work.push(Task::Rules("patterns", &capture.patterns));
305 self.work.push(Task::Str("name", capture.scope.as_deref()));
306 }
307 Task::Str(key, Some(value)) => self.w.string_member(key, &[value]),
308 Task::Str(_, None) => {}
309 Task::Rules(key, rules) => {
310 if !rules.is_empty() {
311 self.w.key(&[key]);
312 self.w.open('[');
313 self.work.push(Task::Close(']'));
314 self.push_elements(rules);
315 }
316 }
317 Task::Captures(key, captures) => self.captures(key, captures),
318 Task::Flag(key, true) => {
319 self.w.key(&[key]);
320 self.w.out.push('1');
321 }
322 Task::Flag(_, false) => {}
323 Task::Key(name) => self.w.key(&[name]),
324 Task::Group(group) => self.w.group_key(group),
325 Task::Element => self.w.next(),
326 Task::Close(bracket) => self.w.close(bracket),
327 }
328 }
329 }
330
331 fn rule(&mut self, rule: &'g Rule) {
334 self.w.open('{');
335 self.work.push(Task::Close('}'));
336 match &rule.0 {
337 Kind::Include(include) => self.include(include),
338 Kind::Patterns(rules) => self.work.push(Task::Rules("patterns", rules)),
339 Kind::Match(m) => self.work.extend([
340 Task::Captures("captures", &m.captures),
341 Task::Str("match", Some(&m.regex)),
342 Task::Str("name", m.scope.as_deref()),
343 ]),
344 Kind::BeginEnd(r) => self.work.extend([
345 Task::Rules("patterns", &r.patterns),
346 Task::Flag("applyEndPatternLast", r.apply_end_pattern_last),
347 Task::Captures("captures", &r.captures),
348 Task::Captures("endCaptures", &r.end_captures),
349 Task::Str("end", Some(&r.end)),
350 Task::Captures("beginCaptures", &r.begin_captures),
351 Task::Str("begin", Some(&r.begin)),
352 Task::Str("contentName", r.content_scope.as_deref()),
353 Task::Str("name", r.scope.as_deref()),
354 ]),
355 Kind::BeginWhile(r) => self.work.extend([
356 Task::Rules("patterns", &r.patterns),
357 Task::Captures("captures", &r.captures),
358 Task::Captures("whileCaptures", &r.while_captures),
359 Task::Str("while", Some(&r.while_)),
360 Task::Captures("beginCaptures", &r.begin_captures),
361 Task::Str("begin", Some(&r.begin)),
362 Task::Str("contentName", r.content_scope.as_deref()),
363 Task::Str("name", r.scope.as_deref()),
364 ]),
365 }
366 }
367
368 fn include(&mut self, include: &'g Include) {
370 let value: [&str; 3] = match include {
371 Include::Repository(name) => ["#", name, ""],
372 Include::SelfRef => ["$self", "", ""],
373 Include::Base => ["$base", "", ""],
374 Include::Scope(scope) => [scope, "", ""],
375 Include::ScopeEntry(scope, name) => [scope, "#", name],
376 };
377 self.w.string_member("include", &value);
378 }
379
380 fn captures(&mut self, key: &'static str, captures: &'g Captures) {
383 if captures.is_empty() {
384 return;
385 }
386 self.w.key(&[key]);
387 self.w.open('{');
388 self.work.push(Task::Close('}'));
389 self.sorted.clear();
390 self.sorted.extend(captures.iter());
391 self.sorted.sort_unstable_by_key(|(group, _)| *group);
392 for (group, capture) in self.sorted.drain(..).rev() {
393 self.work.push(Task::Capture(capture));
394 self.work.push(Task::Group(*group));
395 }
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 fn quoted(text: &str) -> String {
404 let mut out = String::new();
405 json_string(&mut out, &[text]);
406 out
407 }
408
409 #[test]
410 fn test_json_string_escapes() {
411 assert_eq!(quoted("plain"), "\"plain\"");
412 assert_eq!(quoted(""), "\"\"");
413 assert_eq!(quoted(r#"a"b\c"#), r#""a\"b\\c""#);
414 assert_eq!(quoted("\n\r\t\u{8}\u{c}"), r#""\n\r\t\b\f""#);
415 assert_eq!(quoted("\u{0}\u{1}\u{1f}"), r#""\u0000\u0001\u001f""#);
416 assert_eq!(quoted("x\u{2028}y\u{2029}z"), r#""x\u2028y\u2029z""#);
417 assert_eq!(quoted("é→\u{7f}/✓"), "\"é→\u{7f}/✓\"");
418 let mut out = String::new();
419 json_string(&mut out, &["a\"", "", "#b\n"]);
420 assert_eq!(out, r##""a\"#b\n""##);
421 }
422
423 #[test]
424 fn test_push_decimal() {
425 for (value, text) in [
426 (0, "0"),
427 (7, "7"),
428 (10, "10"),
429 (u64::MAX, "18446744073709551615"),
430 ] {
431 let mut out = String::new();
432 push_decimal(&mut out, value);
433 assert_eq!(out, text);
434 }
435 }
436
437 #[test]
438 fn test_writer_layout() {
439 let mut out = String::new();
440 let mut w = Writer {
441 out: &mut out,
442 pretty: true,
443 level: 0,
444 empty: true,
445 };
446 w.open('{');
447 w.key(&["a"]);
448 w.open('[');
449 w.close(']');
450 w.key(&["b"]);
451 w.open('[');
452 w.next();
453 w.out.push('1');
454 w.close(']');
455 w.close('}');
456 assert_eq!(out, "{\n \"a\": [],\n \"b\": [\n 1\n ]\n}");
457 }
458}