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 pub fn parse_value(input: &str) -> Result<crate::ast::Value, QqlError> {
82 let tokens = AstLowerer::lex(input)?;
83 let mut parser = AstLowerer::new(input, tokens);
84 let val = parser.parse_value()?;
85 parser.expect_end()?;
86 Ok(val)
87 }
88}
89
90impl<'a> AstLowerer<'a> {
91 fn new(input: &'a str, tokens: Vec<Token<'a>>) -> Self {
92 Self {
93 input,
94 tokens,
95 index: 0,
96 }
97 }
98
99 fn lower_statement(input: &'a str) -> Result<Stmt, QqlError> {
100 let tokens = Self::lex(input)?;
101 let mut parser = AstLowerer::new(input, tokens);
102 let stmt = parser.parse_stmt()?;
103 if parser.peek()?.kind == TokenKind::Semicolon {
104 parser.advance()?;
105 }
106 parser.expect_end()?;
107 Ok(stmt)
108 }
109
110 fn lower_script(input: &'a str) -> Result<Vec<Stmt>, QqlError> {
111 let tokens = Self::lex(input)?;
112 let mut parser = AstLowerer::new(input, tokens);
113 let mut statements = Vec::new();
114 if parser.peek()?.kind == TokenKind::Semicolon {
115 return Err(QqlError::parse(
116 "QQL-PARSE-EMPTY-STATEMENT",
117 "leading or empty statements are not allowed",
118 parser.peek()?.span,
119 ));
120 }
121
122 while parser.peek()?.kind != TokenKind::Eof {
123 if statements.len() >= MAX_STATEMENTS {
124 return Err(QqlError::parse(
125 "QQL-PARSE-STATEMENT-LIMIT",
126 alloc::format!("a script may contain at most {MAX_STATEMENTS} statements"),
127 parser.peek()?.span,
128 ));
129 }
130 statements.push(parser.parse_stmt()?);
131 match parser.peek()?.kind {
132 TokenKind::Semicolon => {
133 parser.advance()?;
134 if parser.peek()?.kind == TokenKind::Semicolon {
135 return Err(QqlError::parse(
136 "QQL-PARSE-EMPTY-STATEMENT",
137 "repeated semicolons are not allowed",
138 parser.peek()?.span,
139 ));
140 }
141 }
142 TokenKind::Eof => break,
143 _ => {
144 return Err(QqlError::parse(
145 "QQL-PARSE-SEPARATOR",
146 "multiple statements must be separated by a semicolon",
147 parser.peek()?.span,
148 ));
149 }
150 }
151 }
152 Ok(statements)
153 }
154
155 fn lex(input: &'a str) -> Result<Vec<Token<'a>>, QqlError> {
156 let lexer = Lexer::new(input);
157 let mut tokens = Vec::with_capacity(input.len() / 6 + 1);
158 for token_res in lexer {
159 tokens.push(token_res?);
160 }
161 Ok(tokens)
162 }
163
164 fn expect_end(&mut self) -> Result<(), QqlError> {
165 if self.index < self.tokens.len() {
166 let tok = self.tokens[self.index];
167 return Err(QqlError::parse(
168 "QQL-PARSE-TRAILING",
169 alloc::format!("unexpected trailing token '{}'", tok.text),
170 tok.span,
171 ));
172 }
173
174 Ok(())
175 }
176
177 pub fn parse_stmt(&mut self) -> Result<Stmt, QqlError> {
178 let tok = self.peek()?;
179 match tok.kind {
180 TokenKind::Create => self.parse_create(),
181 TokenKind::Alter => self.parse_alter(),
182 TokenKind::Drop => self.parse_drop(),
183 TokenKind::Show => self.parse_show(),
184 TokenKind::Upsert => self.parse_upsert(),
185 TokenKind::Scroll => self.parse_scroll(),
186 TokenKind::Query => self.parse_query(),
187 TokenKind::With => self.parse_query_with_cte(),
188 TokenKind::Delete => self.parse_delete(),
189 TokenKind::Clear => self.parse_clear(),
190 TokenKind::Update => self.parse_update(),
191 TokenKind::Count => self.parse_count(),
192 TokenKind::Set => self.parse_set_quota(),
193 _ => Err(QqlError::parse(
194 "QQL-PARSE-STATEMENT",
195 alloc::format!("expected a QQL statement keyword, got '{}'", tok.text),
196 tok.span,
197 )),
198 }
199 }
200
201 pub fn peek(&mut self) -> Result<Token<'a>, QqlError> {
204 if self.index < self.tokens.len() {
205 Ok(self.tokens[self.index])
206 } else {
207 Ok(Token::eof(self.input.len()))
208 }
209 }
210
211 pub fn peek_nth(&self, offset: usize) -> Token<'a> {
212 let idx = self.index + offset;
213 if idx < self.tokens.len() {
214 self.tokens[idx]
215 } else {
216 Token::eof(self.input.len())
217 }
218 }
219
220 pub fn advance(&mut self) -> Result<Token<'a>, QqlError> {
221 let tok = self.peek()?;
222 if self.index < self.tokens.len() {
223 self.index += 1;
224 }
225 Ok(tok)
226 }
227
228 pub fn expect(&mut self, kind: TokenKind) -> Result<Token<'a>, QqlError> {
229 let tok = self.peek()?;
230 if tok.kind != kind {
231 return Err(QqlError::parse(
232 "QQL-PARSE-EXPECTED",
233 alloc::format!("expected {} but got '{}'", kind, tok.text),
234 tok.span,
235 ));
236 }
237 self.advance()
238 }
239
240 pub fn parse_identifier_str(&mut self) -> Result<&'a str, QqlError> {
243 let tok = self.peek()?;
244 if tok.is_keyword_or_identifier() || tok.kind == TokenKind::String {
245 self.advance()?;
246 Ok(tok.text)
247 } else {
248 Err(QqlError::parse(
249 "QQL-PARSE-IDENTIFIER",
250 alloc::format!("expected identifier or quoted name, got '{}'", tok.text),
251 tok.span,
252 ))
253 }
254 }
255
256 pub fn parse_identifier(&mut self) -> Result<String, QqlError> {
257 self.parse_identifier_str().map(String::from)
258 }
259
260 pub fn parse_value(&mut self) -> Result<crate::ast::Value, QqlError> {
263 let tok = self.peek()?;
264 match tok.kind {
265 TokenKind::String => {
266 self.advance()?;
267 self.decode_string(tok).map(crate::ast::Value::Str)
268 }
269 TokenKind::Float => {
270 self.advance()?;
271 let v: f64 = tok.text.parse().map_err(|_| {
272 QqlError::parse(
273 "QQL-PARSE-FLOAT",
274 alloc::format!("invalid float literal '{}'", tok.text),
275 tok.span,
276 )
277 })?;
278 if !v.is_finite() {
281 return Err(QqlError::parse(
282 "QQL-PARSE-FLOAT",
283 alloc::format!("float literal '{}' is not finite", tok.text),
284 tok.span,
285 ));
286 }
287 Ok(crate::ast::Value::Float(v))
288 }
289 TokenKind::Integer => {
290 self.advance()?;
291 let v: i64 = tok.text.parse().map_err(|_| {
292 QqlError::parse(
293 "QQL-PARSE-INTEGER",
294 alloc::format!("invalid integer literal '{}'", tok.text),
295 tok.span,
296 )
297 })?;
298 Ok(crate::ast::Value::Int(v))
299 }
300 TokenKind::Null => {
301 self.advance()?;
302 Ok(crate::ast::Value::Null)
303 }
304 TokenKind::True => {
305 self.advance()?;
306 Ok(crate::ast::Value::Bool(true))
307 }
308 TokenKind::False => {
309 self.advance()?;
310 Ok(crate::ast::Value::Bool(false))
311 }
312 kind if kind.is_keyword_or_identifier() => {
313 self.advance()?;
314 if ascii_equal(tok.text, "TRUE") {
315 Ok(crate::ast::Value::Bool(true))
316 } else if ascii_equal(tok.text, "FALSE") {
317 Ok(crate::ast::Value::Bool(false))
318 } else if ascii_equal(tok.text, "NULL") {
319 Ok(crate::ast::Value::Null)
320 } else {
321 Ok(crate::ast::Value::Str(tok.text.to_string()))
322 }
323 }
324 TokenKind::Lbrace => self.parse_payload_dict().map(crate::ast::Value::Dict),
325 TokenKind::Lbracket => self.parse_list().map(crate::ast::Value::List),
326 _ => Err(QqlError::parse(
327 "QQL-PARSE-VALUE",
328 alloc::format!("unexpected value token '{}'", tok.text),
329 tok.span,
330 )),
331 }
332 }
333
334 fn decode_string(&self, token: Token<'a>) -> Result<String, QqlError> {
335 let input = self.input.as_bytes();
336 let start = token.span.start;
337 let end = token.span.end;
338 let first_byte = input.get(start).copied().unwrap_or(0);
339 let is_raw_or_backtick = first_byte == b'r' || first_byte == b'`';
340 let triple_quoted = end >= start + 6
346 && (input[start..start + 3] == b"'''"[..] || input[start..start + 3] == b"\"\"\""[..])
347 && input[start..start + 3] == input[end - 3..end];
348 if is_raw_or_backtick
349 || triple_quoted
350 || !(token.text.contains('\\') || first_byte == b'\'' && token.text.contains("''"))
351 {
352 return Ok(token.text.to_string());
353 }
354 let single_quoted = first_byte == b'\'';
355 let mut decoded = String::with_capacity(token.text.len());
356 let mut chars = token.text.chars().peekable();
357 while let Some(ch) = chars.next() {
358 if single_quoted && ch == '\'' && chars.peek() == Some(&'\'') {
359 chars.next();
360 decoded.push('\'');
361 continue;
362 }
363 if ch != '\\' {
364 decoded.push(ch);
365 continue;
366 }
367 let escaped = chars.next().ok_or_else(|| {
368 QqlError::parse(
369 "QQL-PARSE-ESCAPE",
370 "unterminated escape sequence",
371 token.span,
372 )
373 })?;
374 decoded.push(match escaped {
375 'n' => '\n',
376 'r' => '\r',
377 't' => '\t',
378 '\\' => '\\',
379 '\'' => '\'',
380 '"' => '"',
381 '$' => '$',
382 _ => {
383 return Err(QqlError::parse(
384 "QQL-PARSE-ESCAPE",
385 alloc::format!("unsupported escape sequence \\{}", escaped),
386 token.span,
387 ));
388 }
389 });
390 }
391 Ok(decoded)
392 }
393}