Skip to main content

ll_sparql_parser/parser/
mod.rs

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