1pub(crate) mod alter_drop_show;
2pub(crate) mod config_parsers;
3pub(crate) mod config_validation;
4pub(crate) mod create;
5pub(crate) mod filter;
6pub(crate) mod formula;
7pub(crate) mod helpers;
8pub(crate) mod point_ops;
9pub(crate) mod query;
10pub(crate) mod r#update;
11pub(crate) mod upsert;
12pub(crate) mod with_clause;
13
14use crate::ast::Stmt;
15use crate::error::QqlError;
16use crate::lexer::Lexer;
17use crate::token::{Token, TokenKind};
18use alloc::string::String;
19use alloc::vec::Vec;
20pub use config_validation::{
21 check_deleted_threshold, config_bool, config_float_range, config_has_key,
22 config_max_optimization_threads, config_non_negative_u64, config_positive_u64, config_value,
23 merge_collection_config, validate_hnsw_value, validate_index_options,
24 validate_optimizers_value, validate_params_value, validate_vectors_value,
25};
26
27pub struct Parser;
34
35pub(crate) struct AstLowerer<'a> {
36 pub input: &'a str,
37 tokens: Vec<Token<'a>>,
38 index: usize,
39}
40
41pub const MAX_STATEMENTS: usize = 256;
44
45pub fn ascii_equal(s: &str, upper: &str) -> bool {
46 if s.len() != upper.len() {
47 return false;
48 }
49 s.as_bytes()
50 .iter()
51 .zip(upper.as_bytes().iter())
52 .all(|(a, b)| a.to_ascii_uppercase() == *b)
53}
54
55pub fn ascii_equal_lower(s: &str, lower: &str) -> bool {
56 if s.len() != lower.len() {
57 return false;
58 }
59 s.as_bytes()
60 .iter()
61 .zip(lower.as_bytes().iter())
62 .all(|(a, b)| a.to_ascii_lowercase() == *b)
63}
64
65pub fn is_contextual_field_name(kind: TokenKind) -> bool {
66 kind.is_keyword_or_identifier()
67}
68
69impl Parser {
70 pub fn parse(input: &str) -> Result<Stmt, QqlError> {
71 AstLowerer::lower_statement(input)
72 }
73
74 pub fn parse_all(input: &str) -> Result<Vec<Stmt>, QqlError> {
75 AstLowerer::lower_script(input)
76 }
77}
78
79impl<'a> AstLowerer<'a> {
80 fn new(input: &'a str, tokens: Vec<Token<'a>>) -> Self {
81 Self {
82 input,
83 tokens,
84 index: 0,
85 }
86 }
87
88 fn lower_statement(input: &'a str) -> Result<Stmt, QqlError> {
89 let tokens = Self::lex(input)?;
90 let mut parser = AstLowerer::new(input, tokens);
91 let stmt = parser.parse_stmt()?;
92 if parser.peek()?.kind == TokenKind::Semicolon {
93 parser.advance()?;
94 }
95 parser.expect_end()?;
96 Ok(stmt)
97 }
98
99 fn lower_script(input: &'a str) -> Result<Vec<Stmt>, QqlError> {
100 let tokens = Self::lex(input)?;
101 let mut parser = AstLowerer::new(input, tokens);
102 let mut statements = Vec::new();
103 if parser.peek()?.kind == TokenKind::Semicolon {
104 return Err(QqlError::parse(
105 "QQL-PARSE-EMPTY-STATEMENT",
106 "leading or empty statements are not allowed",
107 parser.peek()?.span,
108 ));
109 }
110
111 while parser.peek()?.kind != TokenKind::Eof {
112 if statements.len() >= MAX_STATEMENTS {
113 return Err(QqlError::parse(
114 "QQL-PARSE-STATEMENT-LIMIT",
115 alloc::format!("a script may contain at most {MAX_STATEMENTS} statements"),
116 parser.peek()?.span,
117 ));
118 }
119 statements.push(parser.parse_stmt()?);
120 match parser.peek()?.kind {
121 TokenKind::Semicolon => {
122 parser.advance()?;
123 if parser.peek()?.kind == TokenKind::Semicolon {
124 return Err(QqlError::parse(
125 "QQL-PARSE-EMPTY-STATEMENT",
126 "repeated semicolons are not allowed",
127 parser.peek()?.span,
128 ));
129 }
130 }
131 TokenKind::Eof => break,
132 _ => {
133 return Err(QqlError::parse(
134 "QQL-PARSE-SEPARATOR",
135 "multiple statements must be separated by a semicolon",
136 parser.peek()?.span,
137 ));
138 }
139 }
140 }
141 Ok(statements)
142 }
143
144 fn lex(input: &'a str) -> Result<Vec<Token<'a>>, QqlError> {
145 let lexer = Lexer::new(input);
146 let mut tokens = Vec::with_capacity(input.len() / 6 + 1);
147 for token_res in lexer {
148 tokens.push(token_res?);
149 }
150 Ok(tokens)
151 }
152
153 fn expect_end(&mut self) -> Result<(), QqlError> {
154 if self.index < self.tokens.len() {
155 let tok = self.tokens[self.index];
156 return Err(QqlError::parse(
157 "QQL-PARSE-TRAILING",
158 alloc::format!("unexpected trailing token '{}'", tok.text),
159 tok.span,
160 ));
161 }
162
163 Ok(())
164 }
165
166 pub fn parse_stmt(&mut self) -> Result<Stmt, QqlError> {
167 let tok = self.peek()?;
168 match tok.kind {
169 TokenKind::Create => self.parse_create(),
170 TokenKind::Alter => self.parse_alter(),
171 TokenKind::Drop => self.parse_drop(),
172 TokenKind::Show => self.parse_show(),
173 TokenKind::Upsert => self.parse_upsert(),
174 TokenKind::Scroll => self.parse_scroll(),
175 TokenKind::Query => self.parse_query(),
176 TokenKind::With => self.parse_query_with_cte(),
177 TokenKind::Delete => self.parse_delete(),
178 TokenKind::Clear => self.parse_clear(),
179 TokenKind::Update => self.parse_update(),
180 TokenKind::Count => self.parse_count(),
181 _ => Err(QqlError::parse(
182 "QQL-PARSE-STATEMENT",
183 alloc::format!("expected a QQL statement keyword, got '{}'", tok.text),
184 tok.span,
185 )),
186 }
187 }
188
189 pub fn peek(&mut self) -> Result<Token<'a>, QqlError> {
192 if self.index < self.tokens.len() {
193 Ok(self.tokens[self.index])
194 } else {
195 Ok(Token::eof(self.input.len()))
196 }
197 }
198
199 pub fn peek_nth(&self, offset: usize) -> Token<'a> {
200 let idx = self.index + offset;
201 if idx < self.tokens.len() {
202 self.tokens[idx]
203 } else {
204 Token::eof(self.input.len())
205 }
206 }
207
208 pub fn advance(&mut self) -> Result<Token<'a>, QqlError> {
209 let tok = self.peek()?;
210 if self.index < self.tokens.len() {
211 self.index += 1;
212 }
213 Ok(tok)
214 }
215
216 pub fn expect(&mut self, kind: TokenKind) -> Result<Token<'a>, QqlError> {
217 let tok = self.peek()?;
218 if tok.kind != kind {
219 return Err(QqlError::parse(
220 "QQL-PARSE-EXPECTED",
221 alloc::format!("expected {} but got '{}'", kind, tok.text),
222 tok.span,
223 ));
224 }
225 self.advance()
226 }
227
228 pub fn parse_identifier_str(&mut self) -> Result<&'a str, QqlError> {
231 let tok = self.peek()?;
232 if tok.is_keyword_or_identifier() || tok.kind == TokenKind::String {
233 self.advance()?;
234 Ok(tok.text)
235 } else {
236 Err(QqlError::parse(
237 "QQL-PARSE-IDENTIFIER",
238 alloc::format!("expected identifier or quoted name, got '{}'", tok.text),
239 tok.span,
240 ))
241 }
242 }
243
244 pub fn parse_identifier(&mut self) -> Result<String, QqlError> {
245 self.parse_identifier_str().map(String::from)
246 }
247
248 pub fn parse_value(&mut self) -> Result<crate::ast::Value, QqlError> {
251 let tok = self.peek()?;
252 match tok.kind {
253 TokenKind::String => {
254 self.advance()?;
255 self.decode_string(tok).map(crate::ast::Value::Str)
256 }
257 TokenKind::Float => {
258 self.advance()?;
259 let v: f64 = tok.text.parse().map_err(|_| {
260 QqlError::parse(
261 "QQL-PARSE-FLOAT",
262 alloc::format!("invalid float literal '{}'", tok.text),
263 tok.span,
264 )
265 })?;
266 if !v.is_finite() {
269 return Err(QqlError::parse(
270 "QQL-PARSE-FLOAT",
271 alloc::format!("float literal '{}' is not finite", tok.text),
272 tok.span,
273 ));
274 }
275 Ok(crate::ast::Value::Float(v))
276 }
277 TokenKind::Integer => {
278 self.advance()?;
279 let v: i64 = tok.text.parse().map_err(|_| {
280 QqlError::parse(
281 "QQL-PARSE-INTEGER",
282 alloc::format!("invalid integer literal '{}'", tok.text),
283 tok.span,
284 )
285 })?;
286 Ok(crate::ast::Value::Int(v))
287 }
288 TokenKind::Null => {
289 self.advance()?;
290 Ok(crate::ast::Value::Null)
291 }
292 TokenKind::True => {
293 self.advance()?;
294 Ok(crate::ast::Value::Bool(true))
295 }
296 TokenKind::False => {
297 self.advance()?;
298 Ok(crate::ast::Value::Bool(false))
299 }
300 kind if kind.is_keyword_or_identifier() => {
301 self.advance()?;
302 if ascii_equal(tok.text, "TRUE") {
303 Ok(crate::ast::Value::Bool(true))
304 } else if ascii_equal(tok.text, "FALSE") {
305 Ok(crate::ast::Value::Bool(false))
306 } else if ascii_equal(tok.text, "NULL") {
307 Ok(crate::ast::Value::Null)
308 } else {
309 Ok(crate::ast::Value::Str(tok.text.to_string()))
310 }
311 }
312 TokenKind::Lbrace => self
313 .parse_payload_dict()
314 .map(|items| crate::ast::Value::Dict(items.into_iter().collect())),
315 TokenKind::Lbracket => self.parse_list().map(crate::ast::Value::List),
316 _ => Err(QqlError::parse(
317 "QQL-PARSE-VALUE",
318 alloc::format!("unexpected value token '{}'", tok.text),
319 tok.span,
320 )),
321 }
322 }
323
324 fn decode_string(&self, token: Token<'a>) -> Result<String, QqlError> {
325 let input = self.input.as_bytes();
326 let start = token.span.start;
327 let end = token.span.end;
328 let first_byte = input.get(start).copied().unwrap_or(0);
329 let is_raw_or_backtick = first_byte == b'r' || first_byte == b'`';
330 let triple_quoted = end >= start + 6
336 && (input[start..start + 3] == b"'''"[..] || input[start..start + 3] == b"\"\"\""[..])
337 && input[start..start + 3] == input[end - 3..end];
338 if is_raw_or_backtick
339 || triple_quoted
340 || !(token.text.contains('\\') || first_byte == b'\'' && token.text.contains("''"))
341 {
342 return Ok(token.text.to_string());
343 }
344 let single_quoted = first_byte == b'\'';
345 let mut decoded = String::with_capacity(token.text.len());
346 let mut chars = token.text.chars().peekable();
347 while let Some(ch) = chars.next() {
348 if single_quoted && ch == '\'' && chars.peek() == Some(&'\'') {
349 chars.next();
350 decoded.push('\'');
351 continue;
352 }
353 if ch != '\\' {
354 decoded.push(ch);
355 continue;
356 }
357 let escaped = chars.next().ok_or_else(|| {
358 QqlError::parse(
359 "QQL-PARSE-ESCAPE",
360 "unterminated escape sequence",
361 token.span,
362 )
363 })?;
364 decoded.push(match escaped {
365 'n' => '\n',
366 'r' => '\r',
367 't' => '\t',
368 '\\' => '\\',
369 '\'' => '\'',
370 '"' => '"',
371 '$' => '$',
372 _ => {
373 return Err(QqlError::parse(
374 "QQL-PARSE-ESCAPE",
375 alloc::format!("unsupported escape sequence \\{}", escaped),
376 token.span,
377 ));
378 }
379 });
380 }
381 Ok(decoded)
382 }
383}