summaryrefslogtreecommitdiffstats
path: root/third_party/rust/litrs/src/char/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/litrs/src/char/mod.rs')
-rw-r--r--third_party/rust/litrs/src/char/mod.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/third_party/rust/litrs/src/char/mod.rs b/third_party/rust/litrs/src/char/mod.rs
new file mode 100644
index 0000000000..54f6f1137f
--- /dev/null
+++ b/third_party/rust/litrs/src/char/mod.rs
@@ -0,0 +1,105 @@
+use std::fmt;
+
+use crate::{
+ Buffer, ParseError,
+ err::{perr, ParseErrorKind::*},
+ escape::unescape,
+ parse::{first_byte_or_empty, check_suffix},
+};
+
+
+/// A character literal, e.g. `'g'` or `'🦊'`.
+///
+/// See [the reference][ref] for more information.
+///
+/// [ref]: https://doc.rust-lang.org/reference/tokens.html#character-literals
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CharLit<B: Buffer> {
+ raw: B,
+ /// Start index of the suffix or `raw.len()` if there is no suffix.
+ start_suffix: usize,
+ value: char,
+}
+
+impl<B: Buffer> CharLit<B> {
+ /// Parses the input as a character literal. Returns an error if the input
+ /// is invalid or represents a different kind of literal.
+ pub fn parse(input: B) -> Result<Self, ParseError> {
+ match first_byte_or_empty(&input)? {
+ b'\'' => {
+ let (value, start_suffix) = parse_impl(&input)?;
+ Ok(Self { raw: input, value, start_suffix })
+ },
+ _ => Err(perr(0, DoesNotStartWithQuote)),
+ }
+ }
+
+ /// Returns the character value that this literal represents.
+ pub fn value(&self) -> char {
+ self.value
+ }
+
+ /// The optional suffix. Returns `""` if the suffix is empty/does not exist.
+ pub fn suffix(&self) -> &str {
+ &(*self.raw)[self.start_suffix..]
+ }
+
+ /// Returns the raw input that was passed to `parse`.
+ pub fn raw_input(&self) -> &str {
+ &self.raw
+ }
+
+ /// Returns the raw input that was passed to `parse`, potentially owned.
+ pub fn into_raw_input(self) -> B {
+ self.raw
+ }
+
+}
+
+impl CharLit<&str> {
+ /// Makes a copy of the underlying buffer and returns the owned version of
+ /// `Self`.
+ pub fn to_owned(&self) -> CharLit<String> {
+ CharLit {
+ raw: self.raw.to_owned(),
+ start_suffix: self.start_suffix,
+ value: self.value,
+ }
+ }
+}
+
+impl<B: Buffer> fmt::Display for CharLit<B> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(&self.raw)
+ }
+}
+
+/// Precondition: first character in input must be `'`.
+#[inline(never)]
+pub(crate) fn parse_impl(input: &str) -> Result<(char, usize), ParseError> {
+ let first = input.chars().nth(1).ok_or(perr(None, UnterminatedCharLiteral))?;
+ let (c, len) = match first {
+ '\'' if input.chars().nth(2) == Some('\'') => return Err(perr(1, UnescapedSingleQuote)),
+ '\'' => return Err(perr(None, EmptyCharLiteral)),
+ '\n' | '\t' | '\r'
+ => return Err(perr(1, UnescapedSpecialWhitespace)),
+
+ '\\' => unescape::<char>(&input[1..], 1)?,
+ other => (other, other.len_utf8()),
+ };
+
+ match input[1 + len..].find('\'') {
+ Some(0) => {}
+ Some(_) => return Err(perr(None, OverlongCharLiteral)),
+ None => return Err(perr(None, UnterminatedCharLiteral)),
+ }
+
+ let start_suffix = 1 + len + 1;
+ let suffix = &input[start_suffix..];
+ check_suffix(suffix).map_err(|kind| perr(start_suffix, kind))?;
+
+ Ok((c, start_suffix))
+}
+
+#[cfg(test)]
+mod tests;