Skip to main content

oak_zig/parser/
mod.rs

1/// Zig element types and roles.
2pub mod element_type;
3
4use crate::{
5    language::ZigLanguage,
6    lexer::{ZigLexer, token_type::ZigTokenType},
7    parser::element_type::ZigElementType,
8};
9use oak_core::{
10    GreenNode, OakError,
11    parser::{
12        ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer,
13        pratt::{Associativity, Pratt, PrattParser, binary, unary},
14    },
15    source::{Source, TextEdit},
16};
17
18pub(crate) type State<'a, S> = ParserState<'a, ZigLanguage, S>;
19
20/// Parser for the Zig language.
21pub struct ZigParser<'config> {
22    pub(crate) config: &'config ZigLanguage,
23}
24
25impl<'config> ZigParser<'config> {
26    /// Creates a new Zig parser with the given configuration.
27    pub fn new(config: &'config ZigLanguage) -> Self {
28        Self { config }
29    }
30}
31
32impl<'config> Pratt<ZigLanguage> for ZigParser<'config> {
33    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ZigLanguage> {
34        let cp = state.checkpoint();
35        match state.peek_kind() {
36            Some(ZigTokenType::Identifier) => {
37                state.bump();
38                state.finish_at(cp, ZigElementType::Identifier)
39            }
40            Some(ZigTokenType::IntegerLiteral) | Some(ZigTokenType::FloatLiteral) | Some(ZigTokenType::StringLiteral) | Some(ZigTokenType::CharLiteral) | Some(ZigTokenType::BooleanLiteral) | Some(ZigTokenType::Null) | Some(ZigTokenType::Undefined) => {
41                state.bump();
42                state.finish_at(cp, ZigElementType::Literal)
43            }
44            Some(ZigTokenType::LeftParen) => {
45                state.bump();
46                PrattParser::parse(state, 0, self);
47                state.expect(ZigTokenType::RightParen).ok();
48                state.finish_at(cp, ZigElementType::Root)
49            }
50            _ => {
51                state.bump();
52                state.finish_at(cp, ZigElementType::Error)
53            }
54        }
55    }
56
57    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, ZigLanguage> {
58        let kind = match state.peek_kind() {
59            Some(k) => k,
60            None => return self.primary(state),
61        };
62
63        match kind {
64            ZigTokenType::Minus | ZigTokenType::Tilde | ZigTokenType::Exclamation | ZigTokenType::Ampersand | ZigTokenType::TryKeyword | ZigTokenType::AwaitKeyword => {
65                // unary expects kind to be ZigLanguage::TokenType
66                unary(state, kind, 12, ZigElementType::UnaryExpr, |s, p| PrattParser::parse(s, p, self))
67            }
68            _ => self.primary(state),
69        }
70    }
71
72    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, ZigLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, ZigLanguage>> {
73        let kind = state.peek_kind()?;
74
75        let (prec, assoc) = match kind {
76            ZigTokenType::Assign
77            | ZigTokenType::PlusAssign
78            | ZigTokenType::MinusAssign
79            | ZigTokenType::StarAssign
80            | ZigTokenType::SlashAssign
81            | ZigTokenType::PercentAssign
82            | ZigTokenType::AmpersandAssign
83            | ZigTokenType::PipeAssign
84            | ZigTokenType::CaretAssign
85            | ZigTokenType::LessLessAssign
86            | ZigTokenType::GreaterGreaterAssign => (1, Associativity::Right),
87            ZigTokenType::Or | ZigTokenType::OrOr => (2, Associativity::Left),
88            ZigTokenType::And | ZigTokenType::AndAnd => (3, Associativity::Left),
89            ZigTokenType::Equal | ZigTokenType::NotEqual | ZigTokenType::Less | ZigTokenType::Greater | ZigTokenType::LessEqual | ZigTokenType::GreaterEqual => (4, Associativity::Left),
90            ZigTokenType::Plus | ZigTokenType::Minus | ZigTokenType::PlusPercent | ZigTokenType::MinusPercent | ZigTokenType::PlusPlus => (5, Associativity::Left),
91            ZigTokenType::Star | ZigTokenType::Slash | ZigTokenType::Percent | ZigTokenType::StarPercent | ZigTokenType::StarStar => (6, Associativity::Left),
92            ZigTokenType::CatchKeyword | ZigTokenType::OrElse => (7, Associativity::Right),
93            ZigTokenType::Dot | ZigTokenType::LeftParen | ZigTokenType::LeftBracket => (10, Associativity::Left),
94            _ => return None,
95        };
96
97        if prec < min_precedence {
98            return None;
99        }
100
101        match kind {
102            ZigTokenType::Dot => {
103                let cp = state.checkpoint();
104                state.push_child(left);
105                state.bump();
106                state.expect(ZigTokenType::Identifier).ok();
107                Some(state.finish_at(cp, ZigElementType::Root))
108            }
109            ZigTokenType::LeftParen => {
110                let cp = state.checkpoint();
111                state.push_child(left);
112                state.bump();
113                while state.not_at_end() && !state.at(ZigTokenType::RightParen) {
114                    PrattParser::parse(state, 0, self);
115                    if !state.eat(ZigTokenType::Comma) {
116                        break;
117                    }
118                }
119                state.expect(ZigTokenType::RightParen).ok();
120                Some(state.finish_at(cp, ZigElementType::Root))
121            }
122            ZigTokenType::LeftBracket => {
123                let cp = state.checkpoint();
124                state.push_child(left);
125                state.bump();
126                PrattParser::parse(state, 0, self);
127                state.expect(ZigTokenType::RightBracket).ok();
128                Some(state.finish_at(cp, ZigElementType::Root))
129            }
130            _ => Some(binary(state, left, kind, prec, assoc, ZigElementType::BinaryExpr.into(), |s, p| PrattParser::parse(s, p, self))),
131        }
132    }
133}
134
135impl<'config> Parser<ZigLanguage> for ZigParser<'config> {
136    fn parse<'a, S: Source + ?Sized>(&self, source: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<ZigLanguage>) -> ParseOutput<'a, ZigLanguage> {
137        let lexer = ZigLexer::new(self.config);
138        parse_with_lexer(&lexer, source, edits, cache, |state| {
139            let checkpoint = state.checkpoint();
140
141            while state.not_at_end() {
142                self.parse_statement(state)?;
143            }
144
145            Ok(state.finish_at(checkpoint, ZigElementType::Root))
146        })
147    }
148}
149
150impl<'p> ZigParser<'p> {
151    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
152        match state.peek_kind() {
153            Some(ZigTokenType::Fn) => self.parse_function_declaration(state)?,
154            Some(ZigTokenType::Const) | Some(ZigTokenType::Var) => self.parse_variable_declaration(state)?,
155            Some(ZigTokenType::If) => self.parse_if_statement(state)?,
156            Some(ZigTokenType::While) => self.parse_while_statement(state)?,
157            Some(ZigTokenType::For) => self.parse_for_statement(state)?,
158            Some(ZigTokenType::Return) => self.parse_return_statement(state)?,
159            Some(ZigTokenType::LeftBrace) => self.parse_block(state)?,
160            _ => {
161                PrattParser::parse(state, 0, self);
162                state.eat(ZigTokenType::Semicolon);
163            }
164        }
165        Ok(())
166    }
167
168    fn parse_function_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
169        let cp = state.checkpoint();
170        state.expect(ZigTokenType::Fn).ok();
171        state.expect(ZigTokenType::Identifier).ok();
172        state.expect(ZigTokenType::LeftParen).ok();
173        while state.not_at_end() && !state.at(ZigTokenType::RightParen) {
174            state.advance();
175        }
176        state.expect(ZigTokenType::RightParen).ok();
177        while state.not_at_end() && !state.at(ZigTokenType::LeftBrace) {
178            state.bump();
179        }
180        self.parse_block(state)?;
181        state.finish_at(cp, ZigElementType::FnDeclaration);
182        Ok(())
183    }
184
185    fn parse_variable_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
186        let cp = state.checkpoint();
187        state.bump(); // const or var
188        state.expect(ZigTokenType::Identifier).ok();
189        if state.eat(ZigTokenType::Colon) {
190            while state.not_at_end() && !state.at(ZigTokenType::Assign) && !state.at(ZigTokenType::Semicolon) {
191                state.bump();
192            }
193        }
194        if state.eat(ZigTokenType::Assign) {
195            PrattParser::parse(state, 0, self);
196        }
197        state.eat(ZigTokenType::Semicolon);
198        state.finish_at(cp, ZigElementType::VarDeclaration);
199        Ok(())
200    }
201
202    fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
203        let cp = state.checkpoint();
204        state.expect(ZigTokenType::If).ok();
205        state.expect(ZigTokenType::LeftParen).ok();
206        PrattParser::parse(state, 0, self);
207        state.expect(ZigTokenType::RightParen).ok();
208        self.parse_statement(state)?;
209        if state.eat(ZigTokenType::Else) {
210            self.parse_statement(state)?;
211        }
212        state.finish_at(cp, ZigElementType::IfStatement);
213        Ok(())
214    }
215
216    fn parse_while_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
217        let cp = state.checkpoint();
218        state.expect(ZigTokenType::While).ok();
219        state.expect(ZigTokenType::LeftParen).ok();
220        PrattParser::parse(state, 0, self);
221        state.expect(ZigTokenType::RightParen).ok();
222        self.parse_statement(state)?;
223        state.finish_at(cp, ZigElementType::WhileStatement);
224        Ok(())
225    }
226
227    fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
228        let cp = state.checkpoint();
229        state.expect(ZigTokenType::For).ok();
230        state.expect(ZigTokenType::LeftParen).ok();
231        PrattParser::parse(state, 0, self);
232        state.expect(ZigTokenType::RightParen).ok();
233        self.parse_statement(state)?;
234        state.finish_at(cp, ZigElementType::ForStatement);
235        Ok(())
236    }
237
238    fn parse_return_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
239        let cp = state.checkpoint();
240        state.expect(ZigTokenType::Return).ok();
241        if !state.at(ZigTokenType::Semicolon) {
242            PrattParser::parse(state, 0, self);
243        }
244        state.eat(ZigTokenType::Semicolon);
245        state.finish_at(cp, ZigElementType::ReturnStatement);
246        Ok(())
247    }
248
249    fn parse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
250        let cp = state.checkpoint();
251        state.expect(ZigTokenType::LeftBrace).ok();
252        while state.not_at_end() && !state.at(ZigTokenType::RightBrace) {
253            self.parse_statement(state)?;
254        }
255        state.expect(ZigTokenType::RightBrace).ok();
256        state.finish_at(cp, ZigElementType::Block);
257        Ok(())
258    }
259}