Skip to main content

jsonc_parser/
parser.rs

1use std::borrow::Cow;
2
3use crate::ParseOptions;
4use crate::common::Range;
5use crate::errors::*;
6use crate::scanner::Scanner;
7use crate::scanner::ScannerOptions;
8use crate::tokens::Token;
9
10pub(crate) enum ObjectKey<'a> {
11  String(Cow<'a, str>),
12  Word(&'a str),
13}
14
15impl<'a> ObjectKey<'a> {
16  /// Converts the key into a `Cow`, borrowing from the source when possible
17  /// to avoid an allocation for clean (unescaped) keys.
18  pub fn into_cow(self) -> Cow<'a, str> {
19    match self {
20      ObjectKey::String(s) => s,
21      ObjectKey::Word(s) => Cow::Borrowed(s),
22    }
23  }
24}
25
26/// Shared JSONC parser infrastructure used by both `parse_to_value` and
27/// the serde deserializer. Handles scanning, comment skipping, depth
28/// tracking, and comma/separator logic.
29pub(crate) struct JsoncParser<'a> {
30  pub scanner: Scanner<'a>,
31  #[allow(dead_code)] // used by the serde feature
32  pub text: &'a str,
33  allow_comments: bool,
34  allow_trailing_commas: bool,
35  allow_missing_commas: bool,
36  allow_loose_object_property_names: bool,
37  depth: usize,
38  pending_token: Option<Token<'a>>,
39}
40
41impl<'a> JsoncParser<'a> {
42  pub fn new(text: &'a str, options: &ParseOptions) -> Self {
43    Self {
44      scanner: Scanner::new(
45        text,
46        &ScannerOptions {
47          allow_single_quoted_strings: options.allow_single_quoted_strings,
48          allow_hexadecimal_numbers: options.allow_hexadecimal_numbers,
49          allow_unary_plus_numbers: options.allow_unary_plus_numbers,
50        },
51      ),
52      text,
53      allow_comments: options.allow_comments,
54      allow_trailing_commas: options.allow_trailing_commas,
55      allow_missing_commas: options.allow_missing_commas,
56      allow_loose_object_property_names: options.allow_loose_object_property_names,
57      depth: 0,
58      pending_token: None,
59    }
60  }
61
62  /// Scans the next non-comment token. Returns a pending token if one
63  /// was put back via `put_back`.
64  pub fn scan(&mut self) -> Result<Option<Token<'a>>, ParseError> {
65    if let Some(token) = self.pending_token.take() {
66      return Ok(Some(token));
67    }
68    loop {
69      match self.scanner.scan()? {
70        Some(Token::CommentLine(_) | Token::CommentBlock(_)) => {
71          if !self.allow_comments {
72            return Err(
73              self
74                .scanner
75                .create_error_for_current_token(ParseErrorKind::CommentsNotAllowed),
76            );
77          }
78          continue;
79        }
80        token => return Ok(token),
81      }
82    }
83  }
84
85  /// Puts a token back so the next `scan()` returns it.
86  #[cfg(feature = "serde")]
87  pub fn put_back(&mut self, token: Token<'a>) {
88    debug_assert!(self.pending_token.is_none(), "put_back called with pending token");
89    self.pending_token = Some(token);
90  }
91
92  /// Increments depth and checks the nesting limit.
93  pub fn enter_container(&mut self) -> Result<(), ParseError> {
94    self.depth += 1;
95    if self.depth > 512 {
96      self.depth -= 1;
97      Err(
98        self
99          .scanner
100          .create_error_for_current_token(ParseErrorKind::NestingDepthExceeded),
101      )
102    } else {
103      Ok(())
104    }
105  }
106
107  /// Decrements depth.
108  pub fn exit_container(&mut self) {
109    self.depth -= 1;
110  }
111
112  /// Returns an error appropriate for an unexpected token.
113  pub fn unexpected_token_error(&self, token: &Token) -> ParseError {
114    let kind = match token {
115      Token::CloseBracket => ParseErrorKind::UnexpectedCloseBracket,
116      Token::CloseBrace => ParseErrorKind::UnexpectedCloseBrace,
117      Token::Comma => ParseErrorKind::UnexpectedComma,
118      Token::Colon => ParseErrorKind::UnexpectedColon,
119      Token::Word(_) => ParseErrorKind::UnexpectedWord,
120      _ => ParseErrorKind::UnexpectedToken,
121    };
122    self.scanner.create_error_for_current_token(kind)
123  }
124
125  /// Scans the next object entry (key or close brace), handling commas
126  /// between entries. Pass `first = true` for the first entry.
127  pub fn scan_object_entry(&mut self, first: bool) -> Result<Option<ObjectKey<'a>>, ParseError> {
128    if first {
129      return self.scan_object_key();
130    }
131
132    let after_value_end = self.scanner.token_end();
133    let token = self.scan()?;
134    match token {
135      Some(Token::Comma) => {
136        let comma_range = Range::new(self.scanner.token_start(), self.scanner.token_end());
137        let key = self.scan_object_key()?;
138        if key.is_none() && !self.allow_trailing_commas {
139          return Err(
140            self
141              .scanner
142              .create_error_for_range(comma_range, ParseErrorKind::TrailingCommasNotAllowed),
143          );
144        }
145        Ok(key)
146      }
147      Some(Token::CloseBrace) => Ok(None),
148      Some(Token::String(s)) if self.allow_missing_commas => Ok(Some(ObjectKey::String(s))),
149      Some(Token::Word(s) | Token::Number(s)) if self.allow_missing_commas => {
150        if !self.allow_loose_object_property_names {
151          return Err(
152            self
153              .scanner
154              .create_error_for_current_token(ParseErrorKind::ExpectedStringObjectProperty),
155          );
156        }
157        Ok(Some(ObjectKey::Word(s)))
158      }
159      Some(Token::String(_) | Token::Word(_) | Token::Number(_)) => {
160        let range = Range::new(after_value_end, after_value_end);
161        Err(
162          self
163            .scanner
164            .create_error_for_range(range, ParseErrorKind::ExpectedComma),
165        )
166      }
167      None => Err(
168        self
169          .scanner
170          .create_error_for_current_token(ParseErrorKind::UnterminatedObject),
171      ),
172      _ => Err(
173        self
174          .scanner
175          .create_error_for_current_token(ParseErrorKind::UnexpectedTokenInObject),
176      ),
177    }
178  }
179
180  /// Scans an object property colon separator.
181  pub fn scan_object_colon(&mut self) -> Result<(), ParseError> {
182    match self.scan()? {
183      Some(Token::Colon) => Ok(()),
184      _ => Err(
185        self
186          .scanner
187          .create_error_for_current_token(ParseErrorKind::ExpectedColonAfterObjectKey),
188      ),
189    }
190  }
191
192  /// After an array element, scans for the comma/close-bracket and
193  /// returns the next token.
194  pub fn scan_array_comma(&mut self) -> Result<Option<Token<'a>>, ParseError> {
195    debug_assert!(self.pending_token.is_none(), "the previous value must be consumed");
196    let after_value_end = self.scanner.token_end();
197    match self.scan()? {
198      Some(Token::Comma) => {
199        let comma_range = Range::new(self.scanner.token_start(), self.scanner.token_end());
200        let next = self.scan()?;
201        if matches!(&next, Some(Token::CloseBracket)) && !self.allow_trailing_commas {
202          return Err(
203            self
204              .scanner
205              .create_error_for_range(comma_range, ParseErrorKind::TrailingCommasNotAllowed),
206          );
207        }
208        Ok(next)
209      }
210      Some(token) if !self.allow_missing_commas && token.is_value_start() => {
211        let range = Range::new(after_value_end, after_value_end);
212        Err(
213          self
214            .scanner
215            .create_error_for_range(range, ParseErrorKind::ExpectedComma),
216        )
217      }
218      token => Ok(token),
219    }
220  }
221
222  fn scan_object_key(&mut self) -> Result<Option<ObjectKey<'a>>, ParseError> {
223    match self.scan()? {
224      Some(Token::CloseBrace) => Ok(None),
225      Some(Token::String(s)) => Ok(Some(ObjectKey::String(s))),
226      Some(Token::Word(s) | Token::Number(s)) => {
227        if !self.allow_loose_object_property_names {
228          return Err(
229            self
230              .scanner
231              .create_error_for_current_token(ParseErrorKind::ExpectedStringObjectProperty),
232          );
233        }
234        Ok(Some(ObjectKey::Word(s)))
235      }
236      None => Err(
237        self
238          .scanner
239          .create_error_for_current_token(ParseErrorKind::UnterminatedObject),
240      ),
241      _ => Err(
242        self
243          .scanner
244          .create_error_for_current_token(ParseErrorKind::UnexpectedTokenInObject),
245      ),
246    }
247  }
248}