1use crate::{kind::RubySyntaxKind, language::RubyLanguage, lexer::RubyLexer};
2use oak_core::{
3 GreenNode, OakError, TextEdit,
4 parser::{
5 ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer,
6 pratt::{Associativity, Pratt, PrattParser, binary, unary},
7 },
8 source::Source,
9};
10
11pub(crate) type State<'a, S> = ParserState<'a, RubyLanguage, S>;
12
13pub struct RubyParser<'config> {
14 pub(crate) config: &'config RubyLanguage,
15}
16
17impl<'config> RubyParser<'config> {
18 pub fn new(config: &'config RubyLanguage) -> Self {
19 Self { config }
20 }
21
22 pub(crate) fn parse_root_internal<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<&'a GreenNode<'a, RubyLanguage>, OakError> {
23 let cp = state.checkpoint();
24 while state.not_at_end() {
25 if state.peek_kind().map(|k| k.is_ignored()).unwrap_or(false) {
26 state.bump();
27 continue;
28 }
29 self.parse_statement(state).ok();
30 }
31 Ok(state.finish_at(cp, RubySyntaxKind::Root))
32 }
33
34 fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
35 use crate::kind::RubySyntaxKind::*;
36 match state.peek_kind() {
37 Some(Def) => self.parse_method_def(state)?,
38 Some(Class) => self.parse_class_def(state)?,
39 Some(Module) => self.parse_module_def(state)?,
40 Some(If) => self.parse_if_stmt(state)?,
41 Some(While) => self.parse_while_stmt(state)?,
42 Some(Return) => self.parse_return_stmt(state)?,
43 _ => {
44 PrattParser::parse(state, 0, self);
45 state.eat(Semicolon);
46 state.eat(Newline);
47 }
48 }
49 Ok(())
50 }
51
52 fn parse_method_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
53 use crate::kind::RubySyntaxKind::*;
54 let cp = state.checkpoint();
55 state.bump(); state.expect(Identifier).ok();
57 if state.eat(LeftParen) {
58 while state.not_at_end() && !state.at(RightParen) {
59 state.advance();
60 }
61 state.expect(RightParen).ok();
62 }
63 self.parse_body(state)?;
64 state.finish_at(cp, MethodDefinition);
65 Ok(())
66 }
67
68 fn parse_body<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
69 use crate::kind::RubySyntaxKind::*;
70 while state.not_at_end() && !state.at(End) && !state.at(Else) && !state.at(Elsif) && !state.at(Rescue) && !state.at(Ensure) {
71 self.parse_statement(state)?;
72 }
73 state.eat(End);
74 Ok(())
75 }
76
77 fn parse_class_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
78 use crate::kind::RubySyntaxKind::*;
79 let cp = state.checkpoint();
80 state.bump(); state.expect(Constant).ok();
82 self.parse_body(state)?;
83 state.finish_at(cp, ClassDefinition);
84 Ok(())
85 }
86
87 fn parse_module_def<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
88 use crate::kind::RubySyntaxKind::*;
89 let cp = state.checkpoint();
90 state.bump(); state.expect(Constant).ok();
92 self.parse_body(state)?;
93 state.finish_at(cp, ModuleDefinition);
94 Ok(())
95 }
96
97 fn parse_if_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
98 use crate::kind::RubySyntaxKind::*;
99 let cp = state.checkpoint();
100 state.bump(); PrattParser::parse(state, 0, self);
102 self.parse_body(state)?;
103 state.finish_at(cp, IfStatement);
104 Ok(())
105 }
106
107 fn parse_while_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
108 use crate::kind::RubySyntaxKind::*;
109 let cp = state.checkpoint();
110 state.bump(); PrattParser::parse(state, 0, self);
112 self.parse_body(state)?;
113 state.finish_at(cp, WhileStatement);
114 Ok(())
115 }
116
117 fn parse_return_stmt<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
118 use crate::kind::RubySyntaxKind::*;
119 let cp = state.checkpoint();
120 state.bump(); PrattParser::parse(state, 0, self);
122 state.finish_at(cp, ReturnStatement);
123 Ok(())
124 }
125}
126
127impl<'config> Pratt<RubyLanguage> for RubyParser<'config> {
128 fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RubyLanguage> {
129 use crate::kind::RubySyntaxKind::*;
130 let cp = state.checkpoint();
131 match state.peek_kind() {
132 Some(Identifier) | Some(Constant) | Some(GlobalVariable) | Some(InstanceVariable) | Some(ClassVariable) => {
133 state.bump();
134 state.finish_at(cp, Identifier)
135 }
136 Some(IntegerLiteral) | Some(FloatLiteral) | Some(StringLiteral) | Some(True) | Some(False) | Some(Nil) | Some(Self_) => {
137 state.bump();
138 state.finish_at(cp, LiteralExpression) }
140 Some(LeftParen) => {
141 state.bump();
142 PrattParser::parse(state, 0, self);
143 state.expect(RightParen).ok();
144 state.finish_at(cp, ParenExpression) }
146 _ => {
147 state.bump();
148 state.finish_at(cp, Error)
149 }
150 }
151 }
152
153 fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, RubyLanguage> {
154 use crate::kind::RubySyntaxKind::*;
155 match state.peek_kind() {
156 Some(kind @ (Plus | Minus | Not | Tilde)) => {
157 state.bump();
158 unary(state, kind, 13, UnaryExpression, |st, p| PrattParser::parse(st, p, self))
159 }
160 _ => self.primary(state),
161 }
162 }
163
164 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>> {
165 use crate::kind::RubySyntaxKind::*;
166 let kind = state.peek_kind()?;
167
168 let (prec, assoc) = match kind {
169 Power => (30, Associativity::Right),
170 Multiply | Divide | Modulo => (20, Associativity::Left),
171 Plus | Minus => (10, Associativity::Left),
172 EqualEqual | NotEqual | Less | Greater | LessEqual | GreaterEqual => (5, Associativity::Left),
173 AndAnd => (2, Associativity::Left),
174 OrOr => (1, Associativity::Left),
175 _ => return None,
176 };
177
178 if prec < min_precedence {
179 return None;
180 }
181
182 Some(binary(state, left, kind, prec, assoc, BinaryExpression, |s, p| PrattParser::parse(s, p, self)))
183 }
184}
185
186impl<'config> Parser<RubyLanguage> for RubyParser<'config> {
187 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<RubyLanguage>) -> ParseOutput<'a, RubyLanguage> {
188 let lexer = RubyLexer::new(self.config);
189 parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_root_internal(state))
190 }
191}