Skip to main content

oak_nginx/parser/
mod.rs

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