/proc/self/cwd/parser/internal/lexer.h
Line | Count | Source |
1 | | // Copyright 2026 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_LEXER_H_ |
16 | | #define THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_LEXER_H_ |
17 | | |
18 | | #include <cstddef> |
19 | | #include <cstdint> |
20 | | #include <limits> |
21 | | #include <optional> |
22 | | #include <string> |
23 | | #include <string_view> |
24 | | #include <utility> |
25 | | |
26 | | #include "absl/base/attributes.h" |
27 | | #include "absl/base/optimization.h" |
28 | | #include "absl/functional/function_ref.h" |
29 | | #include "absl/log/absl_check.h" |
30 | | #include "absl/strings/ascii.h" |
31 | | #include "common/source.h" |
32 | | |
33 | | namespace cel::parser_internal { |
34 | | |
35 | | enum class TokenType { |
36 | | kError = 0, |
37 | | kEnd, |
38 | | kWhitespace, |
39 | | kComment, |
40 | | |
41 | | // Keywords |
42 | | kNull, |
43 | | kFalse, |
44 | | kTrue, |
45 | | kIn, |
46 | | kReservedWord, |
47 | | |
48 | | // Literals |
49 | | kInt, |
50 | | kUint, |
51 | | kFloat, |
52 | | kString, |
53 | | kBytes, |
54 | | |
55 | | // Identifiers (standard bare identifiers and backtick-quoted identifiers). |
56 | | // Note: The lexer does not validate whether a quoted/escaped identifier is |
57 | | // source-legal or permitted in its syntactic context. Because |
58 | | // non-source-legal identifiers are used internally in macros and functions, |
59 | | // the parser must strictly validate the characters inside quoted identifiers |
60 | | // and verify that they only appear where permitted (e.g., field selections |
61 | | // and struct field specifiers). |
62 | | kIdent, |
63 | | |
64 | | // Delimiters |
65 | | kLeftBracket, // [ |
66 | | kRightBracket, // ] |
67 | | kLeftBrace, // { |
68 | | kRightBrace, // } |
69 | | kLeftParen, // ( |
70 | | kRightParen, // ) |
71 | | |
72 | | // Operators |
73 | | kDot, // . |
74 | | kComma, // , |
75 | | kMinus, // - |
76 | | kPlus, // + |
77 | | kAsterisk, // * |
78 | | kSlash, // / |
79 | | kPercent, // % |
80 | | kQuestion, // ? |
81 | | kColon, // : |
82 | | kExclamation, // ! |
83 | | kEqual, // = |
84 | | kEqualEqual, // == |
85 | | kExclamationEqual, // != |
86 | | kLess, // < |
87 | | kLessEqual, // <= |
88 | | kGreater, // > |
89 | | kGreaterEqual, // >= |
90 | | kLogicalAnd, // && |
91 | | kLogicalOr, // || |
92 | | }; |
93 | | |
94 | | ABSL_ATTRIBUTE_PURE_FUNCTION std::string_view TokenTypeToString(TokenType type); |
95 | | |
96 | | struct Token final { |
97 | | TokenType type = TokenType::kError; |
98 | | int32_t start = 0; |
99 | | int32_t end = 0; |
100 | | }; |
101 | | |
102 | | struct LexerError final { |
103 | | int32_t start = 0; |
104 | | int32_t end = 0; |
105 | | std::string message; |
106 | | }; |
107 | | |
108 | | // Lexer performs fast tokenization of CEL expression source code. |
109 | | // |
110 | | // Responsibilities & Parser Expectations: |
111 | | // This lexer is designed for speed and does not perform comprehensive semantic |
112 | | // or syntax validation: |
113 | | // |
114 | | // 1. String and Bytes Literal Escape Sequences: |
115 | | // - For standard single- and double-quoted literals ("..." and '...'), the |
116 | | // lexer recognizes backslash ('\') only to determine whether the closing |
117 | | // delimiter ('"' or '\'') is escaped (e.g., \" and \' do not terminate the |
118 | | // literal, whereas \\" terminates it because the backslash is escaped). |
119 | | // - For triple-quoted literals ("""...""" and '''...''') and raw literals |
120 | | // (r"...", r'''...'''), backslashes and escape sequences are not processed |
121 | | // when locating the closing delimiter. |
122 | | // - The lexer does NOT validate, decode, or check the syntax of any escape |
123 | | // sequences (e.g., \n, \r, \t, \xHH, \uHHHH, \U00HHHHHH, \0, or invalid |
124 | | // escapes like \q). All characters and backslashes within the literal |
125 | | // boundaries are preserved verbatim in the token's text span. |
126 | | // - The parser/caller is strictly responsible for validating and unescaping |
127 | | // all escape sequences and reporting syntax errors for invalid escape |
128 | | // sequences when converting string and bytes tokens during AST |
129 | | // construction. |
130 | | // |
131 | | // 2. Numeric Literals: |
132 | | // - Performs only general bounds and format matching for integers and |
133 | | // floating-point numeric literals. The lexer expects the parser to perform |
134 | | // final validation and numeric conversion when building the AST. |
135 | | class Lexer final { |
136 | | public: |
137 | | explicit Lexer(const cel::Source& source) |
138 | 0 | : content_(source.content()), position_(0) { |
139 | 0 | ABSL_DCHECK_LE(content_.size(), static_cast<SourcePosition>( |
140 | 0 | std::numeric_limits<int32_t>::max())); |
141 | 0 | } |
142 | | |
143 | | struct Position final { |
144 | | int32_t position = 0; |
145 | | bool at_end = false; |
146 | | bool done = false; |
147 | | LexerError error; |
148 | | }; |
149 | | |
150 | | Lexer(const Lexer&) = delete; |
151 | | Lexer(Lexer&&) = delete; |
152 | | Lexer& operator=(const Lexer&) = delete; |
153 | | Lexer& operator=(Lexer&&) = delete; |
154 | | |
155 | | // Scans and returns the next token from the source. |
156 | | [[nodiscard]] ABSL_ATTRIBUTE_NOINLINE Token Lex(); |
157 | | |
158 | | // Inspect the error from the last call to `Lex()` that returned an error |
159 | | // token. The reference is not guaranteed to be valid after further calls to |
160 | | // `Lex`. |
161 | | [[nodiscard]] const LexerError& GetError() const |
162 | 0 | ABSL_ATTRIBUTE_LIFETIME_BOUND { |
163 | 0 | return error_; |
164 | 0 | } |
165 | | |
166 | 0 | [[nodiscard]] int32_t GetPosition() const { return position_; } |
167 | | |
168 | 0 | [[nodiscard]] Position SavePosition() const { |
169 | 0 | return Position{position_, at_end_, done_, error_}; |
170 | 0 | } |
171 | | |
172 | 0 | void RestorePosition(const Position& position) { |
173 | 0 | position_ = position.position; |
174 | 0 | at_end_ = position.at_end; |
175 | 0 | done_ = position.done; |
176 | 0 | error_ = position.error; |
177 | 0 | } |
178 | | |
179 | | private: |
180 | 0 | [[nodiscard]] bool Match(char32_t c) const { |
181 | 0 | return position_ < content_.size() && content_.at(position_) == c; |
182 | 0 | } |
183 | | |
184 | 0 | [[nodiscard]] bool MatchIgnoreCase(char32_t c) const { |
185 | 0 | if (position_ >= content_.size()) return false; |
186 | 0 | char32_t cp = content_.at(position_); |
187 | 0 | return cp <= 0x7f && c <= 0x7f && |
188 | 0 | absl::ascii_tolower(static_cast<char>(cp)) == |
189 | 0 | absl::ascii_tolower(static_cast<char>(c)); |
190 | 0 | } |
191 | | |
192 | 0 | void Advance(size_t n) { |
193 | 0 | ABSL_DCHECK_LE(n, static_cast<size_t>(content_.size() - position_)); |
194 | 0 | position_ += static_cast<int32_t>(n); |
195 | 0 | } |
196 | | |
197 | 0 | void AdvanceProcessingNewLines(int32_t end_position) { |
198 | 0 | ABSL_DCHECK_LE(end_position, content_.size()); |
199 | 0 | ABSL_DCHECK_GE(end_position, position_); |
200 | 0 | Advance(static_cast<size_t>(end_position - position_)); |
201 | 0 | } |
202 | | |
203 | 0 | [[nodiscard]] Token MakeToken(TokenType type, int32_t start, int32_t end) { |
204 | 0 | if (ABSL_PREDICT_FALSE(at_end_)) { |
205 | 0 | AtEndTokenCreated(); |
206 | 0 | } |
207 | 0 | return Token{.type = type, .start = start, .end = end}; |
208 | 0 | } |
209 | | |
210 | | [[nodiscard]] Token SetError(int32_t start, int32_t end, |
211 | 0 | std::string message) { |
212 | 0 | error_ = |
213 | 0 | LexerError{.start = start, .end = end, .message = std::move(message)}; |
214 | 0 | return Token{.type = TokenType::kError, .start = start, .end = end}; |
215 | 0 | } |
216 | | |
217 | 0 | void AtEndTokenCreated() { done_ = true; } |
218 | | |
219 | | // Consumes characters up to and including the first occurrence of character |
220 | | // `c` without interpreting backslashes as escapes. Returns true if `c` was |
221 | | // found and consumed; false if end of input was reached. |
222 | | [[nodiscard]] bool ConsumeUntilAfter(char32_t c); |
223 | | |
224 | | // Consumes characters up to and including the first occurrence of substring |
225 | | // `s` without interpreting backslashes as escapes (`s` must not contain |
226 | | // newlines). Returns true if `s` was found and consumed; false if end of |
227 | | // input was reached. |
228 | | [[nodiscard]] bool ConsumeUntilAfterString(std::u32string_view s); |
229 | | |
230 | | // Consumes characters up to and including the first occurrence of `c` that is |
231 | | // not preceded by an odd number of backslash ('\') escape characters. Returns |
232 | | // true if an unescaped `c` was found and consumed; false if reached EOF. |
233 | | [[nodiscard]] bool ConsumeUntilAfterUnescaped(char32_t c); |
234 | | |
235 | | // Consumes characters up to and including the first occurrence of substring |
236 | | // `s` where the first character of `s` is not preceded by an odd number of |
237 | | // backslashes. Returns true if an unescaped `s` was found and consumed; false |
238 | | // if reached EOF. |
239 | | [[nodiscard]] bool ConsumeUntilAfterUnescapedString(std::u32string_view s); |
240 | | |
241 | | [[nodiscard]] bool MatchString(std::u32string_view s) const; |
242 | | |
243 | | [[nodiscard]] std::optional<char32_t> MatchIf( |
244 | | absl::FunctionRef<bool(char32_t)> predicate) const; |
245 | | |
246 | | void ConsumeLine(); |
247 | | |
248 | | void ConsumeWhitespace(); |
249 | | |
250 | | [[nodiscard]] bool Consume(char32_t c); |
251 | | |
252 | | [[nodiscard]] bool ConsumeIgnoreCase(char32_t c); |
253 | | |
254 | | [[nodiscard]] bool ConsumeString(std::u32string_view s); |
255 | | |
256 | | [[nodiscard]] std::optional<char32_t> ConsumeIf( |
257 | | absl::FunctionRef<bool(char32_t)> predicate); |
258 | | |
259 | | [[nodiscard]] bool ConsumeDigits(); |
260 | | |
261 | | [[nodiscard]] bool ConsumeHexDigits(); |
262 | | |
263 | | [[nodiscard]] TokenType ConsumeIntegralSuffix(); |
264 | | |
265 | | // Consumes a backtick-quoted identifier (`...`) and returns |
266 | | // TokenType::kIdent. The token text preserves the surrounding backticks so |
267 | | // the parser can detect quoted identifiers and enforce restrictions on their |
268 | | // characters and allowed syntactic locations (such as field selections and |
269 | | // struct field specifiers). |
270 | | [[nodiscard]] Token ConsumeQuotedIdent(); |
271 | | |
272 | | [[nodiscard]] Token ConsumeStringLiteral(int32_t start, char32_t quote, |
273 | | bool is_bytes = false, |
274 | | bool is_raw = false); |
275 | | |
276 | | // Consumes prefixed string and bytes literals. |
277 | | // Handles the following prefix sequences (case-insensitive for 'r' and 'b'): |
278 | | // - Raw strings: r"...", r'...', r"""...""", r'''...''' |
279 | | // - Bytes: b"...", b'...', b"""...""", b'''...''' |
280 | | // - Raw bytes: br"...", br'...', br"""...""", br'''...''', rb"...", rb'...', |
281 | | // rb"""...""", rb'''...''' |
282 | | [[nodiscard]] std::optional<Token> ConsumePrefixedStringLiteral(); |
283 | | |
284 | | // Consumes a numeric literal token and returns its TokenType (kInt, kUint, or |
285 | | // kFloat). Recognizes the following literal formats: |
286 | | // - Decimal integers (kInt / kUint): 0, 45U, 123456 |
287 | | // - Hexadecimal integers (kInt / kUint): 0x1A, 0XFFu, 0x0U |
288 | | // - Floating-point numbers (kFloat): .12345, 1.23, 1e6, 1.5e+10, .5e-3 |
289 | | [[nodiscard]] Token ConsumeNumericLiteral(); |
290 | | |
291 | | // Consumes an identifier token and checks if it matches any reserved |
292 | | // keywords. |
293 | | [[nodiscard]] Token ConsumeIdent(); |
294 | | |
295 | | cel::SourceContentView content_; |
296 | | int32_t position_ = 0; |
297 | | bool at_end_ = false; |
298 | | bool done_ = false; |
299 | | LexerError error_; |
300 | | }; |
301 | | |
302 | | } // namespace cel::parser_internal |
303 | | |
304 | | #endif // THIRD_PARTY_CEL_CPP_PARSER_INTERNAL_LEXER_H_ |