Skip to main content

oak_powershell/parser/
mod.rs

1/// Element type definitions.
2pub mod element_type;
3
4use crate::{language::PowerShellLanguage, lexer::token_type::PowerShellTokenType, parser::element_type::PowerShellElementType};
5use oak_core::{
6    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
7    source::{Source, TextEdit},
8};
9
10/// Parser for the PowerShell language.
11#[derive(Debug)]
12pub struct PowerShellParser<'a> {
13    /// The language configuration.
14    pub config: &'a PowerShellLanguage,
15}
16
17impl<'a> PowerShellParser<'a> {
18    /// Creates a new `PowerShellParser`.
19    pub fn new(config: &'a PowerShellLanguage) -> Self {
20        Self { config }
21    }
22
23    fn parse_program<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
24        while state.not_at_end() {
25            let kind = state.peek_kind();
26            match kind {
27                Some(PowerShellTokenType::Function) => self.parse_function(state),
28                Some(PowerShellTokenType::Class) => self.parse_class(state),
29                Some(PowerShellTokenType::If) => self.parse_if(state),
30                Some(PowerShellTokenType::While) => self.parse_while(state),
31                Some(PowerShellTokenType::For) => self.parse_for(state),
32                Some(PowerShellTokenType::ForEach) => self.parse_foreach(state),
33                Some(PowerShellTokenType::Try) => self.parse_try(state),
34                Some(PowerShellTokenType::Newline) | Some(PowerShellTokenType::Semicolon) | Some(PowerShellTokenType::Whitespace) => {
35                    state.bump();
36                }
37                _ => self.parse_statement(state),
38            }
39        }
40    }
41
42    fn parse_function<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
43        let checkpoint = state.checkpoint();
44        state.expect(PowerShellTokenType::Function).ok();
45        state.expect(PowerShellTokenType::Identifier).ok();
46        self.skip_trivia(state);
47        if state.at(PowerShellTokenType::LeftBrace) {
48            self.parse_block(state);
49        }
50        state.finish_at(checkpoint, PowerShellElementType::FunctionDef);
51    }
52
53    fn parse_class<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
54        let checkpoint = state.checkpoint();
55        state.expect(PowerShellTokenType::Class).ok();
56        state.expect(PowerShellTokenType::Identifier).ok();
57        self.skip_trivia(state);
58        if state.at(PowerShellTokenType::LeftBrace) {
59            self.parse_block(state);
60        }
61        state.finish_at(checkpoint, PowerShellElementType::ClassDef);
62    }
63
64    fn parse_if<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
65        let checkpoint = state.checkpoint();
66        state.expect(PowerShellTokenType::If).ok();
67        self.skip_trivia(state);
68        state.expect(PowerShellTokenType::LeftParen).ok();
69        while !state.at(PowerShellTokenType::RightParen) && state.not_at_end() {
70            state.bump();
71        }
72        state.expect(PowerShellTokenType::RightParen).ok();
73        self.skip_trivia(state);
74        self.parse_block(state);
75
76        loop {
77            self.skip_trivia(state);
78            if state.at(PowerShellTokenType::ElseIf) {
79                state.bump();
80                self.skip_trivia(state);
81                state.expect(PowerShellTokenType::LeftParen).ok();
82                while !state.at(PowerShellTokenType::RightParen) && state.not_at_end() {
83                    state.bump();
84                }
85                state.expect(PowerShellTokenType::RightParen).ok();
86                self.skip_trivia(state);
87                self.parse_block(state);
88            }
89            else {
90                break;
91            }
92        }
93
94        self.skip_trivia(state);
95        if state.at(PowerShellTokenType::Else) {
96            state.bump();
97            self.skip_trivia(state);
98            self.parse_block(state);
99        }
100
101        state.finish_at(checkpoint, PowerShellElementType::IfStatement);
102    }
103
104    fn parse_while<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
105        let checkpoint = state.checkpoint();
106        state.expect(PowerShellTokenType::While).ok();
107        self.skip_trivia(state);
108        state.expect(PowerShellTokenType::LeftParen).ok();
109        while !state.at(PowerShellTokenType::RightParen) && state.not_at_end() {
110            state.bump();
111        }
112        state.expect(PowerShellTokenType::RightParen).ok();
113        self.skip_trivia(state);
114        self.parse_block(state);
115        state.finish_at(checkpoint, PowerShellElementType::WhileStatement);
116    }
117
118    fn parse_for<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
119        let checkpoint = state.checkpoint();
120        state.expect(PowerShellTokenType::For).ok();
121        self.skip_trivia(state);
122        state.expect(PowerShellTokenType::LeftParen).ok();
123        while !state.at(PowerShellTokenType::RightParen) && state.not_at_end() {
124            state.bump();
125        }
126        state.expect(PowerShellTokenType::RightParen).ok();
127        self.skip_trivia(state);
128        self.parse_block(state);
129        state.finish_at(checkpoint, PowerShellElementType::ForStatement);
130    }
131
132    fn parse_foreach<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
133        let checkpoint = state.checkpoint();
134        state.expect(PowerShellTokenType::ForEach).ok();
135        self.skip_trivia(state);
136        state.expect(PowerShellTokenType::LeftParen).ok();
137        while !state.at(PowerShellTokenType::RightParen) && state.not_at_end() {
138            state.bump();
139        }
140        state.expect(PowerShellTokenType::RightParen).ok();
141        self.skip_trivia(state);
142        self.parse_block(state);
143        state.finish_at(checkpoint, PowerShellElementType::ForEachStatement);
144    }
145
146    fn parse_try<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
147        let checkpoint = state.checkpoint();
148        state.expect(PowerShellTokenType::Try).ok();
149        self.skip_trivia(state);
150        self.parse_block(state);
151
152        while state.at(PowerShellTokenType::Catch) {
153            let catch_cp = state.checkpoint();
154            state.bump();
155            self.skip_trivia(state);
156            if state.at(PowerShellTokenType::LeftBracket) {
157                // Type list
158                while !state.at(PowerShellTokenType::RightBracket) && state.not_at_end() {
159                    state.bump();
160                }
161                state.expect(PowerShellTokenType::RightBracket).ok();
162                self.skip_trivia(state);
163            }
164            self.parse_block(state);
165            state.finish_at(catch_cp, PowerShellElementType::CatchBlock);
166            self.skip_trivia(state);
167        }
168
169        if state.at(PowerShellTokenType::Finally) {
170            let finally_cp = state.checkpoint();
171            state.bump();
172            self.skip_trivia(state);
173            self.parse_block(state);
174            state.finish_at(finally_cp, PowerShellElementType::FinallyBlock);
175        }
176
177        state.finish_at(checkpoint, PowerShellElementType::TryStatement);
178    }
179
180    fn parse_block<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
181        state.expect(PowerShellTokenType::LeftBrace).ok();
182        while !state.at(PowerShellTokenType::RightBrace) && state.not_at_end() {
183            let kind = state.peek_kind();
184            match kind {
185                Some(PowerShellTokenType::Newline) | Some(PowerShellTokenType::Semicolon) | Some(PowerShellTokenType::Whitespace) => {
186                    state.bump();
187                }
188                _ => self.parse_statement(state),
189            }
190        }
191        state.expect(PowerShellTokenType::RightBrace).ok();
192    }
193
194    fn parse_statement<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
195        let checkpoint = state.checkpoint();
196
197        self.parse_pipeline(state);
198
199        if state.at(PowerShellTokenType::Semicolon) || state.at(PowerShellTokenType::Newline) {
200            state.bump();
201        }
202        state.finish_at(checkpoint, PowerShellElementType::ExpressionStatement);
203    }
204
205    fn parse_pipeline<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
206        let checkpoint = state.checkpoint();
207        self.parse_command(state);
208
209        while state.at(PowerShellTokenType::Pipe) {
210            state.bump();
211            self.skip_trivia(state);
212            self.parse_command(state);
213        }
214
215        if state.at(PowerShellTokenType::Pipe) {
216            state.finish_at(checkpoint, PowerShellElementType::Pipeline);
217        }
218    }
219
220    fn parse_command<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
221        let checkpoint = state.checkpoint();
222
223        // Command name or variable
224        if state.not_at_end() && !matches!(state.peek_kind(), Some(PowerShellTokenType::Newline | PowerShellTokenType::Semicolon | PowerShellTokenType::Pipe | PowerShellTokenType::RightBrace)) {
225            state.bump();
226        }
227
228        // Arguments and parameters
229        while state.not_at_end() {
230            self.skip_trivia(state);
231            let kind = state.peek_kind();
232            if matches!(kind, Some(PowerShellTokenType::Newline | PowerShellTokenType::Semicolon | PowerShellTokenType::Pipe | PowerShellTokenType::RightBrace)) {
233                break;
234            }
235            state.bump();
236        }
237
238        state.finish_at(checkpoint, PowerShellElementType::Command);
239    }
240
241    fn skip_trivia<'b, S: Source + ?Sized>(&self, state: &mut ParserState<'b, PowerShellLanguage, S>) {
242        while state.at(PowerShellTokenType::Whitespace) || state.at(PowerShellTokenType::Comment) {
243            state.bump();
244        }
245    }
246}
247
248impl<'a> Parser<PowerShellLanguage> for PowerShellParser<'a> {
249    fn parse<'b, S: Source + ?Sized>(&self, text: &'b S, edits: &[TextEdit], cache: &'b mut impl ParseCache<PowerShellLanguage>) -> ParseOutput<'b, PowerShellLanguage> {
250        let lexer = crate::lexer::PowerShellLexer::new(self.config);
251        parse_with_lexer(&lexer, text, edits, cache, |state| {
252            let checkpoint = state.checkpoint();
253            self.parse_program(state);
254            Ok(state.finish_at(checkpoint, PowerShellElementType::Root))
255        })
256    }
257}