Skip to main content

oak_nginx/parser/
mod.rs

1use crate::{kind::NginxSyntaxKind, language::NginxLanguage, lexer::NginxLexer};
2use oak_core::{
3    GreenNode, OakError,
4    parser::{ParseCache, ParseOutput, Parser, ParserState},
5    source::{Source, TextEdit},
6};
7
8pub struct NginxParser<'a> {
9    pub language: &'a NginxLanguage,
10}
11
12impl<'a> NginxParser<'a> {
13    pub fn new(language: &'a NginxLanguage) -> Self {
14        Self { language }
15    }
16
17    fn parse_directive<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, NginxLanguage, S>) {
18        if state.at(NginxSyntaxKind::CommentToken) {
19            let checkpoint = state.checkpoint();
20            state.bump();
21            state.finish_at(checkpoint, NginxSyntaxKind::Comment.into());
22            return;
23        }
24
25        let is_block_directive = matches!(state.peek_kind(), Some(NginxSyntaxKind::HttpKeyword | NginxSyntaxKind::ServerKeyword | NginxSyntaxKind::LocationKeyword | NginxSyntaxKind::EventsKeyword | NginxSyntaxKind::UpstreamKeyword));
26
27        if is_block_directive {
28            self.parse_block(state);
29        }
30        else {
31            let checkpoint = state.checkpoint();
32            state.bump(); // directive name
33            while state.not_at_end() && !state.at(NginxSyntaxKind::Semicolon) && !state.at(NginxSyntaxKind::LeftBrace) {
34                let p_checkpoint = state.checkpoint();
35                state.bump();
36                state.finish_at(p_checkpoint, NginxSyntaxKind::Parameter.into());
37            }
38            if state.at(NginxSyntaxKind::Semicolon) {
39                state.bump();
40            }
41            state.finish_at(checkpoint, NginxSyntaxKind::Directive.into());
42        }
43    }
44
45    fn parse_block<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, NginxLanguage, S>) {
46        let checkpoint = state.checkpoint();
47        state.bump(); // block keyword (http, server, etc.)
48
49        // Optional parameters for location or upstream
50        while state.not_at_end() && !state.at(NginxSyntaxKind::LeftBrace) {
51            let p_checkpoint = state.checkpoint();
52            state.bump();
53            state.finish_at(p_checkpoint, NginxSyntaxKind::Parameter.into());
54        }
55
56        if state.at(NginxSyntaxKind::LeftBrace) {
57            state.bump();
58            while state.not_at_end() && !state.at(NginxSyntaxKind::RightBrace) {
59                self.parse_directive(state);
60            }
61            if state.at(NginxSyntaxKind::RightBrace) {
62                state.bump();
63            }
64        }
65        state.finish_at(checkpoint, NginxSyntaxKind::Block.into());
66    }
67
68    fn parse_root_internal<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, NginxLanguage, S>) -> Result<&'b GreenNode<'b, NginxLanguage>, OakError> {
69        let checkpoint = state.checkpoint();
70        while state.not_at_end() {
71            self.parse_directive(state);
72        }
73
74        Ok(state.finish_at(checkpoint, NginxSyntaxKind::Root.into()))
75    }
76}
77
78impl<'a> Parser<NginxLanguage> for NginxParser<'a> {
79    fn parse<'b, S: Source + ?Sized>(&self, text: &'b S, edits: &[TextEdit], cache: &'b mut impl ParseCache<NginxLanguage>) -> ParseOutput<'b, NginxLanguage> {
80        let lexer = NginxLexer::new(self.language);
81        oak_core::parser::parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_root_internal(state))
82    }
83}