Skip to main content

oak_nginx/parser/
mod.rs

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