Skip to main content

wxml_parser_rs/parser/
mod.rs

1use serde_json::Value;
2
3mod ast_builder;
4mod cursor;
5mod error;
6mod eslint;
7mod scanner;
8pub mod ir;
9mod script;
10mod serialize;
11mod syntax;
12
13use cursor::Cursor;
14use ir::{NodeIr, ParsedProgram};
15pub(crate) use serialize::{serialize_program, serialize_program_to_string};
16
17pub use eslint::{parse_for_eslint_json, parse_for_eslint_json_string};
18
19pub(crate) struct Parser<'a> {
20  pub(crate) src: &'a str,
21  pub(crate) bytes: &'a [u8],
22  pub(crate) i: usize,
23  pub(crate) line: usize,
24  pub(crate) col: usize,
25  pub(crate) errors: Vec<ir::ParseErrorIr>,
26  pub(crate) emit_script_program: bool,
27}
28
29impl<'a> Parser<'a> {
30  fn new_with_mode(src: &'a str, emit_script_program: bool) -> Self {
31    Self {
32      src,
33      bytes: src.as_bytes(),
34      i: 0,
35      line: 1,
36      col: 1,
37      errors: vec![],
38      emit_script_program,
39    }
40  }
41
42  #[inline(always)]
43  pub(crate) fn is_name_char(c: u8) -> bool {
44    c.is_ascii_alphanumeric() || matches!(c, b':' | b'_' | b'-' | b'.')
45  }
46
47  pub(crate) fn parse_name(&mut self) -> Option<&'a str> {
48    let start = self.i;
49    self.skip_ascii_while(Self::is_name_char);
50    if self.i > start {
51      Some(self.safe_slice(start, self.i))
52    } else {
53      None
54    }
55  }
56}
57
58pub fn parse_json(code: &str) -> Value {
59  parse_json_with_mode(code, false)
60}
61
62/// Parse WXML and return the result as a JSON string directly,
63/// avoiding the expensive `serde_json::from_str` → Value step.
64/// Use this when the consumer will parse JSON on the JS side (e.g. `JSON.parse()`).
65pub fn parse_json_string(code: &str) -> String {
66  serialize_program_to_string(&parse_program_with_mode(code, false))
67}
68
69pub(crate) fn parse_json_with_mode(code: &str, emit_script_program: bool) -> Value {
70  serialize_program(parse_program_with_mode(code, emit_script_program))
71}
72
73pub fn parse_program_with_mode(code: &str, emit_script_program: bool) -> ParsedProgram<'_> {
74  if code.is_empty() {
75    return ParsedProgram {
76      body: vec![],
77      comment_indices: vec![],
78      errors: vec![],
79      end_line: 0,
80      end_col: 0,
81      code_len: 0,
82    };
83  }
84
85  let mut p = Parser::new_with_mode(code, emit_script_program);
86  let body = p.parse_document();
87
88  let comment_indices = body
89    .iter()
90    .enumerate()
91    .filter_map(|(idx, n)| {
92      if matches!(n, NodeIr::Comment { .. }) {
93        Some(idx)
94      } else {
95        None
96      }
97    })
98    .collect();
99
100  ParsedProgram {
101    body,
102    comment_indices,
103    errors: p.errors,
104    end_line: p.line,
105    end_col: p.col,
106    code_len: code.len(),
107  }
108}
109
110#[allow(dead_code)]
111fn _keep_cursor(_c: Cursor) {}