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