oni_comb_parser/text/
quoted_string.rs1use alloc::string::String;
2
3use crate::error::ParseError;
4use crate::fail::{Fail, PResult};
5use crate::input::Input;
6use crate::parser::Parser;
7use crate::str_input::StrInput;
8
9pub struct QuotedString;
10
11pub fn quoted_string() -> QuotedString {
12 QuotedString
13}
14
15impl<'a> Parser<StrInput<'a>> for QuotedString {
16 type Error = ParseError;
17 type Output = String;
18
19 #[inline]
20 fn parse_next(&mut self, input: &mut StrInput<'a>) -> PResult<String, ParseError> {
21 let pos = input.offset();
22 let remaining = input.as_str();
23 let mut chars = remaining.chars();
24
25 match chars.next() {
27 Some('"') => {}
28 _ => {
29 return Err(Fail::Backtrack(ParseError::expected_char(pos, '"')));
30 }
31 }
32
33 let mut result = String::new();
34 let mut consumed = 1; loop {
37 match chars.next() {
38 Some('"') => {
39 consumed += 1;
40 input.advance(consumed);
41 return Ok(result);
42 }
43 Some('\\') => {
44 consumed += 1;
45 match chars.next() {
46 Some('"') => {
47 consumed += 1;
48 result.push('"');
49 }
50 Some('\\') => {
51 consumed += 1;
52 result.push('\\');
53 }
54 Some('/') => {
55 consumed += 1;
56 result.push('/');
57 }
58 Some('b') => {
59 consumed += 1;
60 result.push('\u{0008}');
61 }
62 Some('f') => {
63 consumed += 1;
64 result.push('\u{000C}');
65 }
66 Some('n') => {
67 consumed += 1;
68 result.push('\n');
69 }
70 Some('r') => {
71 consumed += 1;
72 result.push('\r');
73 }
74 Some('t') => {
75 consumed += 1;
76 result.push('\t');
77 }
78 Some('u') => {
79 consumed += 1;
80 let mut hex = String::with_capacity(4);
81 for _ in 0..4 {
82 match chars.next() {
83 Some(c) if c.is_ascii_hexdigit() => {
84 consumed += c.len_utf8();
85 hex.push(c);
86 }
87 _ => {
88 return Err(Fail::Cut(ParseError::expected_description(
89 pos + consumed,
90 "4 hex digits after \\u",
91 )));
92 }
93 }
94 }
95 let code_point = u32::from_str_radix(&hex, 16).unwrap();
96 match char::from_u32(code_point) {
97 Some(c) => result.push(c),
98 None => {
99 return Err(Fail::Cut(ParseError::expected_description(
100 pos + consumed - 4,
101 "valid unicode code point",
102 )));
103 }
104 }
105 }
106 Some(_) => {
107 return Err(Fail::Cut(ParseError::expected_description(
108 pos + consumed,
109 "valid escape sequence",
110 )));
111 }
112 None => {
113 return Err(Fail::Cut(ParseError::expected_description(
114 pos + consumed,
115 "escape character after '\\'",
116 )));
117 }
118 }
119 }
120 Some(c) => {
121 consumed += c.len_utf8();
122 result.push(c);
123 }
124 None => {
125 return Err(Fail::Cut(ParseError::expected_char(pos + consumed, '"')));
126 }
127 }
128 }
129 }
130}