use bytes::BytesMut;
use reblessive::{Stack, Stk};
use self::token_buffer::TokenBuffer;
use crate::sql;
use crate::syn::error::{SyntaxError, bail};
use crate::syn::lexer::Lexer;
use crate::syn::lexer::compound::CompoundToken;
use crate::syn::token::{Span, Token, TokenKind, t};
mod basic;
mod builtin;
mod expression;
mod function;
mod idiom;
mod kind;
pub(crate) mod mac;
mod object;
mod prime;
mod record_id;
mod stmt;
mod token;
mod token_buffer;
mod value;
#[cfg(feature = "arbitrary")]
pub(crate) use builtin::{PATHS, PathKind};
pub(crate) use mac::{enter_object_recursion, enter_query_recursion, unexpected};
use super::error::{RenderedError, syntax_error};
#[cfg(test)]
pub mod test;
pub type ParseResult<T> = Result<T, SyntaxError>;
#[derive(Debug)]
pub enum PartialResult<T> {
MoreData,
Empty {
used: usize,
},
Ok {
value: T,
used: usize,
},
Err {
err: SyntaxError,
used: usize,
},
}
#[derive(Clone, Debug)]
pub struct ParserSettings {
pub legacy_strands: bool,
pub flexible_record_id: bool,
pub object_recursion_limit: usize,
pub query_recursion_limit: usize,
pub expr_recursion_limit: usize,
pub files_enabled: bool,
pub surrealism_enabled: bool,
pub json_string_escapes: bool,
}
impl Default for ParserSettings {
fn default() -> Self {
ParserSettings {
legacy_strands: false,
flexible_record_id: true,
object_recursion_limit: 100,
query_recursion_limit: 20,
expr_recursion_limit: 128,
files_enabled: false,
surrealism_enabled: false,
json_string_escapes: false,
}
}
}
impl ParserSettings {
pub fn default_with_experimental(enabled: bool) -> Self {
ParserSettings {
files_enabled: enabled,
surrealism_enabled: enabled,
..Self::default()
}
}
}
pub struct Parser<'a> {
lexer: Lexer<'a>,
last_span: Span,
token_buffer: TokenBuffer<4>,
pub(crate) table_as_field: bool,
settings: ParserSettings,
unscape_buffer: Vec<u8>,
}
impl<'a> Parser<'a> {
pub fn new(source: &'a [u8]) -> Self {
Parser::new_with_settings(source, ParserSettings::default())
}
pub fn new_with_experimental(source: &'a [u8], enabled: bool) -> Self {
Parser::new_with_settings(source, ParserSettings::default_with_experimental(enabled))
}
pub fn new_with_settings(source: &'a [u8], settings: ParserSettings) -> Self {
Parser {
lexer: Lexer::new(source),
last_span: Span::empty(),
token_buffer: TokenBuffer::new(),
table_as_field: true,
settings,
unscape_buffer: Vec::new(),
}
}
pub fn with_settings(mut self, settings: ParserSettings) -> Self {
self.settings = settings;
self
}
#[expect(clippy::should_implement_trait)]
pub fn next(&mut self) -> Token {
let res = self.token_buffer.pop().unwrap_or_else(|| self.lexer.next_token());
self.last_span = res.span;
res
}
pub fn next_whitespace(&mut self) -> Option<Token> {
if let Some(x) = self.peek_whitespace() {
self.pop_peek();
return Some(x);
}
None
}
pub fn has_peek(&self) -> bool {
self.token_buffer.is_empty()
}
pub fn pop_peek(&mut self) -> Token {
let res = self.token_buffer.pop().expect("token buffer is non-empty");
self.last_span = res.span;
res
}
pub fn peek(&mut self) -> Token {
let Some(x) = self.token_buffer.first() else {
let res = self.lexer.next_token();
self.token_buffer.push(res);
return res;
};
x
}
pub fn peek_whitespace(&mut self) -> Option<Token> {
let token = if let Some(x) = self.token_buffer.first() {
x
} else {
let token = self.lexer.next_token();
self.token_buffer.push(token);
token
};
if !token.span.follows_from(&self.last_span) {
return None;
}
Some(token)
}
pub fn peek_kind(&mut self) -> TokenKind {
self.peek().kind
}
pub(crate) fn peek_token_at(&mut self, at: u8) -> Token {
for _ in self.token_buffer.len()..=at {
let r = self.lexer.next_token();
self.token_buffer.push(r);
}
self.token_buffer.at(at).expect("token exists at index")
}
pub fn peek1(&mut self) -> Token {
self.peek_token_at(1)
}
pub fn peek2(&mut self) -> Token {
self.peek_token_at(2)
}
pub fn peek_whitespace_token_at<const AT: u8>(&mut self) -> Option<Token> {
const { assert!(AT < 4, "Peeking more then 4 tokens is not supported") };
if AT == 0 {
return self.peek_whitespace();
}
for _ in self.token_buffer.len()..=AT {
let res = self.lexer.next_token();
self.token_buffer.push(res);
}
let Some(token) = self.token_buffer.at(AT) else {
unreachable!()
};
let Some(prev_token) = self.token_buffer.at(AT - 1) else {
unreachable!()
};
if !token.span.follows_from(&prev_token.span) {
return None;
}
Some(token)
}
pub fn peek_whitespace1(&mut self) -> Option<Token> {
self.peek_whitespace_token_at::<1>()
}
pub fn peek_whitespace2(&mut self) -> Option<Token> {
self.peek_whitespace_token_at::<2>()
}
pub fn recent_span(&mut self) -> Span {
self.token_buffer.first().map(|x| x.span).unwrap_or(self.last_span)
}
pub fn last_span(&mut self) -> Span {
self.last_span
}
pub fn assert_finished(&mut self) -> ParseResult<()> {
let p = self.peek();
if p.kind != TokenKind::Eof {
bail!("Unexpected token `{}`, expected no more tokens",p.kind, @p.span);
}
Ok(())
}
pub fn eat(&mut self, token: TokenKind) -> bool {
let peek = self.peek();
if token == peek.kind {
self.token_buffer.pop();
self.last_span = peek.span;
true
} else {
false
}
}
pub fn eat_whitespace(&mut self, token: TokenKind) -> bool {
let Some(peek) = self.peek_whitespace() else {
return false;
};
if token == peek.kind {
self.token_buffer.pop();
self.last_span = peek.span;
true
} else {
false
}
}
fn expect_closing_delimiter(&mut self, kind: TokenKind, should_close: Span) -> ParseResult<()> {
let peek = self.peek();
if peek.kind != kind {
bail!("Unexpected token `{}` expected delimiter `{kind}`",
peek.kind,
@self.recent_span(),
@should_close => "expected this delimiter to close"
);
}
self.pop_peek();
Ok(())
}
pub fn backup_after(&mut self, span: Span) {
self.token_buffer.clear();
self.lexer.backup_after(span);
}
pub async fn parse_query(&mut self, stk: &mut Stk) -> ParseResult<sql::Ast> {
let statements = self.parse_stmt_list(stk).await?;
Ok(sql::Ast {
expressions: statements,
})
}
async fn parse_statement(&mut self, stk: &mut Stk) -> ParseResult<sql::TopLevelExpr> {
self.parse_top_level_expr(stk).await
}
pub(crate) async fn parse_expr(&mut self, stk: &mut Stk) -> ParseResult<sql::Expr> {
self.parse_expr_start(stk).await
}
pub fn lex_compound<F, R>(
&mut self,
start: Token,
f: F,
) -> Result<CompoundToken<R>, SyntaxError>
where
F: Fn(&mut Lexer, Token) -> Result<R, SyntaxError>,
{
let res = self.lexer.lex_compound(start, f)?;
self.last_span = res.span;
Ok(res)
}
pub fn span_str(&self, span: Span) -> &str {
self.lexer.span_str(span)
}
pub fn unescape_ident_span(&mut self, span: Span) -> Result<&str, SyntaxError> {
let str = self.lexer.span_str(span);
Lexer::unescape_ident_span(str, span, &mut self.unscape_buffer)
}
pub fn unescape_string_span(&mut self, span: Span) -> Result<&str, SyntaxError> {
let str = self.lexer.span_str(span);
Lexer::unescape_string_span(
str,
span,
&mut self.unscape_buffer,
self.settings.json_string_escapes,
)
}
pub fn unescape_regex_span(&mut self, span: Span) -> Result<&str, SyntaxError> {
let str = self.lexer.span_str(span);
Lexer::unescape_regex_span(str, span, &mut self.unscape_buffer)
}
pub(crate) async fn speculate<T, F>(&mut self, stk: &mut Stk, cb: F) -> ParseResult<Option<T>>
where
F: AsyncFnOnce(&mut Stk, &mut Parser) -> ParseResult<Option<T>>,
{
let backup = self.last_span();
match cb(stk, self).await {
Ok(Some(x)) => Ok(Some(x)),
Ok(None) => {
self.backup_after(backup);
Ok(None)
}
Err(e) => Err(e),
}
}
}
pub struct StatementStream {
stack: Stack,
settings: ParserSettings,
col_offset: usize,
line_offset: usize,
}
impl StatementStream {
#[expect(clippy::new_without_default)]
pub fn new() -> Self {
Self::new_with_settings(ParserSettings::default())
}
pub fn new_with_settings(settings: ParserSettings) -> Self {
StatementStream {
stack: Stack::new(),
settings,
col_offset: 0,
line_offset: 0,
}
}
fn accumulate_line_col(&mut self, bytes: &[u8]) {
let (line_num, remaining) = std::str::from_utf8(bytes)
.expect("parser validated utf8")
.lines()
.enumerate()
.last()
.unwrap_or((0, ""));
self.line_offset += line_num;
if line_num > 0 {
self.col_offset = 0;
}
self.col_offset += remaining.chars().count();
}
pub(crate) fn parse_partial(
&mut self,
buffer: &mut BytesMut,
) -> Result<Option<sql::TopLevelExpr>, RenderedError> {
let mut slice = &**buffer;
if slice.len() > u32::MAX as usize {
slice = &slice[..u32::MAX as usize];
}
let mut parser = Parser::new_with_settings(slice, self.settings.clone());
while parser.eat(t!(";")) {}
if parser.peek().span.offset != 0 && buffer.len() > u32::MAX as usize {
let eaten = buffer.split_to(parser.peek().span.offset as usize);
self.accumulate_line_col(&eaten);
slice = &**buffer;
if slice.len() > u32::MAX as usize {
slice = &slice[..u32::MAX as usize];
}
parser = Parser::new_with_settings(slice, self.settings.clone())
}
if parser.peek().is_eof() {
return Ok(None);
}
let res = self.stack.enter(|stk| parser.parse_statement(stk)).finish();
if parser.peek().is_eof() {
if buffer.len() > u32::MAX as usize {
let error = syntax_error!("Cannot parse query, statement exceeded maximum size of 4GB", @parser.last_span());
return Err(error
.render_on_bytes(buffer)
.offset_location(self.line_offset, self.col_offset));
}
return Ok(None);
}
if !parser.eat(t!(";")) {
let peek = parser.next();
if parser.peek1().is_eof() {
return Ok(None);
}
if let Err(e) = res {
return Err(e
.render_on_bytes(slice)
.offset_location(self.line_offset, self.col_offset));
}
let error = syntax_error!("Unexpected token `{}` expected the query to end.",peek.kind.as_str(),
@peek.span => "maybe forgot a semicolon after the previous statement?");
return Err(error
.render_on_bytes(slice)
.offset_location(self.line_offset, self.col_offset));
}
while parser.eat(t!(";")) {}
let eaten = buffer.split_to(parser.last_span().after_offset() as usize);
let res = res.map(Some).map_err(|e| {
e.render_on_bytes(&eaten).offset_location(self.line_offset, self.col_offset)
});
self.accumulate_line_col(&eaten);
res
}
pub(crate) fn parse_complete(
&mut self,
buffer: &mut BytesMut,
) -> Result<Option<sql::TopLevelExpr>, RenderedError> {
let mut slice = &**buffer;
if slice.len() > u32::MAX as usize {
slice = &slice[..u32::MAX as usize];
}
let mut parser = Parser::new_with_settings(slice, self.settings.clone());
while parser.eat(t!(";")) {}
if parser.peek().is_eof() {
buffer.clear();
return Ok(None);
}
match self.stack.enter(|stk| parser.parse_statement(stk)).finish() {
Ok(x) => {
if !parser.peek().is_eof() && !parser.eat(t!(";")) {
let peek = parser.peek();
let error = syntax_error!("Unexpected token `{}` expected the query to end.",peek.kind.as_str(),
@peek.span => "maybe forgot a semicolon after the previous statement?");
return Err(error
.render_on_bytes(slice)
.offset_location(self.line_offset, self.col_offset));
}
let eaten = buffer.split_to(parser.last_span().after_offset() as usize);
self.accumulate_line_col(&eaten);
Ok(Some(x))
}
Err(e) => {
Err(e.render_on_bytes(slice).offset_location(self.line_offset, self.col_offset))
}
}
}
}