Skip to main content

qail_core/parser/
mod.rs

1//! QAIL Parser using nom.
2//!
3//! Parses QAIL v2 keyword-based syntax into an AST.
4//!
5//! # Syntax Overview
6//!
7//! ```text
8//! get users
9//! fields id, email
10//! where active = true
11//! order by created_at desc
12//! limit 10
13//! ```
14
15/// Grammar rules and parsing combinators.
16pub mod grammar;
17pub mod query_file;
18pub mod schema;
19
20#[cfg(test)]
21mod tests;
22
23use crate::ast::*;
24use crate::error::{QailError, QailResult};
25
26/// Maximum Qail input length (64 KB).
27/// Prevents stack overflow via deeply nested parenthesized expressions
28/// (the recursive descent parser has no depth limit, but 64KB is insufficient
29/// to encode enough nesting to blow the stack while being generous for any
30/// legitimate query).
31const MAX_INPUT_LENGTH: usize = 64 * 1024;
32
33/// Parse a complete QAIL query string (v2 syntax only).
34/// Uses keyword-based syntax: `get table fields * where col = value`
35/// Also supports shorthand: `get table[filter]` desugars to `get table where filter`
36pub fn parse(input: &str) -> QailResult<Qail> {
37    let input = input.trim();
38
39    // R8-A: Reject oversized inputs before recursive descent to prevent stack overflow
40    if input.len() > MAX_INPUT_LENGTH {
41        return Err(QailError::parse(
42            0,
43            format!(
44                "Input too large: {} bytes (max {} bytes)",
45                input.len(),
46                MAX_INPUT_LENGTH,
47            ),
48        ));
49    }
50
51    // Use grammar::parse which handles comment stripping + [filter] desugaring
52    match grammar::parse(input) {
53        Ok(cmd) => Ok(cmd),
54        Err(e) => Err(QailError::parse(0, e)),
55    }
56}