mod grammar;
use std::cell::Cell;
use crate::SyntaxKind;
use grammar::{parse_QueryUnit, parse_UpdateUnit};
use logos::Logos;
use rowan::{GreenNode, GreenNodeBuilder};
pub struct Parser {
tokens: Vec<Token>,
pos: usize,
fuel: Cell<u32>,
events: Vec<Event>,
}
#[derive(Debug, Clone)]
struct Token {
kind: SyntaxKind,
text: std::string::String,
}
impl Token {
fn is_trivia(&self) -> bool {
matches!(self.kind, SyntaxKind::WHITESPACE | SyntaxKind::Comment)
}
}
pub fn parse_text(input: &str, entry: TopEntryPoint) -> GreenNode {
let tokens = lex(input);
let parse_input = tokens
.iter()
.filter(|token| !token.is_trivia())
.cloned()
.collect();
let output = entry.parse(parse_input);
build_tree(tokens, output)
}
fn build_tree(tokens: Vec<Token>, events: Vec<Event>) -> GreenNode {
let mut tokens = tokens.into_iter().peekable();
let mut builder = GreenNodeBuilder::new();
for event in &events[..events.len() - 1] {
match event {
Event::Open { kind } => {
while *kind != SyntaxKind::QueryUnit
&& tokens.peek().map_or(false, |next| next.is_trivia())
{
let token = tokens.next().unwrap();
builder.token(token.kind.into(), &token.text);
}
builder.start_node((*kind).into());
}
Event::Close => {
builder.finish_node();
}
Event::Advance => {
while tokens.peek().map_or(false, |next| next.is_trivia()) {
let token = tokens.next().unwrap();
builder.token(token.kind.into(), &token.text);
}
let token = tokens.next().unwrap();
builder.token(token.kind.into(), &token.text);
}
}
}
assert!(matches!(events.last(), Some(Event::Close)));
while tokens.peek().map_or(false, |next| next.is_trivia()) {
let token = tokens.next().unwrap();
builder.token(token.kind.into(), &token.text);
}
builder.finish_node();
builder.finish()
}
impl Parser {
fn new(input: Vec<Token>) -> Self {
Self {
tokens: input,
pos: 0,
fuel: 256.into(),
events: Vec::new(),
}
}
}
enum Event {
Open { kind: SyntaxKind },
Close,
Advance,
}
struct MarkOpened {
index: usize,
}
impl Parser {
fn open(&mut self) -> MarkOpened {
let mark = MarkOpened {
index: self.events.len(),
};
self.events.push(Event::Open {
kind: SyntaxKind::Error,
});
mark
}
fn close(&mut self, m: MarkOpened, kind: SyntaxKind) {
self.events[m.index] = Event::Open { kind };
self.events.push(Event::Close);
}
fn advance(&mut self) {
assert!(!self.eof());
self.fuel.set(256);
self.events.push(Event::Advance);
self.pos += 1;
}
fn eof(&self) -> bool {
self.pos == self.tokens.len()
}
fn nth(&self, lookahead: usize) -> SyntaxKind {
if self.fuel.get() == 0 {
panic!("parser is stuck")
}
self.fuel.set(self.fuel.get() - 1);
self.tokens
.get(self.pos + lookahead)
.map_or(SyntaxKind::Eof, |it| it.kind)
}
fn at(&self, kind: SyntaxKind) -> bool {
self.nth(0) == kind
}
fn at_any(&self, kinds: &[SyntaxKind]) -> bool {
kinds.iter().any(|kind| self.at(*kind))
}
fn eat(&mut self, kind: SyntaxKind) -> bool {
if self.at(kind) {
self.advance();
true
} else {
false
}
}
fn expect(&mut self, kind: SyntaxKind) {
if self.eat(kind) {
return;
}
eprintln!("expected {kind:?}");
}
fn advance_with_error(&mut self, error: &str) {
let m = self.open();
eprintln!("{error}");
self.advance();
self.close(m, SyntaxKind::Error);
}
}
#[derive(Debug)]
pub enum TopEntryPoint {
QueryUnit,
UpdateUnit,
}
impl TopEntryPoint {
fn parse(&self, input: Vec<Token>) -> Vec<Event> {
let mut parser = Parser::new(input);
match self {
TopEntryPoint::QueryUnit => parse_QueryUnit(&mut parser),
TopEntryPoint::UpdateUnit => parse_UpdateUnit(&mut parser),
}
parser.events
}
}
fn lex(text: &str) -> Vec<Token> {
let mut lexer = SyntaxKind::lexer(text);
let mut tokens = Vec::new();
while let Some(result) = lexer.next() {
tokens.push(Token {
kind: result.unwrap_or(SyntaxKind::Error),
text: lexer.slice().to_string(),
});
}
tokens
}
pub fn guess_operation_type(input: &str) -> Option<TopEntryPoint> {
let tokens = lex(input);
tokens.iter().find_map(|token| match token.kind {
SyntaxKind::SELECT | SyntaxKind::CONSTRUCT | SyntaxKind::ASK | SyntaxKind::DESCRIBE => {
Some(TopEntryPoint::QueryUnit)
}
SyntaxKind::LOAD
| SyntaxKind::CLEAR
| SyntaxKind::DROP
| SyntaxKind::CREATE
| SyntaxKind::ADD
| SyntaxKind::MOVE
| SyntaxKind::COPY
| SyntaxKind::INSERT
| SyntaxKind::INSERT_DATA
| SyntaxKind::DELETE
| SyntaxKind::DELETE_DATA
| SyntaxKind::DELETE_WHERE
| SyntaxKind::USING => Some(TopEntryPoint::UpdateUnit),
_ => None,
})
}
#[cfg(test)]
mod tests;