Skip to main content

oak_regex/parser/
mod.rs

1use crate::{RegexLanguage, RegexSyntaxKind, lexer::RegexLexer};
2use oak_core::{
3    GreenNode, Source, TextEdit,
4    parser::{Associativity, ParseCache, ParseOutput, Parser, ParserState, Pratt, PrattParser, binary},
5};
6
7pub(crate) type State<'a, S> = ParserState<'a, RegexLanguage, S>;
8
9#[allow(missing_docs)]
10pub struct RegexParser<'config> {
11    /// Language configuration
12    pub(crate) config: &'config RegexLanguage,
13}
14
15impl<'config> RegexParser<'config> {
16    pub fn new(config: &'config RegexLanguage) -> Self {
17        Self { config }
18    }
19}
20
21impl<'config> Pratt<RegexLanguage> for RegexParser<'config> {
22    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RegexLanguage> {
23        use RegexSyntaxKind::*;
24        let cp = state.checkpoint();
25        match state.peek_kind() {
26            Some(Character) => {
27                state.bump();
28                state.finish_at(cp, RegexPattern.into())
29            }
30            Some(Dot) => {
31                state.bump();
32                state.finish_at(cp, RegexPattern.into())
33            }
34            Some(LParen) => {
35                state.bump();
36                PrattParser::parse(state, 0, self);
37                state.expect(RParen).ok();
38                state.finish_at(cp, RegexPattern.into())
39            }
40            Some(LBrack) => {
41                state.bump();
42                while state.not_at_end() && !state.at(RBrack) {
43                    state.advance();
44                }
45                state.expect(RBrack).ok();
46                state.finish_at(cp, RegexPattern.into())
47            }
48            _ => {
49                state.bump();
50                state.finish_at(cp, Error.into())
51            }
52        }
53    }
54
55    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RegexLanguage> {
56        use RegexSyntaxKind::*;
57        match state.peek_kind() {
58            Some(Hat) | Some(Dollar) => {
59                let cp = state.checkpoint();
60                state.bump();
61                state.finish_at(cp, RegexPattern.into())
62            }
63            Some(Backslash) => {
64                let cp = state.checkpoint();
65                state.bump();
66                state.advance();
67                state.finish_at(cp, RegexPattern.into())
68            }
69            _ => self.primary(state),
70        }
71    }
72
73    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, RegexLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, RegexLanguage>> {
74        use RegexSyntaxKind::*;
75        let kind = state.peek_kind()?;
76
77        let (prec, assoc) = match kind {
78            Pipe => (1, Associativity::Left),
79            Question | Star | Plus => (10, Associativity::Right),
80            _ => return None,
81        };
82
83        if prec < min_precedence {
84            return None;
85        }
86
87        match kind {
88            Pipe => Some(binary(state, left, Pipe, prec, assoc, RegexPattern.into(), |s, p| PrattParser::parse(s, p, self))),
89            Question | Star | Plus => {
90                let cp = state.checkpoint();
91                state.push_child(left);
92                state.bump();
93                Some(state.finish_at(cp, RegexPattern.into()))
94            }
95            _ => None,
96        }
97    }
98}
99
100impl<'config> Parser<RegexLanguage> for RegexParser<'config> {
101    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<RegexLanguage>) -> ParseOutput<'a, RegexLanguage> {
102        let lexer = RegexLexer::new(&self.config);
103        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| {
104            let checkpoint = state.checkpoint();
105            while state.not_at_end() {
106                PrattParser::parse(state, 0, self);
107            }
108            Ok(state.finish_at(checkpoint, RegexSyntaxKind::RegexPattern.into()))
109        })
110    }
111}