Skip to main content

oak_ruby/parser/
mod.rs

1use oak_core::TokenType;
2/// Element types for the Ruby language.
3pub mod element_type;
4
5use crate::{
6    language::RubyLanguage,
7    lexer::{RubyLexer, token_type::RubyTokenType},
8};
9use oak_core::{
10    GreenNode, OakError, TextEdit,
11    parser::{
12        ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer,
13        pratt::{Associativity, Pratt, PrattParser, binary, unary},
14    },
15    source::Source,
16};
17
18pub(crate) type State<'a, S> = ParserState<'a, RubyLanguage, S>;
19
20/// A parser for the Ruby language.
21pub struct RubyParser<'config> {
22    pub(crate) config: &'config RubyLanguage,
23}
24
25impl<'config> RubyParser<'config> {
26    /// Creates a new `RubyParser` with the given configuration.
27    pub fn new(config: &'config RubyLanguage) -> Self {
28        Self { config }
29    }
30
31    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
32        use crate::lexer::token_type::RubyTokenType::*;
33        match state.peek_kind() {
34            Some(Def) => self.parse_method_def(state)?,
35            Some(Class) => self.parse_class_def(state)?,
36            Some(Module) => self.parse_module_def(state)?,
37            Some(If) => self.parse_if_stmt(state)?,
38            Some(Unless) => self.parse_unless_stmt(state)?,
39            Some(While) => self.parse_while_stmt(state)?,
40            Some(Until) => self.parse_until_stmt(state)?,
41            Some(For) => self.parse_for_stmt(state)?,
42            Some(Case) => self.parse_case_stmt(state)?,
43            Some(Begin) => self.parse_begin_stmt(state)?,
44            Some(Return) => self.parse_return_stmt(state)?,
45            _ => {
46                PrattParser::parse(state, 0, self);
47                state.eat(Semicolon);
48                state.eat(Newline);
49            }
50        }
51        Ok(())
52    }
53
54    fn parse_unless_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
55        use crate::lexer::token_type::RubyTokenType::*;
56        let cp = state.checkpoint();
57        state.bump(); // unless
58        PrattParser::parse(state, 0, self);
59        self.parse_body(state)?;
60        state.finish_at(cp, crate::parser::element_type::RubyElementType::UnlessStatement);
61        Ok(())
62    }
63
64    fn parse_until_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
65        use crate::lexer::token_type::RubyTokenType::*;
66        let cp = state.checkpoint();
67        state.bump(); // until
68        PrattParser::parse(state, 0, self);
69        self.parse_body(state)?;
70        state.finish_at(cp, crate::parser::element_type::RubyElementType::UntilStatement);
71        Ok(())
72    }
73
74    fn parse_for_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
75        use crate::lexer::token_type::RubyTokenType::*;
76        let cp = state.checkpoint();
77        state.bump(); // for
78        state.expect(Identifier).ok();
79        state.expect(In).ok();
80        PrattParser::parse(state, 0, self);
81        self.parse_body(state)?;
82        state.finish_at(cp, crate::parser::element_type::RubyElementType::ForStatement);
83        Ok(())
84    }
85
86    fn parse_case_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
87        use crate::lexer::token_type::RubyTokenType::*;
88        let cp = state.checkpoint();
89        state.bump(); // case
90        if !state.at(When) {
91            PrattParser::parse(state, 0, self);
92        }
93
94        while state.at(When) {
95            let when_cp = state.checkpoint();
96            state.bump(); // when
97            PrattParser::parse(state, 0, self);
98            while state.eat(Comma) {
99                PrattParser::parse(state, 0, self);
100            }
101            state.eat(Then);
102            self.parse_case_body(state)?;
103            state.finish_at(when_cp, crate::parser::element_type::RubyElementType::WhenClause);
104        }
105
106        if state.eat(Else) {
107            self.parse_case_body(state)?;
108        }
109
110        state.expect(End).ok();
111        state.finish_at(cp, crate::parser::element_type::RubyElementType::CaseStatement);
112        Ok(())
113    }
114
115    fn parse_case_body<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
116        use crate::lexer::token_type::RubyTokenType::*;
117        while state.not_at_end() && !state.at(End) && !state.at(When) && !state.at(Else) {
118            self.parse_statement(state)?
119        }
120        Ok(())
121    }
122
123    fn parse_begin_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
124        use crate::lexer::token_type::RubyTokenType::*;
125        let cp = state.checkpoint();
126        state.bump(); // begin
127
128        while state.not_at_end() && !state.at(End) && !state.at(Rescue) && !state.at(Ensure) && !state.at(Else) {
129            self.parse_statement(state)?
130        }
131
132        while state.at(Rescue) {
133            let rescue_cp = state.checkpoint();
134            state.bump(); // rescue
135            if !state.at(Then) && !state.at(Newline) && !state.at(Semicolon) {
136                PrattParser::parse(state, 0, self); // Exception class
137                if state.at(EqualGreater) {
138                    state.bump();
139                    state.expect(Identifier).ok();
140                }
141            }
142            state.eat(Then);
143            while state.not_at_end() && !state.at(End) && !state.at(Rescue) && !state.at(Ensure) && !state.at(Else) {
144                self.parse_statement(state)?
145            }
146            state.finish_at(rescue_cp, crate::parser::element_type::RubyElementType::RescueClause);
147        }
148
149        if state.eat(Else) {
150            while state.not_at_end() && !state.at(End) && !state.at(Ensure) {
151                self.parse_statement(state)?
152            }
153        }
154
155        if state.at(Ensure) {
156            let ensure_cp = state.checkpoint();
157            state.bump(); // ensure
158            while state.not_at_end() && !state.at(End) {
159                self.parse_statement(state)?
160            }
161            state.finish_at(ensure_cp, crate::parser::element_type::RubyElementType::EnsureClause);
162        }
163
164        state.expect(End).ok();
165        state.finish_at(cp, crate::parser::element_type::RubyElementType::BeginStatement);
166        Ok(())
167    }
168
169    fn parse_method_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
170        use crate::lexer::token_type::RubyTokenType::*;
171        let cp = state.checkpoint();
172        state.bump(); // def
173        state.expect(Identifier).ok();
174        if state.eat(LeftParen) {
175            while state.not_at_end() && !state.at(RightParen) {
176                state.advance()
177            }
178            let _ = state.expect(RightParen);
179        }
180        self.parse_body(state)?;
181        state.finish_at(cp, crate::parser::element_type::RubyElementType::MethodDefinition);
182        Ok(())
183    }
184
185    fn parse_body<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
186        use crate::lexer::token_type::RubyTokenType::*;
187        while state.not_at_end() && !state.at(End) && !state.at(Else) && !state.at(Elsif) && !state.at(Rescue) && !state.at(Ensure) {
188            self.parse_statement(state)?
189        }
190        state.eat(End);
191        Ok(())
192    }
193
194    fn parse_class_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
195        use crate::lexer::token_type::RubyTokenType::*;
196        let cp = state.checkpoint();
197        state.bump(); // class
198        state.expect(Constant).ok();
199        self.parse_body(state)?;
200        state.finish_at(cp, crate::parser::element_type::RubyElementType::ClassDefinition);
201        Ok(())
202    }
203
204    fn parse_module_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
205        use crate::lexer::token_type::RubyTokenType::*;
206        let cp = state.checkpoint();
207        state.bump(); // module
208        state.expect(Constant).ok();
209        self.parse_body(state)?;
210        state.finish_at(cp, crate::parser::element_type::RubyElementType::ModuleDefinition);
211        Ok(())
212    }
213
214    fn parse_if_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
215        use crate::lexer::token_type::RubyTokenType::*;
216        let cp = state.checkpoint();
217        state.bump(); // if
218        PrattParser::parse(state, 0, self);
219        self.parse_body(state)?;
220        state.finish_at(cp, crate::parser::element_type::RubyElementType::IfStatement);
221        Ok(())
222    }
223
224    fn parse_while_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
225        use crate::lexer::token_type::RubyTokenType::*;
226        let cp = state.checkpoint();
227        state.bump(); // while
228        PrattParser::parse(state, 0, self);
229        self.parse_body(state)?;
230        state.finish_at(cp, crate::parser::element_type::RubyElementType::WhileStatement);
231        Ok(())
232    }
233
234    fn parse_return_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
235        use crate::lexer::token_type::RubyTokenType::*;
236        let cp = state.checkpoint();
237        state.bump(); // return
238        PrattParser::parse(state, 0, self);
239        state.finish_at(cp, crate::parser::element_type::RubyElementType::ReturnStatement);
240        Ok(())
241    }
242}
243
244impl<'config> Pratt<RubyLanguage> for RubyParser<'config> {
245    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RubyLanguage> {
246        use crate::lexer::token_type::RubyTokenType::*;
247        let cp = state.checkpoint();
248        match state.peek_kind() {
249            Some(Identifier) | Some(Constant) | Some(GlobalVariable) | Some(InstanceVariable) | Some(ClassVariable) => {
250                state.bump();
251                state.finish_at(cp, crate::parser::element_type::RubyElementType::Identifier)
252            }
253            Some(IntegerLiteral) | Some(FloatLiteral) | Some(StringLiteral) | Some(True) | Some(False) | Some(Nil) | Some(Self_) => {
254                state.bump();
255                state.finish_at(cp, crate::parser::element_type::RubyElementType::LiteralExpression) // Simplified handling
256            }
257            Some(LeftParen) => {
258                state.bump();
259                PrattParser::parse(state, 0, self);
260                state.expect(RightParen).ok();
261                state.finish_at(cp, crate::parser::element_type::RubyElementType::ParenExpression) // Simplified handling
262            }
263            _ => {
264                state.bump();
265                state.finish_at(cp, crate::parser::element_type::RubyElementType::Error)
266            }
267        }
268    }
269
270    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RubyLanguage> {
271        use crate::lexer::token_type::RubyTokenType::*;
272        match state.peek_kind() {
273            Some(kind @ (Plus | Minus | Not | Tilde)) => {
274                state.bump();
275                unary(state, kind, 13, crate::parser::element_type::RubyElementType::UnaryExpression, |st, p| PrattParser::parse(st, p, self))
276            }
277            _ => self.primary(state),
278        }
279    }
280
281    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, RubyLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, RubyLanguage>> {
282        use crate::lexer::token_type::RubyTokenType::*;
283        let kind = state.peek_kind()?;
284
285        let (prec, assoc) = match kind {
286            Power => (30, Associativity::Right),
287            Multiply | Divide | Modulo => (20, Associativity::Left),
288            Plus | Minus => (10, Associativity::Left),
289            EqualEqual | NotEqual | Less | Greater | LessEqual | GreaterEqual => (5, Associativity::Left),
290            AndAnd => (2, Associativity::Left),
291            OrOr => (1, Associativity::Left),
292            _ => return None,
293        };
294
295        if prec < min_precedence {
296            return None;
297        }
298
299        Some(binary(state, left, kind, prec, assoc, crate::parser::element_type::RubyElementType::BinaryExpression, |s, p| PrattParser::parse(s, p, self)))
300    }
301}
302
303impl<'config> Parser<RubyLanguage> for RubyParser<'config> {
304    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<RubyLanguage>) -> ParseOutput<'a, RubyLanguage> {
305        let lexer = RubyLexer::new(&self.config);
306        parse_with_lexer(&lexer, text, edits, cache, |state| {
307            let cp = state.checkpoint();
308            while state.not_at_end() {
309                if state.peek_kind().map(|k| k.is_ignored()).unwrap_or(false) {
310                    state.bump();
311                    continue;
312                }
313                let _ = self.parse_statement(state);
314            }
315            Ok(state.finish_at(cp, crate::parser::element_type::RubyElementType::Root))
316        })
317    }
318}