ll_sparql_parser/parser/
mod.rs

1mod grammar;
2
3use std::cell::Cell;
4
5use crate::SyntaxKind;
6use grammar::{parse_QueryUnit, parse_UpdateUnit};
7use logos::Logos;
8use rowan::{GreenNode, GreenNodeBuilder};
9use wasm_bindgen::prelude::wasm_bindgen;
10
11pub struct Parser {
12    tokens: Vec<Token>,
13    pos: usize,
14    fuel: Cell<u32>,
15    events: Vec<Event>,
16}
17
18#[derive(Debug, Clone)]
19pub(super) struct Token {
20    kind: SyntaxKind,
21    text: std::string::String,
22}
23
24impl Token {
25    fn is_trivia(&self) -> bool {
26        matches!(self.kind, SyntaxKind::WHITESPACE | SyntaxKind::Comment)
27    }
28
29    pub(super) fn kind(&self) -> SyntaxKind {
30        self.kind
31    }
32}
33
34pub fn parse_text(input: &str, entry: TopEntryPoint) -> GreenNode {
35    let tokens = lex(input);
36    let parse_input = tokens
37        .iter()
38        .filter(|token| !token.is_trivia())
39        .cloned()
40        .collect();
41    let output = entry.parse(parse_input);
42    build_tree(tokens, output)
43}
44
45fn build_tree(tokens: Vec<Token>, events: Vec<Event>) -> GreenNode {
46    let mut tokens = tokens.into_iter().peekable();
47    let mut builder = GreenNodeBuilder::new();
48
49    // Special case: pop the last `Close` event to ensure
50    // that the stack is non-empty inside the loop.
51    // assert!(matches!(events.pop(), Some(Event::Close)));
52    for event in &events[..events.len() - 1] {
53        match event {
54            Event::Open { kind } => {
55                while !matches!(kind, SyntaxKind::QueryUnit | SyntaxKind::UpdateUnit)
56                    && tokens.peek().map_or(false, |next| next.is_trivia())
57                {
58                    let token = tokens.next().unwrap();
59                    builder.token(token.kind.into(), &token.text);
60                }
61                builder.start_node((*kind).into());
62            }
63            Event::Close => {
64                builder.finish_node();
65            }
66
67            Event::Advance => {
68                while tokens.peek().map_or(false, |next| next.is_trivia()) {
69                    let token = tokens.next().unwrap();
70                    builder.token(token.kind.into(), &token.text);
71                }
72                let token = tokens.next().unwrap();
73                builder.token(token.kind.into(), &token.text);
74            }
75        }
76    }
77    // Eat trailing trivia tokens
78    assert!(matches!(events.last(), Some(Event::Close)));
79    while tokens.peek().map_or(false, |next| next.is_trivia()) {
80        let token = tokens.next().unwrap();
81        builder.token(token.kind.into(), &token.text);
82    }
83    builder.finish_node();
84    builder.finish()
85}
86
87impl Parser {
88    fn new(input: Vec<Token>) -> Self {
89        Self {
90            tokens: input,
91            pos: 0,
92            fuel: 256.into(),
93            events: Vec::new(),
94        }
95    }
96}
97
98enum Event {
99    Open { kind: SyntaxKind },
100    Close,
101    Advance,
102}
103
104struct MarkOpened {
105    index: usize,
106}
107
108impl Parser {
109    fn open(&mut self) -> MarkOpened {
110        let mark = MarkOpened {
111            index: self.events.len(),
112        };
113        self.events.push(Event::Open {
114            kind: SyntaxKind::Error,
115        });
116        mark
117    }
118
119    fn close(&mut self, m: MarkOpened, kind: SyntaxKind) {
120        self.events[m.index] = Event::Open { kind };
121        self.events.push(Event::Close);
122    }
123
124    fn advance(&mut self) {
125        assert!(!self.eof());
126        self.fuel.set(256);
127        self.events.push(Event::Advance);
128        self.pos += 1;
129    }
130
131    fn eof(&self) -> bool {
132        self.pos == self.tokens.len()
133    }
134
135    fn nth(&self, lookahead: usize) -> SyntaxKind {
136        if self.fuel.get() == 0 {
137            panic!("parser is stuck")
138        }
139        self.fuel.set(self.fuel.get() - 1);
140        self.tokens
141            .get(self.pos + lookahead)
142            .map_or(SyntaxKind::Eof, |it| it.kind)
143    }
144
145    fn at(&self, kind: SyntaxKind) -> bool {
146        self.nth(0) == kind
147    }
148
149    fn at_any(&self, kinds: &[SyntaxKind]) -> bool {
150        kinds.iter().any(|kind| self.at(*kind))
151    }
152
153    fn eat(&mut self, kind: SyntaxKind) -> bool {
154        if self.at(kind) {
155            self.advance();
156            true
157        } else {
158            false
159        }
160    }
161
162    fn expect(&mut self, kind: SyntaxKind) {
163        if self.eat(kind) {
164            return;
165        }
166        // TODO: Error reporting.
167        eprintln!("expected {kind:?}");
168    }
169
170    fn advance_with_error(&mut self, error: &str) {
171        let m = self.open();
172        // TODO: Error reporting.
173        eprintln!("{error}");
174        self.advance();
175        self.close(m, SyntaxKind::Error);
176    }
177}
178
179#[derive(Debug)]
180pub enum TopEntryPoint {
181    QueryUnit,
182    UpdateUnit,
183}
184
185impl TopEntryPoint {
186    fn parse(&self, input: Vec<Token>) -> Vec<Event> {
187        let mut parser = Parser::new(input);
188        match self {
189            TopEntryPoint::QueryUnit => parse_QueryUnit(&mut parser),
190            TopEntryPoint::UpdateUnit => parse_UpdateUnit(&mut parser),
191        }
192        parser.events
193    }
194}
195
196pub(super) fn lex(text: &str) -> Vec<Token> {
197    let mut lexer = SyntaxKind::lexer(text);
198    let mut tokens = Vec::new();
199
200    while let Some(result) = lexer.next() {
201        tokens.push(Token {
202            kind: result.unwrap_or(SyntaxKind::Error),
203            text: lexer.slice().to_string(),
204        });
205    }
206    tokens
207}
208
209pub fn guess_operation_type(input: &str) -> Option<TopEntryPoint> {
210    let tokens = lex(input);
211    tokens.iter().find_map(|token| match token.kind {
212        SyntaxKind::SELECT | SyntaxKind::CONSTRUCT | SyntaxKind::ASK | SyntaxKind::DESCRIBE => {
213            Some(TopEntryPoint::QueryUnit)
214        }
215        SyntaxKind::LOAD
216        | SyntaxKind::CLEAR
217        | SyntaxKind::DROP
218        | SyntaxKind::CREATE
219        | SyntaxKind::ADD
220        | SyntaxKind::MOVE
221        | SyntaxKind::COPY
222        | SyntaxKind::INSERT
223        | SyntaxKind::INSERT_DATA
224        | SyntaxKind::DELETE
225        | SyntaxKind::DELETE_DATA
226        | SyntaxKind::DELETE_WHERE => Some(TopEntryPoint::UpdateUnit),
227        _ => None,
228    })
229}
230
231#[wasm_bindgen]
232pub fn determine_operation_type(input: &str) -> String {
233    match guess_operation_type(input) {
234        Some(TopEntryPoint::QueryUnit) => "Query",
235        Some(TopEntryPoint::UpdateUnit) => "Update",
236        None => "Unknown",
237    }
238    .to_string()
239}
240
241#[cfg(test)]
242mod tests;