1pub fn is_placeholder_start(bytes: &[u8], i: usize) -> bool {
5 if i == 0 {
6 return true;
7 }
8 !matches!(
9 bytes[i - 1],
10 b'0'..=b'9'
11 | b'A'..=b'Z'
12 | b'a'..=b'z'
13 | b'_'
14 | b'$'
15 | b'\''
16 | b'"'
17 | b'`'
18 | b'}'
19 | b']'
20 )
21}
22
23#[inline]
25pub fn is_ident_start(b: u8) -> bool {
26 b.is_ascii_alphabetic() || b == b'_'
27}
28
29#[inline]
31pub fn is_ident_continue(b: u8) -> bool {
32 b.is_ascii_alphanumeric() || b == b'_'
33}
34
35pub fn scan_line_comment(bytes: &[u8], mut i: usize) -> usize {
37 while i < bytes.len() && bytes[i] != b'\n' {
38 i += 1;
39 }
40 i
41}
42
43pub fn scan_backtick(bytes: &[u8], mut i: usize) -> usize {
45 i += 1;
46 while i < bytes.len() {
47 if bytes[i] == b'`' {
48 return i + 1;
49 }
50 i += 1;
51 }
52 i
53}
54
55pub fn scan_single_quoted(bytes: &[u8], mut i: usize) -> usize {
57 i += 1;
58 while i < bytes.len() {
59 let sc = bytes[i];
60 i += 1;
61 if sc == b'\\' && i < bytes.len() {
62 i += 1;
63 } else if sc == b'\'' {
64 if i < bytes.len() && bytes[i] == b'\'' {
65 i += 1;
66 } else {
67 break;
68 }
69 }
70 }
71 i
72}
73
74#[inline]
75fn has_closing_triple(bytes: &[u8], start: usize, triple: &[u8; 3]) -> bool {
76 start + 2 < bytes.len() && &bytes[start..start + 3] == triple
77}
78
79pub fn scan_triple_quoted(bytes: &[u8], mut i: usize, quote: u8) -> usize {
81 i += 3;
82 let triple = [quote, quote, quote];
83 while i < bytes.len() {
84 if has_closing_triple(bytes, i, &triple) {
85 return i + 3;
86 }
87 i += 1;
88 }
89 i
90}
91
92pub fn scan_raw_string(bytes: &[u8], mut i: usize, quote: u8) -> usize {
94 i += 2;
95 while i < bytes.len() {
96 if bytes[i] == quote {
97 return i + 1;
98 }
99 i += 1;
100 }
101 i
102}
103
104pub fn scan_double_quoted(bytes: &[u8], mut i: usize) -> usize {
106 i += 1;
107 while i < bytes.len() {
108 let sc = bytes[i];
109 i += 1;
110 if sc == b'\\' && i < bytes.len() {
111 i += 1;
112 } else if sc == b'"' {
113 break;
114 }
115 }
116 i
117}
118
119pub fn skip_protected(bytes: &[u8], i: usize) -> Option<usize> {
122 match bytes[i] {
123 b'-' if i + 1 < bytes.len() && bytes[i + 1] == b'-' => {
124 Some(scan_line_comment(bytes, i + 2))
125 }
126 b'`' => Some(scan_backtick(bytes, i)),
127 b'\'' => {
128 if i + 2 < bytes.len() && bytes[i + 1] == b'\'' && bytes[i + 2] == b'\'' {
129 Some(scan_triple_quoted(bytes, i, b'\''))
130 } else {
131 Some(scan_single_quoted(bytes, i))
132 }
133 }
134 b'"' => {
135 if i + 2 < bytes.len() && bytes[i + 1] == b'"' && bytes[i + 2] == b'"' {
136 Some(scan_triple_quoted(bytes, i, b'"'))
137 } else {
138 Some(scan_double_quoted(bytes, i))
139 }
140 }
141 b'r' if i + 1 < bytes.len() && (bytes[i + 1] == b'\'' || bytes[i + 1] == b'"') => {
142 let quote = bytes[i + 1];
143 if i + 3 < bytes.len() && bytes[i + 2] == quote && bytes[i + 3] == quote {
144 Some(scan_triple_quoted(bytes, i + 1, quote))
145 } else {
146 Some(scan_raw_string(bytes, i, quote))
147 }
148 }
149 _ => None,
150 }
151}
152
153pub fn ident_at(source: &str, start: usize, end: usize) -> &str {
156 source.get(start..end).unwrap_or("")
157}