use rudb_common::{Error, Result};
use crate::generated::keywords::{KEYWORDS, UNRESERVED};
use crate::generated::rules::{CHILDREN, FIRST, NODES, NULLABLE, PROGRAM, RULES, SYMBOLS};
use crate::rules::{Node, Op, Suggestion};
use crate::token::{Flags, Kind, Token};
use crate::tokenize::tokenize;
pub const NONE: u32 = u32::MAX;
const MAX_DEPTH: usize = 262_144;
const MEMO_EMPTY: u32 = u32::MAX;
const MEMO_FAILED: u32 = u32::MAX - 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParseNode {
pub rule: u32,
pub start: u32,
pub end: u32,
pub first_child: u32,
pub next_sibling: u32,
}
#[derive(Debug, Clone)]
pub struct Tree {
nodes: Vec<ParseNode>,
root: u32,
steps: u64,
}
impl Tree {
pub fn root(&self) -> u32 {
self.root
}
pub fn arena_len(&self) -> usize {
self.nodes.len()
}
pub fn steps(&self) -> u64 {
self.steps
}
pub fn node(&self, index: u32) -> ParseNode {
self.nodes[index as usize]
}
pub fn name(&self, index: u32) -> &'static str {
RULES[self.node(index).rule as usize].name
}
pub fn children(&self, index: u32) -> Children<'_> {
Children { tree: self, next: self.node(index).first_child }
}
pub fn text<'a>(&self, index: u32, query: &'a str, tokens: &[Token]) -> &'a str {
let node = self.node(index);
if node.end <= node.start {
let at = tokens.get(node.start as usize).map_or(query.len(), |t| t.start as usize);
return &query[at..at];
}
let start = tokens[node.start as usize].start as usize;
let end = tokens[node.end as usize - 1].end as usize;
&query[start..end]
}
}
#[derive(Debug)]
pub struct Children<'a> {
tree: &'a Tree,
next: u32,
}
impl Iterator for Children<'_> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.next == NONE {
return None;
}
let current = self.next;
self.next = self.tree.node(current).next_sibling;
Some(current)
}
}
pub fn parse(query: &str) -> Result<Tree> {
let tokens = tokenize(query)?;
parse_tokens(query, &tokens, PROGRAM, true)
}
pub fn parse_from(query: &str, rule_name: &str, filter: bool) -> Result<Tree> {
let index = RULES
.binary_search_by(|candidate| candidate.name.cmp(rule_name))
.map_err(|_| Error::parser(format!("no rule named {rule_name}")))?;
let tokens = tokenize(query)?;
parse_tokens(query, &tokens, index as u32, filter)
}
pub fn parse_tokens(query: &str, tokens: &[Token], root: u32, filter: bool) -> Result<Tree> {
Matcher::new(query, tokens, filter).run(root)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FrameOp {
Rule,
Sequence,
Choice,
Optional,
Repeat,
}
#[derive(Debug, Clone, Copy)]
struct Frame {
op: FrameOp,
a: u32,
b: u32,
start: u32,
step: u32,
mark: u32,
head: u32,
tail: u32,
}
enum Action {
Enter(u32),
Succeed(u32, u32),
Fail,
Done(Option<u32>),
}
struct Matcher<'a> {
query: &'a str,
tokens: &'a [Token],
keys: Vec<u64>,
arena: Vec<ParseNode>,
stack: Vec<Frame>,
memo: Vec<u32>,
slot_of: &'static [u32],
filter: bool,
pos: u32,
steps: u64,
furthest: u32,
}
fn slots() -> &'static (Box<[u32]>, usize) {
use std::sync::OnceLock;
static SLOTS: OnceLock<(Box<[u32]>, usize)> = OnceLock::new();
SLOTS.get_or_init(build_slots)
}
fn build_slots() -> (Box<[u32]>, usize) {
let mut slots = vec![NONE; RULES.len()];
let mut next = 0;
for (index, rule) in RULES.iter().enumerate() {
if rule.memoized {
slots[index] = next;
next += 1;
}
}
(slots.into_boxed_slice(), next as usize)
}
impl<'a> Matcher<'a> {
fn new(query: &'a str, tokens: &'a [Token], filter: bool) -> Self {
let keys = tokens.iter().map(|token| crate::rules::token_key(*token)).collect();
let memo = vec![MEMO_EMPTY; slots().1 * (tokens.len() + 1)];
Self {
query,
tokens,
keys,
arena: Vec::with_capacity(tokens.len()),
stack: Vec::with_capacity(64),
memo,
slot_of: &slots().0,
filter,
pos: 0,
steps: 0,
furthest: 0,
}
}
fn run(mut self, root: u32) -> Result<Tree> {
self.push(Frame {
op: FrameOp::Rule,
a: root,
b: 0,
start: 0,
step: 0,
mark: 0,
head: NONE,
tail: NONE,
})?;
let mut action = Action::Enter(RULES[root as usize].root);
let node = loop {
action = match action {
Action::Enter(node) => self.enter(node)?,
Action::Succeed(head, tail) => self.settle_ok(head, tail),
Action::Fail => self.settle_fail(),
Action::Done(result) => match result {
Some(node) => break node,
None => return Err(self.syntax_error(self.furthest)),
},
};
};
if (self.pos as usize) < self.tokens.len()
&& self.tokens[self.pos as usize].kind != Kind::EndOfInput
{
return Err(self.syntax_error(self.pos.max(self.furthest)));
}
Ok(Tree { nodes: self.arena, root: node, steps: self.steps })
}
fn token(&self, pos: u32) -> Token {
self.tokens.get(pos as usize).copied().unwrap_or(Token {
kind: Kind::EndOfInput,
flags: Flags::default(),
keyword: crate::token::NOT_A_KEYWORD,
start: self.query.len() as u32,
end: self.query.len() as u32,
})
}
fn key(&self, pos: u32) -> u64 {
self.keys.get(pos as usize).copied().unwrap_or(crate::rules::FIRST_END)
}
fn push(&mut self, frame: Frame) -> Result<()> {
if self.stack.len() >= MAX_DEPTH {
let token = self.token(self.pos);
return Err(Error::parser(format!(
"memory exhausted at or near \"{}\"",
token.text(self.query)
))
.with_span(token.span()));
}
self.stack.push(frame);
Ok(())
}
fn alloc(&mut self, node: ParseNode) -> u32 {
self.arena.push(node);
(self.arena.len() - 1) as u32
}
fn reached(&mut self, pos: u32) {
if pos > self.furthest {
self.furthest = pos;
}
}
fn syntax_error(&self, pos: u32) -> Error {
let token = self.token(pos);
if token.kind == Kind::EndOfInput {
return Error::parser("syntax error at end of input").with_span(token.span());
}
Error::parser(format!("syntax error at or near \"{}\"", token.text(self.query)))
.with_span(token.span())
}
fn enter(&mut self, index: u32) -> Result<Action> {
self.steps += 1;
if self.filter
&& !NULLABLE[index as usize]
&& FIRST[index as usize] & self.key(self.pos) == 0
{
self.reached(self.pos);
return Ok(Action::Fail);
}
let node = NODES[index as usize];
match node.op {
Op::Rule => self.enter_rule(node.a),
Op::Sequence => {
self.push(self.frame(FrameOp::Sequence, node.a, node.b))?;
Ok(Action::Enter(CHILDREN[node.a as usize]))
}
Op::Choice => {
self.push(self.frame(FrameOp::Choice, node.a, node.b))?;
Ok(Action::Enter(CHILDREN[node.a as usize]))
}
Op::Optional => {
self.push(self.frame(FrameOp::Optional, node.a, 0))?;
Ok(Action::Enter(node.a))
}
Op::Repeat => {
self.push(self.frame(FrameOp::Repeat, node.a, 0))?;
Ok(Action::Enter(node.a))
}
_ => Ok(self.terminal(node)),
}
}
fn frame(&self, op: FrameOp, a: u32, b: u32) -> Frame {
Frame { op, a, b, start: self.pos, step: 0, mark: self.pos, head: NONE, tail: NONE }
}
fn enter_rule(&mut self, rule: u32) -> Result<Action> {
let slot = self.slot_of[rule as usize];
if slot != NONE {
match self.memo[self.memo_index(slot)] {
MEMO_EMPTY => {}
MEMO_FAILED => return Ok(Action::Fail),
stored => {
let source = self.arena[stored as usize];
self.pos = source.end;
let copy = self.alloc(ParseNode { next_sibling: NONE, ..source });
return Ok(Action::Succeed(copy, copy));
}
}
}
self.push(self.frame(FrameOp::Rule, rule, 0))?;
Ok(Action::Enter(RULES[rule as usize].root))
}
fn memo_index(&self, slot: u32) -> usize {
slot as usize * (self.tokens.len() + 1) + self.pos as usize
}
fn settle_ok(&mut self, head: u32, tail: u32) -> Action {
let Some(mut frame) = self.stack.pop() else {
return Action::Done(Some(head));
};
if head != NONE {
if frame.head == NONE {
frame.head = head;
} else {
self.arena[frame.tail as usize].next_sibling = head;
}
frame.tail = tail;
}
match frame.op {
FrameOp::Rule => {
let node = self.alloc(ParseNode {
rule: frame.a,
start: frame.start,
end: self.pos,
first_child: frame.head,
next_sibling: NONE,
});
self.remember(frame.a, frame.start, node);
Action::Succeed(node, node)
}
FrameOp::Sequence => {
frame.step += 1;
if frame.step == frame.b {
Action::Succeed(frame.head, frame.tail)
} else {
let next = CHILDREN[(frame.a + frame.step) as usize];
self.stack.push(frame);
Action::Enter(next)
}
}
FrameOp::Choice | FrameOp::Optional => Action::Succeed(frame.head, frame.tail),
FrameOp::Repeat => {
debug_assert!(self.pos != frame.mark, "a repeat went round without consuming");
if self.pos == frame.mark {
return Action::Succeed(frame.head, frame.tail);
}
frame.mark = self.pos;
frame.step += 1;
let child = frame.a;
self.stack.push(frame);
Action::Enter(child)
}
}
}
fn settle_fail(&mut self) -> Action {
let Some(mut frame) = self.stack.pop() else {
return Action::Done(None);
};
match frame.op {
FrameOp::Rule => {
self.pos = frame.start;
self.remember(frame.a, frame.start, MEMO_FAILED);
Action::Fail
}
FrameOp::Sequence => {
self.pos = frame.start;
Action::Fail
}
FrameOp::Choice => {
frame.step += 1;
self.pos = frame.start;
if frame.step == frame.b {
Action::Fail
} else {
frame.head = NONE;
frame.tail = NONE;
let next = CHILDREN[(frame.a + frame.step) as usize];
self.stack.push(frame);
Action::Enter(next)
}
}
FrameOp::Optional => {
self.pos = frame.start;
Action::Succeed(NONE, NONE)
}
FrameOp::Repeat => {
self.pos = frame.mark;
if frame.step == 0 { Action::Fail } else { Action::Succeed(frame.head, frame.tail) }
}
}
}
fn remember(&mut self, rule: u32, start: u32, entry: u32) {
let slot = self.slot_of[rule as usize];
if slot != NONE {
let index = slot as usize * (self.tokens.len() + 1) + start as usize;
self.memo[index] = entry;
}
}
fn terminal(&mut self, node: Node) -> Action {
self.reached(self.pos);
if self.pos as usize >= self.tokens.len() {
return Action::Fail;
}
let token = self.tokens[self.pos as usize];
let matched = match node.op {
Op::Keyword => u32::from(token.keyword) == node.a,
Op::KeywordClass => {
token.kind == Kind::Keyword && u32::from(class_of(token)) & node.a != 0
}
Op::Symbol => token.text(self.query) == SYMBOLS[node.a as usize],
Op::Number => token.kind == Kind::Number && token.text(self.query) != ".",
Op::Operator => {
token.kind == Kind::Operator && is_bare_operator(token.text(self.query))
}
Op::EndOfInput => token.kind == Kind::EndOfInput,
Op::String => return self.string(token),
Op::Identifier => self.identifier(token, node),
other => unreachable!("{other:?} is a composite and never reaches here"),
};
if matched {
self.pos += 1;
Action::Succeed(NONE, NONE)
} else {
Action::Fail
}
}
fn string(&mut self, token: Token) -> Action {
if token.kind != Kind::String {
return Action::Fail;
}
self.pos += 1;
if !is_plain_string(token.text(self.query)) {
return Action::Succeed(NONE, NONE);
}
while let Some(next) = self.tokens.get(self.pos as usize) {
if next.kind != Kind::String
|| !next.flags.has(Flags::NEWLINE)
|| next.flags.has(Flags::BLOCK_COMMENT)
|| !is_plain_string(next.text(self.query))
{
break;
}
self.pos += 1;
}
Action::Succeed(NONE, NONE)
}
fn identifier(&mut self, token: Token, node: Node) -> bool {
let suggestion = SUGGESTIONS[node.a as usize];
let shaped = match token.kind {
Kind::QuotedIdentifier => true,
Kind::Identifier | Kind::Keyword => true,
Kind::String => {
suggestion.supports_string_literal() && is_plain_string(token.text(self.query))
}
_ => false,
};
if !shaped {
return false;
}
if node.flags & Node::RESERVED != 0 {
return true;
}
if token.kind != Kind::Keyword {
return true;
}
let class = class_of(token);
class & UNRESERVED != 0 || class & suggestion.allowed_class() != 0
}
}
fn class_of(token: Token) -> u8 {
KEYWORDS[token.keyword as usize].1
}
fn is_plain_string(text: &str) -> bool {
text.starts_with('\'')
}
const OPERATOR_CHARACTERS: &[u8] = b"+-*/%^<>=~!@&|";
const NOT_OPERATORS: [&str; 15] = [
"->", "->>", "<=", ">=", "!=", "==", "<>", "~~", "~~*", "~~~", "~*", "!~~", "!~~*", "!~", "!~*",
];
fn is_bare_operator(text: &str) -> bool {
if text.len() < 2 || NOT_OPERATORS.contains(&text) {
return false;
}
text.bytes().all(|byte| OPERATOR_CHARACTERS.contains(&byte))
}
const SUGGESTIONS: [Suggestion; 11] = [
Suggestion::Variable,
Suggestion::CatalogName,
Suggestion::SchemaName,
Suggestion::TableName,
Suggestion::ColumnName,
Suggestion::ScalarFunctionName,
Suggestion::TableFunctionName,
Suggestion::TypeName,
Suggestion::PragmaName,
Suggestion::SettingName,
Suggestion::FileName,
];
#[cfg(test)]
mod tests {
use super::{
NONE, SUGGESTIONS, Tree, is_bare_operator, is_plain_string, parse, parse_from, parse_tokens,
};
use crate::corpus::CORPUS;
use crate::generated::rules::PROGRAM;
use crate::tokenize::tokenize;
fn names(tree: &Tree, node: u32, into: &mut Vec<&'static str>) {
into.push(tree.name(node));
for child in tree.children(node) {
names(tree, child, into);
}
}
fn find(tree: &Tree, node: u32, name: &str) -> Option<u32> {
if tree.name(node) == name {
return Some(node);
}
tree.children(node).find_map(|child| find(tree, child, name))
}
fn shape(query: &str) -> Vec<&'static str> {
let tree = parse(query).expect("parses");
let mut out = Vec::new();
names(&tree, tree.root(), &mut out);
out
}
#[test]
fn the_suggestions_are_dense_and_in_order() {
for (index, suggestion) in SUGGESTIONS.iter().enumerate() {
assert_eq!(*suggestion as usize, index);
}
}
#[test]
fn an_empty_script_parses() {
let tree = parse("").expect("an empty script is a script with no statements");
assert_eq!(tree.name(tree.root()), "Program");
}
#[test]
fn a_select_parses_and_the_root_is_the_program() {
let tree = parse("SELECT 1").expect("parses");
assert_eq!(tree.name(tree.root()), "Program");
let statements: Vec<_> = tree.children(tree.root()).collect();
assert_eq!(statements.len(), 1);
assert_eq!(tree.name(statements[0]), "TopLevelStatement");
}
#[test]
fn the_shape_has_the_rules_the_grammar_names() {
let shape = shape("SELECT 1");
assert!(shape.contains(&"SelectStatement"), "{shape:?}");
}
#[test]
fn a_statement_covers_the_text_it_came_from() {
let query = " SELECT 1 ";
let tokens = tokenize(query).expect("tokenizes");
let tree = parse_tokens(query, &tokens, PROGRAM, true).expect("parses");
let statement = find(&tree, tree.root(), "SelectStatement").expect("there is one");
assert_eq!(tree.text(statement, query, &tokens), "SELECT 1");
}
#[test]
fn several_statements_parse_as_several() {
let tree = parse("SELECT 1; SELECT 2; SELECT 3").expect("parses");
let shape = shape("SELECT 1; SELECT 2; SELECT 3");
assert_eq!(shape.iter().filter(|name| **name == "SelectStatement").count(), 3);
assert!(tree.children(tree.root()).count() >= 3);
}
#[test]
fn a_trailing_semicolon_makes_an_empty_statement() {
let one = parse("SELECT 1").expect("parses");
let two = parse("SELECT 1;").expect("parses");
assert_eq!(one.children(one.root()).count(), 1);
assert_eq!(two.children(two.root()).count(), 2);
let last = two.children(two.root()).last().expect("there is a last one");
let inside: Vec<_> = two.children(last).map(|child| two.name(child)).collect();
assert_eq!(inside, ["EndOfInput"], "the extra one holds no statement");
}
#[test]
fn rubbish_after_a_statement_is_an_error() {
let error = parse("SELECT 1 rubbish here").expect_err("not a query");
assert!(error.message().starts_with("syntax error at or near"), "{}", error.message());
}
#[test]
fn a_word_that_is_not_a_statement_is_an_error() {
let error = parse("SELCT 1").expect_err("not a query");
assert!(error.message().contains("syntax error"), "{}", error.message());
}
#[test]
fn the_error_points_at_the_furthest_token_reached() {
let error = parse("SELECT 1 FROM").expect_err("not a query");
assert!(error.span().is_some(), "an error about a place should say which place");
}
#[test]
fn a_soft_keyword_is_a_column_name_and_also_a_keyword() {
parse("SELECT ascending FROM t").expect("a soft word is a name");
parse("SELECT x FROM t ORDER BY x ASCENDING").expect("a soft word is also a literal");
}
#[test]
fn a_reserved_word_is_not_a_column_name() {
parse("SELECT x FROM t").expect("an ordinary name is fine");
parse("SELECT * FROM t WHERE all").expect_err("`all` is reserved");
}
#[test]
fn an_unreserved_word_is_a_column_name_everywhere() {
parse("SELECT abort FROM t").expect("`abort` is unreserved");
}
#[test]
fn a_function_name_keyword_is_a_function_and_not_a_column() {
parse("SELECT binary(x) FROM t").expect("a function name position takes it");
parse("SELECT binary FROM t").expect_err("a column name position does not");
}
#[test]
fn a_quoted_name_is_a_name_whatever_it_spells() {
parse(r#"SELECT "all" FROM t"#).expect("quoting takes a word out of every class");
}
#[test]
fn adjacent_strings_across_a_line_are_one_literal() {
parse("SELECT 'a'\n'b'").expect("a continuation");
parse("SELECT 'a' 'b'").expect_err("on one line they are two strings and a syntax error");
}
#[test]
fn deep_nesting_is_an_error_and_not_a_crash() {
let query = format!("SELECT {}1{}", "(".repeat(200_000), ")".repeat(200_000));
let error = parse(&query).expect_err("too deep to parse");
assert!(error.message().contains("memory exhausted"), "{}", error.message());
}
#[test]
fn nesting_that_is_merely_rude_still_parses() {
let query = format!("SELECT {}1{}", "(".repeat(500), ")".repeat(500));
parse(&query).expect("five hundred deep is fine");
}
#[test]
fn a_named_rule_can_be_parsed_on_its_own() {
let tree = parse_from("SELECT 1", "SelectStatement", true).expect("parses");
assert_eq!(tree.name(tree.root()), "SelectStatement");
}
#[test]
fn asking_for_a_rule_that_does_not_exist_says_so() {
let error = parse_from("SELECT 1", "NoSuchRule", true).expect_err("no such rule");
assert!(error.message().contains("NoSuchRule"));
}
#[test]
fn the_children_of_a_leaf_rule_are_none() {
let tree = parse("SELECT 1").expect("parses");
let mut leaves = 0;
for index in 0..tree.arena_len() as u32 {
if tree.node(index).first_child == NONE {
leaves += 1;
}
}
assert!(leaves > 0, "every tree has leaves");
}
#[test]
fn what_counts_as_a_bare_operator() {
for text in ["&&", "@>", "<@", "||", "^@", "<<", ">>", "//", "**", "<<=", ">>="] {
assert!(is_bare_operator(text), "{text} should be an operator");
}
for text in ["->", "->>", "<=", ">=", "!=", "==", "<>", "~~", "!~~*"] {
assert!(!is_bare_operator(text), "{text} is spelled by a rule");
}
for text in ["::", ":=", "+", "(", ","] {
assert!(!is_bare_operator(text), "{text} is not an operator");
}
}
#[test]
fn the_corpus_parses() {
for query in CORPUS {
parse(query).unwrap_or_else(|error| panic!("{query}\n {}", error.message()));
}
}
#[test]
fn the_corpus_parses_the_same_with_the_filter_off() {
for query in CORPUS {
let filtered = parse_from(query, "Program", true).expect("parses");
let plain = parse_from(query, "Program", false).expect("parses unfiltered");
let mut a = Vec::new();
let mut b = Vec::new();
names(&filtered, filtered.root(), &mut a);
names(&plain, plain.root(), &mut b);
assert_eq!(a, b, "{query} parsed differently with the filter on");
}
}
#[test]
fn the_work_stays_proportional_to_the_query() {
for query in CORPUS {
let tree = parse(query).expect("parses");
let tokens = tokenize(query).expect("tokenizes").len() as u64;
let per_token = tree.steps() / tokens;
assert!(per_token < 400, "{query} took {per_token} steps a token");
}
}
#[test]
fn what_counts_as_a_plain_string() {
assert!(is_plain_string("'a'"));
assert!(!is_plain_string("E'a'"));
assert!(!is_plain_string("$$a$$"));
assert!(!is_plain_string(r#""a""#));
}
}