Skip to main content

oak_nix/parser/
mod.rs

1/// Element types for the Nix language syntax tree.
2pub mod element_type;
3
4use crate::{
5    language::NixLanguage,
6    lexer::{NixLexer, token_type::NixTokenType},
7};
8use oak_core::{
9    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
10    source::{Source, TextEdit},
11};
12
13pub(crate) type State<'a, S> = ParserState<'a, NixLanguage, S>;
14
15/// Parser for the Nix language.
16pub struct NixParser<'a> {
17    /// The language configuration for this parser.
18    pub language: &'a NixLanguage,
19}
20
21impl<'a> NixParser<'a> {
22    /// Creates a new `NixParser` with the given configuration.
23    pub fn new(language: &'a NixLanguage) -> Self {
24        Self { language }
25    }
26
27    fn parse_expr<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
28        match state.peek_kind() {
29            Some(NixTokenType::LeftBrace) => self.parse_set(state),
30            Some(NixTokenType::LeftBracket) => self.parse_list(state),
31            Some(NixTokenType::Let) => self.parse_let_in(state),
32            Some(NixTokenType::If) => self.parse_if_then_else(state),
33            Some(NixTokenType::Identifier) => {
34                state.bump();
35                if state.at(NixTokenType::Colon) {
36                    // This might be a lambda, but for now just bump
37                    state.bump();
38                    self.parse_expr(state)
39                }
40            }
41            _ => state.bump(),
42        }
43    }
44
45    fn parse_set<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
46        let cp = state.checkpoint();
47        state.expect(NixTokenType::LeftBrace).ok();
48        while state.not_at_end() && !state.at(NixTokenType::RightBrace) {
49            self.parse_binding(state)
50        }
51        state.expect(NixTokenType::RightBrace).ok();
52        state.finish_at(cp, crate::parser::element_type::NixElementType::Set);
53    }
54
55    fn parse_list<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
56        let cp = state.checkpoint();
57        state.expect(NixTokenType::LeftBracket).ok();
58        while state.not_at_end() && !state.at(NixTokenType::RightBracket) {
59            self.parse_expr(state)
60        }
61        state.expect(NixTokenType::RightBracket).ok();
62        state.finish_at(cp, crate::parser::element_type::NixElementType::List);
63    }
64
65    fn parse_let_in<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
66        let cp = state.checkpoint();
67        state.expect(NixTokenType::Let).ok();
68        while state.not_at_end() && !state.at(NixTokenType::In) {
69            self.parse_binding(state)
70        }
71        if state.at(NixTokenType::In) {
72            state.bump();
73            self.parse_expr(state)
74        }
75        state.finish_at(cp, crate::parser::element_type::NixElementType::LetIn);
76    }
77
78    fn parse_if_then_else<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
79        let cp = state.checkpoint();
80        state.expect(NixTokenType::If).ok();
81        self.parse_expr(state);
82        if state.at(NixTokenType::Then) {
83            state.bump();
84            self.parse_expr(state)
85        }
86        if state.at(NixTokenType::Else) {
87            state.bump();
88            self.parse_expr(state)
89        }
90        state.finish_at(cp, crate::parser::element_type::NixElementType::IfThenElse);
91    }
92
93    fn parse_binding<'b, S: Source + ?Sized>(&self, state: &mut State<'b, S>) {
94        let cp = state.checkpoint();
95        state.expect(NixTokenType::Identifier).ok();
96        if state.at(NixTokenType::Assign) {
97            state.bump();
98            self.parse_expr(state);
99            state.expect(NixTokenType::Semicolon).ok();
100        }
101        state.finish_at(cp, crate::parser::element_type::NixElementType::Binding);
102    }
103}
104
105impl<'config> Parser<NixLanguage> for NixParser<'config> {
106    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<NixLanguage>) -> ParseOutput<'a, NixLanguage> {
107        let lexer = NixLexer::new(self.language);
108        parse_with_lexer(&lexer, text, edits, cache, |state| {
109            let cp = state.checkpoint();
110
111            while state.not_at_end() {
112                self.parse_expr(state);
113            }
114
115            Ok(state.finish_at(cp, crate::parser::element_type::NixElementType::Root))
116        })
117    }
118}