natalie_parser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/Dockerfile +26 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +55 -0
  7. data/Rakefile +242 -0
  8. data/ext/natalie_parser/extconf.rb +9 -0
  9. data/ext/natalie_parser/mri_creator.hpp +139 -0
  10. data/ext/natalie_parser/natalie_parser.cpp +144 -0
  11. data/include/natalie_parser/creator/debug_creator.hpp +113 -0
  12. data/include/natalie_parser/creator.hpp +108 -0
  13. data/include/natalie_parser/lexer/interpolated_string_lexer.hpp +64 -0
  14. data/include/natalie_parser/lexer/regexp_lexer.hpp +37 -0
  15. data/include/natalie_parser/lexer/word_array_lexer.hpp +57 -0
  16. data/include/natalie_parser/lexer.hpp +135 -0
  17. data/include/natalie_parser/node/alias_node.hpp +35 -0
  18. data/include/natalie_parser/node/arg_node.hpp +74 -0
  19. data/include/natalie_parser/node/array_node.hpp +34 -0
  20. data/include/natalie_parser/node/array_pattern_node.hpp +28 -0
  21. data/include/natalie_parser/node/assignment_node.hpp +34 -0
  22. data/include/natalie_parser/node/back_ref_node.hpp +28 -0
  23. data/include/natalie_parser/node/begin_block_node.hpp +25 -0
  24. data/include/natalie_parser/node/begin_node.hpp +52 -0
  25. data/include/natalie_parser/node/begin_rescue_node.hpp +47 -0
  26. data/include/natalie_parser/node/bignum_node.hpp +37 -0
  27. data/include/natalie_parser/node/block_node.hpp +55 -0
  28. data/include/natalie_parser/node/block_pass_node.hpp +33 -0
  29. data/include/natalie_parser/node/break_node.hpp +32 -0
  30. data/include/natalie_parser/node/call_node.hpp +85 -0
  31. data/include/natalie_parser/node/case_in_node.hpp +40 -0
  32. data/include/natalie_parser/node/case_node.hpp +52 -0
  33. data/include/natalie_parser/node/case_when_node.hpp +43 -0
  34. data/include/natalie_parser/node/class_node.hpp +39 -0
  35. data/include/natalie_parser/node/colon2_node.hpp +44 -0
  36. data/include/natalie_parser/node/colon3_node.hpp +34 -0
  37. data/include/natalie_parser/node/constant_node.hpp +26 -0
  38. data/include/natalie_parser/node/def_node.hpp +55 -0
  39. data/include/natalie_parser/node/defined_node.hpp +33 -0
  40. data/include/natalie_parser/node/encoding_node.hpp +26 -0
  41. data/include/natalie_parser/node/end_block_node.hpp +25 -0
  42. data/include/natalie_parser/node/evaluate_to_string_node.hpp +37 -0
  43. data/include/natalie_parser/node/false_node.hpp +23 -0
  44. data/include/natalie_parser/node/fixnum_node.hpp +36 -0
  45. data/include/natalie_parser/node/float_node.hpp +36 -0
  46. data/include/natalie_parser/node/hash_node.hpp +34 -0
  47. data/include/natalie_parser/node/hash_pattern_node.hpp +27 -0
  48. data/include/natalie_parser/node/identifier_node.hpp +123 -0
  49. data/include/natalie_parser/node/if_node.hpp +43 -0
  50. data/include/natalie_parser/node/infix_op_node.hpp +46 -0
  51. data/include/natalie_parser/node/interpolated_node.hpp +33 -0
  52. data/include/natalie_parser/node/interpolated_regexp_node.hpp +28 -0
  53. data/include/natalie_parser/node/interpolated_shell_node.hpp +22 -0
  54. data/include/natalie_parser/node/interpolated_string_node.hpp +31 -0
  55. data/include/natalie_parser/node/interpolated_symbol_key_node.hpp +18 -0
  56. data/include/natalie_parser/node/interpolated_symbol_node.hpp +28 -0
  57. data/include/natalie_parser/node/iter_node.hpp +45 -0
  58. data/include/natalie_parser/node/keyword_arg_node.hpp +25 -0
  59. data/include/natalie_parser/node/keyword_splat_node.hpp +38 -0
  60. data/include/natalie_parser/node/logical_and_node.hpp +40 -0
  61. data/include/natalie_parser/node/logical_or_node.hpp +40 -0
  62. data/include/natalie_parser/node/match_node.hpp +38 -0
  63. data/include/natalie_parser/node/module_node.hpp +32 -0
  64. data/include/natalie_parser/node/multiple_assignment_arg_node.hpp +32 -0
  65. data/include/natalie_parser/node/multiple_assignment_node.hpp +37 -0
  66. data/include/natalie_parser/node/next_node.hpp +37 -0
  67. data/include/natalie_parser/node/nil_node.hpp +23 -0
  68. data/include/natalie_parser/node/nil_sexp_node.hpp +23 -0
  69. data/include/natalie_parser/node/node.hpp +155 -0
  70. data/include/natalie_parser/node/node_with_args.hpp +47 -0
  71. data/include/natalie_parser/node/not_match_node.hpp +35 -0
  72. data/include/natalie_parser/node/not_node.hpp +37 -0
  73. data/include/natalie_parser/node/nth_ref_node.hpp +27 -0
  74. data/include/natalie_parser/node/op_assign_accessor_node.hpp +74 -0
  75. data/include/natalie_parser/node/op_assign_and_node.hpp +34 -0
  76. data/include/natalie_parser/node/op_assign_node.hpp +47 -0
  77. data/include/natalie_parser/node/op_assign_or_node.hpp +34 -0
  78. data/include/natalie_parser/node/pin_node.hpp +33 -0
  79. data/include/natalie_parser/node/range_node.hpp +52 -0
  80. data/include/natalie_parser/node/redo_node.hpp +20 -0
  81. data/include/natalie_parser/node/regexp_node.hpp +36 -0
  82. data/include/natalie_parser/node/retry_node.hpp +20 -0
  83. data/include/natalie_parser/node/return_node.hpp +34 -0
  84. data/include/natalie_parser/node/safe_call_node.hpp +31 -0
  85. data/include/natalie_parser/node/sclass_node.hpp +37 -0
  86. data/include/natalie_parser/node/self_node.hpp +23 -0
  87. data/include/natalie_parser/node/shadow_arg_node.hpp +40 -0
  88. data/include/natalie_parser/node/shell_node.hpp +32 -0
  89. data/include/natalie_parser/node/splat_node.hpp +39 -0
  90. data/include/natalie_parser/node/splat_value_node.hpp +32 -0
  91. data/include/natalie_parser/node/stabby_proc_node.hpp +29 -0
  92. data/include/natalie_parser/node/string_node.hpp +42 -0
  93. data/include/natalie_parser/node/super_node.hpp +44 -0
  94. data/include/natalie_parser/node/symbol_key_node.hpp +19 -0
  95. data/include/natalie_parser/node/symbol_node.hpp +30 -0
  96. data/include/natalie_parser/node/to_array_node.hpp +33 -0
  97. data/include/natalie_parser/node/true_node.hpp +23 -0
  98. data/include/natalie_parser/node/unary_op_node.hpp +41 -0
  99. data/include/natalie_parser/node/undef_node.hpp +31 -0
  100. data/include/natalie_parser/node/until_node.hpp +21 -0
  101. data/include/natalie_parser/node/while_node.hpp +52 -0
  102. data/include/natalie_parser/node/yield_node.hpp +29 -0
  103. data/include/natalie_parser/node.hpp +89 -0
  104. data/include/natalie_parser/parser.hpp +218 -0
  105. data/include/natalie_parser/token.hpp +842 -0
  106. data/include/tm/defer.hpp +34 -0
  107. data/include/tm/hashmap.hpp +826 -0
  108. data/include/tm/macros.hpp +16 -0
  109. data/include/tm/optional.hpp +223 -0
  110. data/include/tm/owned_ptr.hpp +186 -0
  111. data/include/tm/recursion_guard.hpp +156 -0
  112. data/include/tm/shared_ptr.hpp +259 -0
  113. data/include/tm/string.hpp +1447 -0
  114. data/include/tm/tests.hpp +78 -0
  115. data/include/tm/vector.hpp +796 -0
  116. data/lib/natalie_parser/sexp.rb +36 -0
  117. data/lib/natalie_parser/version.rb +5 -0
  118. data/lib/natalie_parser.rb +3 -0
  119. data/natalie_parser.gemspec +23 -0
  120. data/src/lexer/interpolated_string_lexer.cpp +88 -0
  121. data/src/lexer/regexp_lexer.cpp +95 -0
  122. data/src/lexer/word_array_lexer.cpp +134 -0
  123. data/src/lexer.cpp +1703 -0
  124. data/src/node/alias_node.cpp +11 -0
  125. data/src/node/assignment_node.cpp +33 -0
  126. data/src/node/begin_node.cpp +29 -0
  127. data/src/node/begin_rescue_node.cpp +33 -0
  128. data/src/node/class_node.cpp +22 -0
  129. data/src/node/interpolated_regexp_node.cpp +19 -0
  130. data/src/node/interpolated_shell_node.cpp +25 -0
  131. data/src/node/interpolated_string_node.cpp +111 -0
  132. data/src/node/interpolated_symbol_node.cpp +25 -0
  133. data/src/node/match_node.cpp +14 -0
  134. data/src/node/module_node.cpp +21 -0
  135. data/src/node/multiple_assignment_node.cpp +37 -0
  136. data/src/node/node.cpp +10 -0
  137. data/src/node/node_with_args.cpp +35 -0
  138. data/src/node/op_assign_node.cpp +36 -0
  139. data/src/node/string_node.cpp +33 -0
  140. data/src/parser.cpp +2972 -0
  141. data/src/token.cpp +27 -0
  142. metadata +186 -0
data/src/parser.cpp ADDED
@@ -0,0 +1,2972 @@
1
+ #include "natalie_parser/parser.hpp"
2
+ #include "natalie_parser/creator/debug_creator.hpp"
3
+
4
+ namespace NatalieParser {
5
+
6
+ enum class Parser::Precedence {
7
+ LOWEST,
8
+ ARRAY, // []
9
+ WORD_ARRAY, // %w[]
10
+ HASH, // {}
11
+ CASE, // case/when/else
12
+ COMPOSITION, // and/or
13
+ EXPR_MODIFIER, // if/unless/while/until
14
+ TERNARY_FALSE, // _ ? _ : (_)
15
+ ASSIGNMENT_RHS, // a = (_)
16
+ INLINE_RESCUE, // foo rescue 2
17
+ ITER_BLOCK, // do |n| ... end
18
+ BARE_CALL_ARG, // foo (_), b
19
+ OP_ASSIGNMENT, // += -= *= **= /= %= |= &= ^= >>= <<= ||= &&=
20
+ TERNARY_TRUE, // _ ? (_) : _
21
+ CALL_ARG, // foo( (_), b )
22
+ TERNARY_QUESTION, // (_) ? _ : _
23
+ LOGICAL_OR, // ||
24
+ LOGICAL_AND, // &&
25
+ ASSIGNMENT_LHS, // (_) = 1
26
+ SPLAT, // *args, **kwargs
27
+ RANGE, // ..
28
+ LOGICAL_NOT, // not
29
+ EQUALITY, // <=> == === != =~ !~
30
+ LESS_GREATER, // <= < > >=
31
+ BITWISE_OR, // ^ |
32
+ BITWISE_AND, // &
33
+ BITWISE_SHIFT, // << >>
34
+ DEF_ARG, // def foo( (_), b ) and { | (_), b | ... }
35
+ SUM, // + -
36
+ PRODUCT, // * / %
37
+ NUMBER_DOT, // 2.bar 2&.bar
38
+ UNARY_MINUS, // -
39
+ EXPONENT, // **
40
+ UNARY_PLUS, // ! ~ +
41
+ ITER_CURLY, // { |n| ... }
42
+ CONSTANT_RESOLUTION, // ::
43
+ DOT, // foo.bar foo&.bar
44
+ CALL, // foo()
45
+ REF, // foo[1] / foo[1] = 2
46
+ };
47
+
48
+ bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence current_precedence) {
49
+ auto next_precedence = get_precedence(token, left);
50
+
51
+ // printf("token %d, left %d, current_precedence %d, next_precedence %d\n", (int)token.type(), (int)left->type(), (int)current_precedence, (int)next_precedence);
52
+
53
+ if (left->is_symbol_key()) {
54
+ // Symbol keys are handled by parse_hash and parse_call_hash_args,
55
+ // so return as soon as possible to one of those functions.
56
+ return false;
57
+ }
58
+
59
+ if (next_precedence == Precedence::ITER_BLOCK) {
60
+ // Simple precedence comparison to the nearest neighbor is not
61
+ // sufficient for block association. For example, the following
62
+ // code, if looking at precedence rules alone, would attach the
63
+ // block to the '+' op, which would be incorrect:
64
+ //
65
+ // bar + baz do ... end
66
+ // ^ block should NOT attach here
67
+ //
68
+ // Rather, we want:
69
+ //
70
+ // bar + baz do ... end
71
+ // ^ block attaches here
72
+ //
73
+ // But changing the precedence order cannot fix this, because in
74
+ // many cases, we need the block to attach farther left, e.g.:
75
+ //
76
+ // foo bar + baz do ... end
77
+ // ^ block attaches here
78
+ //
79
+ // Thus the answer is to keep track of how many calls deep we are
80
+ // When our call depth is zero, that's where we attach the block.
81
+ //
82
+ // NOTE: `m_call_depth` should probably be called
83
+ // `m_call_that_can_accept_a_block_depth`, but that's a bit long.
84
+ //
85
+ return m_call_depth.last() == 0;
86
+ }
87
+
88
+ if (next_precedence == Precedence::ITER_CURLY)
89
+ return left->is_callable();
90
+
91
+ return next_precedence > current_precedence;
92
+ }
93
+
94
+ Parser::Precedence Parser::get_precedence(Token &token, SharedPtr<Node> left) {
95
+ switch (token.type()) {
96
+ case Token::Type::Plus:
97
+ return left ? Precedence::SUM : Precedence::UNARY_PLUS;
98
+ case Token::Type::Minus:
99
+ return left ? Precedence::SUM : Precedence::UNARY_MINUS;
100
+ case Token::Type::Equal:
101
+ return Precedence::ASSIGNMENT_LHS;
102
+ case Token::Type::AmpersandAmpersandEqual:
103
+ case Token::Type::AmpersandEqual:
104
+ case Token::Type::CaretEqual:
105
+ case Token::Type::LeftShiftEqual:
106
+ case Token::Type::MinusEqual:
107
+ case Token::Type::PipePipeEqual:
108
+ case Token::Type::PercentEqual:
109
+ case Token::Type::PipeEqual:
110
+ case Token::Type::PlusEqual:
111
+ case Token::Type::RightShiftEqual:
112
+ case Token::Type::SlashEqual:
113
+ case Token::Type::StarEqual:
114
+ case Token::Type::StarStarEqual:
115
+ return Precedence::OP_ASSIGNMENT;
116
+ case Token::Type::Ampersand:
117
+ return Precedence::BITWISE_AND;
118
+ case Token::Type::Caret:
119
+ case Token::Type::Pipe:
120
+ return Precedence::BITWISE_OR;
121
+ case Token::Type::Comma:
122
+ // NOTE: the only time this precedence is used is for multiple assignment
123
+ return Precedence::ARRAY;
124
+ case Token::Type::LeftShift:
125
+ case Token::Type::RightShift:
126
+ return Precedence::BITWISE_SHIFT;
127
+ case Token::Type::LParen:
128
+ return Precedence::CALL;
129
+ case Token::Type::AndKeyword:
130
+ case Token::Type::OrKeyword:
131
+ return Precedence::COMPOSITION;
132
+ case Token::Type::ConstantResolution:
133
+ return Precedence::CONSTANT_RESOLUTION;
134
+ case Token::Type::Dot:
135
+ case Token::Type::SafeNavigation:
136
+ if (left && left->is_numeric())
137
+ return Precedence::NUMBER_DOT;
138
+ return Precedence::DOT;
139
+ case Token::Type::EqualEqual:
140
+ case Token::Type::EqualEqualEqual:
141
+ case Token::Type::NotEqual:
142
+ case Token::Type::Match:
143
+ case Token::Type::NotMatch:
144
+ return Precedence::EQUALITY;
145
+ case Token::Type::StarStar:
146
+ return Precedence::EXPONENT;
147
+ case Token::Type::IfKeyword:
148
+ case Token::Type::UnlessKeyword:
149
+ case Token::Type::WhileKeyword:
150
+ case Token::Type::UntilKeyword:
151
+ return Precedence::EXPR_MODIFIER;
152
+ case Token::Type::RescueKeyword:
153
+ return Precedence::INLINE_RESCUE;
154
+ case Token::Type::DoKeyword:
155
+ return Precedence::ITER_BLOCK;
156
+ case Token::Type::LCurlyBrace:
157
+ return Precedence::ITER_CURLY;
158
+ case Token::Type::Comparison:
159
+ case Token::Type::LessThan:
160
+ case Token::Type::LessThanOrEqual:
161
+ case Token::Type::GreaterThan:
162
+ case Token::Type::GreaterThanOrEqual:
163
+ return Precedence::LESS_GREATER;
164
+ case Token::Type::AmpersandAmpersand:
165
+ return Precedence::LOGICAL_AND;
166
+ case Token::Type::NotKeyword:
167
+ return Precedence::LOGICAL_NOT;
168
+ case Token::Type::PipePipe:
169
+ return Precedence::LOGICAL_OR;
170
+ case Token::Type::Percent:
171
+ case Token::Type::Slash:
172
+ case Token::Type::Star:
173
+ return Precedence::PRODUCT;
174
+ case Token::Type::DotDot:
175
+ case Token::Type::DotDotDot:
176
+ return Precedence::RANGE;
177
+ case Token::Type::LBracket:
178
+ case Token::Type::LBracketRBracket: {
179
+ auto current = current_token();
180
+ if (left && treat_left_bracket_as_element_reference(left, current))
181
+ return Precedence::REF;
182
+ break;
183
+ }
184
+ case Token::Type::TernaryQuestion:
185
+ return Precedence::TERNARY_QUESTION;
186
+ case Token::Type::TernaryColon:
187
+ return Precedence::TERNARY_FALSE;
188
+ case Token::Type::Not:
189
+ case Token::Type::Tilde:
190
+ return Precedence::UNARY_PLUS;
191
+ default:
192
+ break;
193
+ }
194
+ auto current = current_token();
195
+ if (left && is_first_arg_of_call_without_parens(left, current))
196
+ return Precedence::CALL;
197
+ return Precedence::LOWEST;
198
+ }
199
+
200
+ SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHashmap &locals) {
201
+ skip_newlines();
202
+
203
+ m_precedence_stack.push(precedence);
204
+
205
+ auto null_fn = null_denotation(current_token().type());
206
+ if (!null_fn)
207
+ throw_unexpected("expression");
208
+
209
+ auto left = (this->*null_fn)(locals);
210
+
211
+ while (current_token().is_valid()) {
212
+ auto token = current_token();
213
+ if (!higher_precedence(token, left, precedence))
214
+ break;
215
+ auto left_fn = left_denotation(token, left, precedence);
216
+ if (!left_fn)
217
+ throw_unexpected(token, "expression");
218
+ left = (this->*left_fn)(left, locals);
219
+ m_precedence_stack.pop();
220
+ m_precedence_stack.push(precedence);
221
+ }
222
+
223
+ m_precedence_stack.pop();
224
+
225
+ return left;
226
+ }
227
+
228
+ SharedPtr<Node> Parser::tree() {
229
+ skip_newlines();
230
+ SharedPtr<Node> tree = new BlockNode { current_token() };
231
+ validate_current_token();
232
+ LocalsHashmap locals { TM::HashType::TMString };
233
+ skip_newlines();
234
+ while (!current_token().is_eof()) {
235
+ auto exp = parse_expression(Precedence::LOWEST, locals);
236
+ tree->as_block_node().add_node(exp);
237
+ validate_current_token();
238
+ next_expression();
239
+ }
240
+ if (tree->as_block_node().has_one_node())
241
+ tree = tree->as_block_node().take_first_node();
242
+ return tree;
243
+ }
244
+
245
+ SharedPtr<BlockNode> Parser::parse_body(LocalsHashmap &locals, Precedence precedence, std::function<bool(Token::Type)> is_end, bool allow_rescue) {
246
+ m_call_depth.push(0);
247
+ SharedPtr<BlockNode> body = new BlockNode { current_token() };
248
+ validate_current_token();
249
+ skip_newlines();
250
+ while (!current_token().is_eof() && !is_end(current_token().type())) {
251
+ if (allow_rescue && current_token().type() == Token::Type::RescueKeyword) {
252
+ auto token = body->token();
253
+ SharedPtr<BeginNode> begin_node = new BeginNode { body->token(), body };
254
+ parse_rest_of_begin(begin_node.ref(), locals);
255
+ rewind(); // so the 'end' keyword can be consumed by our caller
256
+ return new BlockNode { token, begin_node.static_cast_as<Node>() };
257
+ }
258
+ auto exp = parse_expression(precedence, locals);
259
+ body->add_node(exp);
260
+ if (is_end(current_token().type()))
261
+ break;
262
+ next_expression();
263
+ }
264
+ m_call_depth.pop();
265
+ return body;
266
+ }
267
+
268
+ SharedPtr<BlockNode> Parser::parse_body(LocalsHashmap &locals, Precedence precedence, Token::Type end_token_type, bool allow_rescue) {
269
+ return parse_body(
270
+ locals,
271
+ precedence,
272
+ [&](Token::Type type) { return type == end_token_type; },
273
+ allow_rescue);
274
+ }
275
+
276
+ SharedPtr<BlockNode> Parser::parse_def_body(LocalsHashmap &locals) {
277
+ return parse_body(locals, Precedence::LOWEST, Token::Type::EndKeyword, true);
278
+ }
279
+
280
+ SharedPtr<Node> Parser::parse_alias(LocalsHashmap &locals) {
281
+ auto token = current_token();
282
+ advance();
283
+ SharedPtr<SymbolNode> new_name = parse_alias_arg(locals, "alias new name (first argument)", false);
284
+ auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)", true);
285
+ return new AliasNode { token, new_name, existing_name };
286
+ }
287
+
288
+ SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char *expected_message, bool reinsert_collapsed_newline) {
289
+ auto token = current_token();
290
+ switch (token.type()) {
291
+ // TODO: handle Constant too
292
+ case Token::Type::BareName: {
293
+ advance();
294
+ return new SymbolNode { token, token.literal_string() };
295
+ }
296
+ case Token::Type::Symbol:
297
+ return parse_symbol(locals).static_cast_as<SymbolNode>();
298
+ case Token::Type::InterpolatedSymbolBegin:
299
+ return parse_interpolated_symbol(locals).static_cast_as<SymbolNode>();
300
+ default:
301
+ if (token.is_operator() || token.is_keyword()) {
302
+ advance();
303
+ if (token.can_precede_collapsible_newline() && reinsert_collapsed_newline) {
304
+ // Some operators at the end of a line cause the newlines to be collapsed:
305
+ //
306
+ // foo <<
307
+ // bar
308
+ //
309
+ // ...but in this case (an alias), collapsing the newline was a mistake:
310
+ //
311
+ // alias foo <<
312
+ // def bar; end
313
+ //
314
+ // So, we'll put the newline back.
315
+ m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column() });
316
+ }
317
+ return new SymbolNode { token, new String(token.type_value()) };
318
+ } else {
319
+ throw_unexpected(expected_message);
320
+ }
321
+ }
322
+ }
323
+
324
+ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
325
+ SharedPtr<ArrayNode> array = new ArrayNode { current_token() };
326
+ if (current_token().type() == Token::Type::LBracketRBracket) {
327
+ advance();
328
+ return array.static_cast_as<Node>();
329
+ }
330
+ advance(); // [
331
+ m_call_depth.push(0);
332
+ auto add_node = [&]() -> SharedPtr<Node> {
333
+ auto token = current_token();
334
+ if (token.is_rbracket()) {
335
+ advance();
336
+ return array.static_cast_as<Node>();
337
+ }
338
+ if (token.type() == Token::Type::SymbolKey) {
339
+ array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket));
340
+ expect(Token::Type::RBracket, "array closing bracket");
341
+ advance();
342
+ return array.static_cast_as<Node>();
343
+ }
344
+ auto value = parse_expression(Precedence::ARRAY, locals);
345
+ token = current_token();
346
+ if (token.is_hash_rocket()) {
347
+ array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, value));
348
+ expect(Token::Type::RBracket, "array closing bracket");
349
+ advance();
350
+ return array.static_cast_as<Node>();
351
+ }
352
+ array->add_node(value);
353
+ return {};
354
+ };
355
+ auto ret = add_node();
356
+ if (ret) return ret;
357
+ while (current_token().is_comma()) {
358
+ advance();
359
+ ret = add_node();
360
+ if (ret) return ret;
361
+ }
362
+ expect(Token::Type::RBracket, "array closing bracket");
363
+ advance(); // ]
364
+ m_call_depth.pop();
365
+ return array.static_cast_as<Node>();
366
+ }
367
+
368
+ void Parser::parse_comma_separated_expressions(ArrayNode &array, LocalsHashmap &locals) {
369
+ array.add_node(parse_expression(Precedence::ARRAY, locals));
370
+ while (current_token().type() == Token::Type::Comma) {
371
+ advance();
372
+ array.add_node(parse_expression(Precedence::ARRAY, locals));
373
+ }
374
+ }
375
+
376
+ SharedPtr<Node> Parser::parse_back_ref(LocalsHashmap &) {
377
+ auto token = current_token();
378
+ advance();
379
+ return new BackRefNode { token, token.literal_string()->at(0) };
380
+ }
381
+
382
+ SharedPtr<Node> Parser::parse_begin(LocalsHashmap &locals) {
383
+ auto token = current_token();
384
+ advance();
385
+ next_expression();
386
+ auto is_end = [&](Token::Type type) { return type == Token::Type::RescueKeyword || type == Token::Type::ElseKeyword || type == Token::Type::EnsureKeyword || type == Token::Type::EndKeyword; };
387
+ auto body = parse_body(locals, Precedence::LOWEST, is_end, true);
388
+ if (!is_end(current_token().type()))
389
+ throw_unexpected("begin: rescue, else, ensure, or end");
390
+
391
+ SharedPtr<BeginNode> begin_node = new BeginNode { token, body };
392
+ parse_rest_of_begin(begin_node.ref(), locals);
393
+
394
+ // a begin/end with nothing else just becomes a BlockNode
395
+ if (begin_node->can_be_simple_block()) {
396
+ auto body = begin_node->body();
397
+ // if this BlockNode has a single child node, and
398
+ // there is no trailing if/unless/while/until modifier,
399
+ // we can just return the one node
400
+ if (body->has_one_node() && !current_token().is_expression_modifier())
401
+ return body->take_first_node();
402
+ return body.static_cast_as<Node>();
403
+ }
404
+
405
+ return begin_node.static_cast_as<Node>();
406
+ }
407
+
408
+ void Parser::parse_rest_of_begin(BeginNode &begin_node, LocalsHashmap &locals) {
409
+ auto is_end_of_rescue = [&](Token::Type type) { return type == Token::Type::RescueKeyword || type == Token::Type::ElseKeyword || type == Token::Type::EnsureKeyword || type == Token::Type::EndKeyword; };
410
+ auto is_end_of_else = [&](Token::Type type) { return type == Token::Type::EnsureKeyword || type == Token::Type::EndKeyword; };
411
+ while (!current_token().is_eof() && !current_token().is_end_keyword()) {
412
+ switch (current_token().type()) {
413
+ case Token::Type::RescueKeyword: {
414
+ SharedPtr<BeginRescueNode> rescue_node = new BeginRescueNode { current_token() };
415
+ advance();
416
+ if (!current_token().is_end_of_line() && current_token().type() != Token::Type::HashRocket) {
417
+ auto name = parse_expression(Precedence::BARE_CALL_ARG, locals);
418
+ rescue_node->add_exception_node(name);
419
+ while (current_token().is_comma()) {
420
+ advance();
421
+ auto name = parse_expression(Precedence::BARE_CALL_ARG, locals);
422
+ rescue_node->add_exception_node(name);
423
+ }
424
+ }
425
+ if (current_token().is_hash_rocket()) {
426
+ advance();
427
+ SharedPtr<IdentifierNode> name;
428
+ switch (current_token().type()) {
429
+ case Token::Type::BareName:
430
+ name = new IdentifierNode { current_token(), current_token().literal_string() };
431
+ advance();
432
+ break;
433
+ default:
434
+ throw_unexpected("exception name");
435
+ }
436
+ name->add_to_locals(locals);
437
+ rescue_node->set_exception_name(name);
438
+ }
439
+ next_expression();
440
+ auto body = parse_body(locals, Precedence::LOWEST, is_end_of_rescue, false);
441
+ if (!is_end_of_rescue(current_token().type()))
442
+ throw_unexpected("begin: rescue, else, ensure, or end");
443
+ rescue_node->set_body(body);
444
+ begin_node.add_rescue_node(rescue_node);
445
+ break;
446
+ }
447
+ case Token::Type::ElseKeyword: {
448
+ advance();
449
+ next_expression();
450
+ auto body = parse_body(locals, Precedence::LOWEST, is_end_of_else, false);
451
+ if (!is_end_of_else(current_token().type()))
452
+ throw_unexpected("begin: ensure or end");
453
+ begin_node.set_else_body(body);
454
+ break;
455
+ }
456
+ case Token::Type::EnsureKeyword: {
457
+ advance();
458
+ next_expression();
459
+ auto body = parse_body(locals, Precedence::LOWEST);
460
+ begin_node.set_ensure_body(body);
461
+ break;
462
+ }
463
+ default:
464
+ throw_unexpected("begin end");
465
+ }
466
+ }
467
+ expect(Token::Type::EndKeyword, "begin/rescue/ensure end");
468
+ advance();
469
+ }
470
+
471
+ SharedPtr<Node> Parser::parse_begin_block(LocalsHashmap &locals) {
472
+ bool is_top_level = m_precedence_stack.size() == 1;
473
+ if (!is_top_level)
474
+ throw SyntaxError { "BEGIN is permitted only at toplevel" };
475
+ auto token = current_token();
476
+ advance(); // BEGIN
477
+ expect(Token::Type::LCurlyBrace, "BEGIN {}");
478
+ auto node = new BeginBlockNode { token };
479
+ return parse_iter_expression(node, locals);
480
+ }
481
+
482
+ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
483
+ auto token = current_token();
484
+ advance();
485
+ auto end_node = parse_expression(Precedence::LOWEST, locals);
486
+ return new RangeNode {
487
+ token,
488
+ new NilNode { token },
489
+ end_node,
490
+ token.type() == Token::Type::DotDotDot
491
+ };
492
+ }
493
+
494
+ SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
495
+ auto token = current_token();
496
+ advance();
497
+ auto value = parse_expression(Precedence::UNARY_PLUS, locals);
498
+ return new BlockPassNode { token, value };
499
+ }
500
+
501
+ SharedPtr<Node> Parser::parse_bool(LocalsHashmap &) {
502
+ auto token = current_token();
503
+ switch (current_token().type()) {
504
+ case Token::Type::TrueKeyword:
505
+ advance();
506
+ return new TrueNode { token };
507
+ case Token::Type::FalseKeyword:
508
+ advance();
509
+ return new FalseNode { token };
510
+ default:
511
+ TM_UNREACHABLE();
512
+ }
513
+ }
514
+
515
+ SharedPtr<Node> Parser::parse_break(LocalsHashmap &locals) {
516
+ auto token = current_token();
517
+ advance();
518
+ if (current_token().is_lparen()) {
519
+ advance();
520
+ if (current_token().is_rparen()) {
521
+ advance();
522
+ return new BreakNode { token, new NilSexpNode { token } };
523
+ } else {
524
+ auto arg = parse_expression(Precedence::BARE_CALL_ARG, locals);
525
+ expect(Token::Type::RParen, "break closing paren");
526
+ advance();
527
+ return new BreakNode { token, arg };
528
+ }
529
+ } else if (current_token().can_be_first_arg_of_implicit_call()) {
530
+ auto value = parse_expression(Precedence::BARE_CALL_ARG, locals);
531
+ if (current_token().is_comma()) {
532
+ SharedPtr<ArrayNode> array = new ArrayNode { token };
533
+ array->add_node(value);
534
+ while (current_token().is_comma()) {
535
+ advance();
536
+ array->add_node(parse_expression(Precedence::BARE_CALL_ARG, locals));
537
+ }
538
+ value = array.static_cast_as<Node>();
539
+ }
540
+ return new BreakNode { token, value };
541
+ }
542
+ return new BreakNode { token };
543
+ }
544
+
545
+ SharedPtr<Node> Parser::parse_case(LocalsHashmap &locals) {
546
+ auto case_token = current_token();
547
+ advance(); // case
548
+ SharedPtr<Node> subject;
549
+ switch (current_token().type()) {
550
+ case Token::Type::WhenKeyword:
551
+ subject = new NilNode { case_token };
552
+ break;
553
+ case Token::Type::Newline:
554
+ case Token::Type::Semicolon:
555
+ advance();
556
+ subject = new NilNode { case_token };
557
+ break;
558
+ default:
559
+ subject = parse_expression(Precedence::CASE, locals);
560
+ next_expression();
561
+ }
562
+ SharedPtr<CaseNode> node = new CaseNode { case_token, subject };
563
+ while (!current_token().is_end_keyword()) {
564
+ auto token = current_token();
565
+ switch (token.type()) {
566
+ case Token::Type::WhenKeyword: {
567
+ advance();
568
+ SharedPtr<ArrayNode> condition_array = new ArrayNode { token };
569
+ parse_comma_separated_expressions(condition_array.ref(), locals);
570
+ if (current_token().type() == Token::Type::ThenKeyword) {
571
+ advance();
572
+ skip_newlines();
573
+ } else {
574
+ next_expression();
575
+ }
576
+ auto body = parse_case_when_body(locals);
577
+ auto when_node = new CaseWhenNode { token, condition_array.static_cast_as<Node>(), body };
578
+ node->add_node(when_node);
579
+ break;
580
+ }
581
+ case Token::Type::InKeyword: {
582
+ advance();
583
+ SharedPtr<Node> pattern = parse_case_in_patterns(locals);
584
+ if (current_token().type() == Token::Type::ThenKeyword) {
585
+ advance();
586
+ skip_newlines();
587
+ } else {
588
+ next_expression();
589
+ }
590
+ auto body = parse_case_in_body(locals);
591
+ auto in_node = new CaseInNode { token, pattern, body };
592
+ node->add_node(in_node);
593
+ break;
594
+ }
595
+ case Token::Type::ElseKeyword: {
596
+ if (node->nodes().is_empty())
597
+ throw_unexpected("case 'when' or 'in'");
598
+ advance();
599
+ skip_newlines();
600
+ SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
601
+ node->set_else_node(body);
602
+ expect(Token::Type::EndKeyword, "case end");
603
+ break;
604
+ }
605
+ default:
606
+ throw_unexpected("case when keyword");
607
+ }
608
+ }
609
+ expect(Token::Type::EndKeyword, "case end");
610
+ advance();
611
+ return node.static_cast_as<Node>();
612
+ }
613
+
614
+ SharedPtr<BlockNode> Parser::parse_case_in_body(LocalsHashmap &locals) {
615
+ return parse_case_when_body(locals);
616
+ }
617
+
618
+ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
619
+ auto token = current_token();
620
+ SharedPtr<Node> node;
621
+ switch (token.type()) {
622
+ case Token::Type::BareName:
623
+ advance();
624
+ node = new IdentifierNode { token, true };
625
+ break;
626
+ case Token::Type::Caret:
627
+ advance();
628
+ expect(Token::Type::BareName, "pinned variable name");
629
+ node = new PinNode { token, new IdentifierNode { current_token(), true } };
630
+ advance();
631
+ break;
632
+ case Token::Type::Constant:
633
+ node = parse_constant(locals);
634
+ break;
635
+ case Token::Type::LBracketRBracket:
636
+ advance();
637
+ node = new ArrayPatternNode { token };
638
+ break;
639
+ case Token::Type::LBracket: {
640
+ // TODO: might need to keep track of and pass along precedence value?
641
+ advance();
642
+ SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
643
+ if (current_token().is_rbracket()) {
644
+ advance();
645
+ node = array.static_cast_as<Node>();
646
+ break;
647
+ }
648
+ array->add_node(parse_case_in_pattern(locals));
649
+ while (current_token().is_comma()) {
650
+ advance();
651
+ array->add_node(parse_case_in_pattern(locals));
652
+ }
653
+ expect(Token::Type::RBracket, "array pattern closing bracket");
654
+ advance();
655
+ node = array.static_cast_as<Node>();
656
+ break;
657
+ }
658
+ case Token::Type::LCurlyBrace: {
659
+ advance();
660
+ SharedPtr<HashPatternNode> hash = new HashPatternNode { token };
661
+ if (current_token().type() == Token::Type::RCurlyBrace) {
662
+ advance();
663
+ node = hash.static_cast_as<Node>();
664
+ break;
665
+ }
666
+ expect(Token::Type::SymbolKey, "hash pattern symbol key");
667
+ hash->add_node(parse_symbol(locals));
668
+ hash->add_node(parse_case_in_pattern(locals));
669
+ while (current_token().is_comma()) {
670
+ advance(); // ,
671
+ expect(Token::Type::Symbol, "hash pattern symbol");
672
+ hash->add_node(parse_symbol(locals));
673
+ hash->add_node(parse_case_in_pattern(locals));
674
+ }
675
+ expect(Token::Type::RCurlyBrace, "hash pattern closing brace");
676
+ advance();
677
+ node = hash.static_cast_as<Node>();
678
+ break;
679
+ }
680
+ case Token::Type::Bignum:
681
+ case Token::Type::Fixnum:
682
+ case Token::Type::Float:
683
+ node = parse_lit(locals);
684
+ break;
685
+ case Token::Type::Star: {
686
+ auto splat_token = current_token();
687
+ advance();
688
+ SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
689
+ switch (current_token().type()) {
690
+ case Token::Type::BareName:
691
+ case Token::Type::Constant: {
692
+ auto name = new IdentifierNode { current_token(), true };
693
+ name->prepend_to_name('*');
694
+ array->add_node(name);
695
+ advance();
696
+ break;
697
+ }
698
+ default:
699
+ array->add_node(new SplatNode { splat_token });
700
+ }
701
+ node = array.static_cast_as<Node>();
702
+ break;
703
+ }
704
+ case Token::Type::String:
705
+ node = parse_string(locals);
706
+ break;
707
+ case Token::Type::Symbol:
708
+ node = parse_symbol(locals);
709
+ break;
710
+ default:
711
+ throw_unexpected("case in pattern");
712
+ }
713
+ token = current_token();
714
+ if (token.is_hash_rocket()) {
715
+ advance();
716
+ expect(Token::Type::BareName, "pattern name");
717
+ token = current_token();
718
+ advance();
719
+ auto identifier = new IdentifierNode { token, true };
720
+ node = new AssignmentNode { token, identifier, node };
721
+ }
722
+ return node;
723
+ }
724
+
725
+ SharedPtr<Node> Parser::parse_case_in_patterns(LocalsHashmap &locals) {
726
+ Vector<SharedPtr<Node>> patterns;
727
+ patterns.push(parse_case_in_pattern(locals));
728
+ while (current_token().type() == Token::Type::Pipe) {
729
+ advance();
730
+ patterns.push(parse_case_in_pattern(locals));
731
+ }
732
+ while (current_token().is_comma()) {
733
+ advance();
734
+ auto last_pattern = patterns.last();
735
+ auto next_pattern = parse_case_in_pattern(locals);
736
+ if (last_pattern->type() == Node::Type::ArrayPattern) {
737
+ last_pattern.static_cast_as<ArrayPatternNode>()->add_node(next_pattern);
738
+ } else {
739
+ patterns.pop();
740
+ auto array_pattern = new ArrayPatternNode { last_pattern->token() };
741
+ array_pattern->add_node(last_pattern);
742
+ array_pattern->add_node(next_pattern);
743
+ patterns.push(array_pattern);
744
+ }
745
+ }
746
+ assert(patterns.size() > 0);
747
+ if (patterns.size() == 1) {
748
+ return patterns.pop_front();
749
+ } else {
750
+ auto first = patterns.pop_front();
751
+ auto second = patterns.pop_front();
752
+ auto pattern = new LogicalOrNode { first->token(), first, second };
753
+ while (!patterns.is_empty()) {
754
+ auto next = patterns.pop_front();
755
+ pattern = new LogicalOrNode { next->token(), pattern, next };
756
+ }
757
+ return pattern;
758
+ }
759
+ }
760
+
761
+ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
762
+ SharedPtr<BlockNode> body = new BlockNode { current_token() };
763
+ validate_current_token();
764
+ skip_newlines();
765
+ while (!current_token().is_eof() && !current_token().is_when_keyword() && !current_token().is_else_keyword() && !current_token().is_end_keyword()) {
766
+ auto exp = parse_expression(Precedence::LOWEST, locals);
767
+ body->add_node(exp);
768
+ validate_current_token();
769
+ next_expression();
770
+ }
771
+ if (!current_token().is_when_keyword() && !current_token().is_else_keyword() && !current_token().is_end_keyword())
772
+ throw_unexpected("case: when, else, or end");
773
+ return body;
774
+ }
775
+
776
+ SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
777
+ Token name_token;
778
+ if (current_token().type() == Token::Type::ConstantResolution) {
779
+ name_token = peek_token();
780
+ } else {
781
+ name_token = current_token();
782
+ }
783
+ if (name_token.type() != Token::Type::Constant)
784
+ throw SyntaxError { "class/module name must be CONSTANT" };
785
+ return parse_expression(Precedence::LESS_GREATER, locals);
786
+ }
787
+
788
+ SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
789
+ auto token = current_token();
790
+ if (peek_token().type() == Token::Type::LeftShift)
791
+ return parse_sclass(locals);
792
+ advance();
793
+ LocalsHashmap our_locals { TM::HashType::TMString };
794
+ SharedPtr<Node> name = parse_class_or_module_name(our_locals);
795
+ SharedPtr<Node> superclass;
796
+ if (current_token().type() == Token::Type::LessThan) {
797
+ advance();
798
+ superclass = parse_expression(Precedence::LOWEST, our_locals);
799
+ } else {
800
+ superclass = new NilNode { token };
801
+ }
802
+ SharedPtr<BlockNode> body = parse_body(our_locals, Precedence::LOWEST, Token::Type::EndKeyword, true);
803
+ expect(Token::Type::EndKeyword, "class end");
804
+ advance();
805
+ return new ClassNode { token, name, superclass, body };
806
+ };
807
+
808
+ SharedPtr<Node> Parser::parse_multiple_assignment_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
809
+ if (!left->is_assignable())
810
+ throw_unexpected("assignment =");
811
+ SharedPtr<MultipleAssignmentNode> list = new MultipleAssignmentNode { left->token() };
812
+ list->add_node(left);
813
+ while (current_token().is_comma()) {
814
+ advance();
815
+ if (current_token().is_rparen() || current_token().is_equal()) {
816
+ // trailing comma with no additional identifier
817
+ break;
818
+ }
819
+ auto node = parse_assignment_identifier(true, locals);
820
+ list->add_node(node);
821
+ }
822
+ return list.static_cast_as<Node>();
823
+ }
824
+
825
+ SharedPtr<Node> Parser::parse_assignment_identifier(bool allow_splat, LocalsHashmap &locals) {
826
+ auto token = current_token();
827
+ SharedPtr<Node> node;
828
+ switch (token.type()) {
829
+ case Token::Type::BareName:
830
+ case Token::Type::ClassVariable:
831
+ case Token::Type::Constant:
832
+ case Token::Type::GlobalVariable:
833
+ case Token::Type::InstanceVariable:
834
+ node = parse_identifier(locals);
835
+ break;
836
+ case Token::Type::ConstantResolution:
837
+ node = parse_top_level_constant(locals);
838
+ break;
839
+ case Token::Type::LParen: {
840
+ advance(); // (
841
+ node = parse_assignment_identifier(true, locals);
842
+ node = parse_multiple_assignment_expression(node, locals);
843
+ if (!current_token().is_rparen())
844
+ throw_unexpected("closing paren for multiple assignment");
845
+ advance(); // )
846
+ break;
847
+ }
848
+ case Token::Type::Star: {
849
+ if (!allow_splat)
850
+ expect(Token::Type::BareName, "assignment identifier");
851
+ auto splat_token = current_token();
852
+ advance();
853
+ if (current_token().is_assignable()) {
854
+ auto id = parse_assignment_identifier(false, locals);
855
+ node = new SplatNode { splat_token, id };
856
+ } else {
857
+ node = new SplatNode { splat_token };
858
+ }
859
+ break;
860
+ }
861
+ default:
862
+ throw_unexpected("assignment identifier");
863
+ }
864
+ token = current_token();
865
+ bool consumed = false;
866
+ do {
867
+ token = current_token();
868
+ switch (token.type()) {
869
+ case Token::Type::ConstantResolution:
870
+ node = parse_constant_resolution_expression(node, locals);
871
+ break;
872
+ case Token::Type::Dot:
873
+ node = parse_send_expression(node, locals);
874
+ break;
875
+ case Token::Type::LBracket:
876
+ if (treat_left_bracket_as_element_reference(node, token))
877
+ node = parse_ref_expression(node, locals);
878
+ else
879
+ consumed = true;
880
+ break;
881
+ default:
882
+ consumed = true;
883
+ break;
884
+ }
885
+ } while (!consumed);
886
+ return node;
887
+ }
888
+
889
+ SharedPtr<Node> Parser::parse_constant(LocalsHashmap &) {
890
+ auto node = new ConstantNode { current_token() };
891
+ advance();
892
+ return node;
893
+ };
894
+
895
+ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
896
+ auto def_token = current_token();
897
+ advance();
898
+ LocalsHashmap our_locals { TM::HashType::TMString };
899
+ SharedPtr<Node> self_node;
900
+ SharedPtr<String> name = new String("");
901
+ auto token = current_token();
902
+ switch (token.type()) {
903
+ case Token::Type::BareName:
904
+ if (peek_token().type() == Token::Type::Dot) {
905
+ self_node = parse_identifier(locals);
906
+ advance(); // dot
907
+ }
908
+ name = parse_method_name(locals);
909
+ break;
910
+ case Token::Type::Constant:
911
+ if (peek_token().type() == Token::Type::Dot) {
912
+ self_node = parse_constant(locals);
913
+ advance(); // dot
914
+ }
915
+ name = parse_method_name(locals);
916
+ break;
917
+ case Token::Type::SelfKeyword:
918
+ if (peek_token().type() == Token::Type::Dot) {
919
+ self_node = new SelfNode { current_token() };
920
+ advance(); // self
921
+ advance(); // dot
922
+ }
923
+ name = parse_method_name(locals);
924
+ break;
925
+ default: {
926
+ if (token.is_operator() || token.is_keyword()) {
927
+ name = parse_method_name(locals);
928
+ } else {
929
+ self_node = parse_expression(Precedence::DOT, locals);
930
+ expect(Token::Type::Dot, "dot followed by method name");
931
+ advance(); // dot
932
+ name = parse_method_name(locals);
933
+ }
934
+ }
935
+ }
936
+ if (current_token().is_equal() && !current_token().whitespace_precedes()) {
937
+ advance();
938
+ name->append_char('=');
939
+ }
940
+ auto args = Vector<SharedPtr<Node>> {};
941
+ if (current_token().is_lparen()) {
942
+ advance();
943
+ if (current_token().is_rparen()) {
944
+ advance();
945
+ } else {
946
+ parse_def_args(args, our_locals);
947
+ expect(Token::Type::RParen, "args closing paren");
948
+ advance();
949
+ }
950
+ } else if (current_token().is_bare_name() || current_token().is_splat() || current_token().is_symbol_key()) {
951
+ parse_def_args(args, our_locals);
952
+ }
953
+ SharedPtr<BlockNode> body;
954
+ if (current_token().is_equal()) { // one-line method def
955
+ advance(); // =
956
+ if (name->ends_with("=") && !name->ends_with("=="))
957
+ throw SyntaxError { "setter method cannot be defined in an endless method definition" };
958
+ auto exp = parse_expression(Precedence::LOWEST, our_locals);
959
+ body = new BlockNode { exp->token(), exp };
960
+ } else {
961
+ body = parse_def_body(our_locals);
962
+ expect(Token::Type::EndKeyword, "def end");
963
+ advance();
964
+ }
965
+ return new DefNode {
966
+ def_token,
967
+ self_node,
968
+ name,
969
+ args,
970
+ body
971
+ };
972
+ };
973
+
974
+ SharedPtr<Node> Parser::parse_defined(LocalsHashmap &locals) {
975
+ auto token = current_token();
976
+ advance();
977
+ auto arg = parse_expression(Precedence::BARE_CALL_ARG, locals);
978
+ return new DefinedNode { token, arg };
979
+ }
980
+
981
+ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
982
+ parse_def_single_arg(args, locals);
983
+ while (current_token().is_comma()) {
984
+ advance();
985
+ parse_def_single_arg(args, locals);
986
+ }
987
+ }
988
+
989
+ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
990
+ auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
991
+ auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
992
+
993
+ auto token = current_token();
994
+ switch (token.type()) {
995
+ case Token::Type::BareName: {
996
+ if (args_have_keyword())
997
+ throw_unexpected(token, nullptr, "normal arg after keyword arg");
998
+ SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
999
+ advance();
1000
+ arg->add_to_locals(locals);
1001
+ if (current_token().is_equal()) {
1002
+ if (args_have_any_splat())
1003
+ throw_unexpected(token, nullptr, "default value after splat");
1004
+ advance(); // =
1005
+ arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1006
+ }
1007
+ args.push(arg.static_cast_as<Node>());
1008
+ return;
1009
+ }
1010
+ case Token::Type::LParen: {
1011
+ advance();
1012
+ auto sub_args = Vector<SharedPtr<Node>> {};
1013
+ parse_def_args(sub_args, locals);
1014
+ expect(Token::Type::RParen, "nested args closing paren");
1015
+ advance();
1016
+ auto masgn = new MultipleAssignmentArgNode { token };
1017
+ for (auto arg : sub_args) {
1018
+ masgn->add_node(arg);
1019
+ }
1020
+ args.push(masgn);
1021
+ return;
1022
+ }
1023
+ case Token::Type::Star: {
1024
+ if (args_have_any_splat())
1025
+ throw_unexpected(token, nullptr, "splat after keyword splat");
1026
+ advance();
1027
+ SharedPtr<ArgNode> arg;
1028
+ if (current_token().is_bare_name()) {
1029
+ arg = new ArgNode { token, current_token().literal_string() };
1030
+ advance();
1031
+ arg->add_to_locals(locals);
1032
+ } else {
1033
+ arg = new ArgNode { token };
1034
+ }
1035
+ arg->set_splat(true);
1036
+ args.push(arg.static_cast_as<Node>());
1037
+ return;
1038
+ }
1039
+ case Token::Type::StarStar: {
1040
+ advance();
1041
+ SharedPtr<ArgNode> arg;
1042
+ if (current_token().is_bare_name()) {
1043
+ arg = new ArgNode { token, current_token().literal_string() };
1044
+ advance();
1045
+ arg->add_to_locals(locals);
1046
+ } else if (current_token().is_keyword()) {
1047
+ arg = new ArgNode { token, new String(current_token().type_value()) };
1048
+ advance();
1049
+ } else {
1050
+ arg = new ArgNode { token };
1051
+ }
1052
+ arg->set_kwsplat(true);
1053
+ args.push(arg.static_cast_as<Node>());
1054
+ return;
1055
+ }
1056
+ case Token::Type::Ampersand: {
1057
+ advance();
1058
+ expect(Token::Type::BareName, "block name");
1059
+ auto arg = new ArgNode { token, current_token().literal_string() };
1060
+ advance();
1061
+ arg->add_to_locals(locals);
1062
+ arg->set_block_arg(true);
1063
+ args.push(arg);
1064
+ return;
1065
+ }
1066
+ case Token::Type::SymbolKey: {
1067
+ SharedPtr<KeywordArgNode> arg = new KeywordArgNode { token, current_token().literal_string() };
1068
+ advance();
1069
+ switch (current_token().type()) {
1070
+ case Token::Type::Comma:
1071
+ case Token::Type::Newline:
1072
+ case Token::Type::Pipe:
1073
+ case Token::Type::RParen:
1074
+ case Token::Type::Semicolon:
1075
+ break;
1076
+ default:
1077
+ arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1078
+ }
1079
+ arg->add_to_locals(locals);
1080
+ args.push(arg.static_cast_as<Node>());
1081
+ return;
1082
+ }
1083
+ default:
1084
+ throw_unexpected("argument");
1085
+ }
1086
+ }
1087
+
1088
+ SharedPtr<Node> Parser::parse_encoding(LocalsHashmap &) {
1089
+ auto token = current_token();
1090
+ advance(); // __ENCODING__
1091
+ return new EncodingNode { token };
1092
+ }
1093
+
1094
+ SharedPtr<Node> Parser::parse_end_block(LocalsHashmap &locals) {
1095
+ auto token = current_token();
1096
+ advance(); // END
1097
+ expect(Token::Type::LCurlyBrace, "END {}");
1098
+ auto node = new EndBlockNode { token };
1099
+ return parse_iter_expression(node, locals);
1100
+ }
1101
+
1102
+ SharedPtr<Node> Parser::parse_modifier_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
1103
+ auto token = current_token();
1104
+ switch (token.type()) {
1105
+ case Token::Type::IfKeyword: {
1106
+ advance();
1107
+ auto condition = parse_expression(Precedence::LOWEST, locals);
1108
+ return new IfNode { token, condition, left, new NilNode { token } };
1109
+ }
1110
+ case Token::Type::UnlessKeyword: {
1111
+ advance();
1112
+ auto condition = parse_expression(Precedence::LOWEST, locals);
1113
+ return new IfNode { token, condition, new NilNode { token }, left };
1114
+ }
1115
+ case Token::Type::UntilKeyword: {
1116
+ advance();
1117
+ auto condition = parse_expression(Precedence::LOWEST, locals);
1118
+ SharedPtr<BlockNode> body;
1119
+ bool pre = true;
1120
+ if (left->type() == Node::Type::Block) {
1121
+ body = left.static_cast_as<BlockNode>();
1122
+ pre = false;
1123
+ } else {
1124
+ if (left->type() == Node::Type::Begin) pre = false;
1125
+ body = new BlockNode { token, left };
1126
+ }
1127
+ return new UntilNode { token, condition, body, pre };
1128
+ }
1129
+ case Token::Type::WhileKeyword: {
1130
+ advance();
1131
+ auto condition = parse_expression(Precedence::LOWEST, locals);
1132
+ SharedPtr<BlockNode> body;
1133
+ bool pre = true;
1134
+ if (left->type() == Node::Type::Block) {
1135
+ body = left.static_cast_as<BlockNode>();
1136
+ pre = false;
1137
+ } else {
1138
+ if (left->type() == Node::Type::Begin) pre = false;
1139
+ body = new BlockNode { token, left };
1140
+ }
1141
+ return new WhileNode { token, condition, body, pre };
1142
+ }
1143
+ default:
1144
+ TM_NOT_YET_IMPLEMENTED();
1145
+ }
1146
+ }
1147
+
1148
+ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
1149
+ auto token = current_token();
1150
+ advance();
1151
+ return new StringNode { token, token.file() };
1152
+ }
1153
+
1154
+ SharedPtr<Node> Parser::parse_group(LocalsHashmap &locals) {
1155
+ auto token = current_token();
1156
+ advance(); // (
1157
+
1158
+ if (current_token().is_rparen()) {
1159
+ advance();
1160
+ return new NilSexpNode { token };
1161
+ }
1162
+
1163
+ auto body = parse_body(locals, Precedence::LOWEST, Token::Type::RParen, false);
1164
+ SharedPtr<Node> exp;
1165
+ if (body->has_one_node())
1166
+ exp = body->take_first_node();
1167
+ else
1168
+ exp = body.static_cast_as<Node>();
1169
+
1170
+ expect(Token::Type::RParen, "group closing paren");
1171
+ advance(); // )
1172
+
1173
+ if (current_token().is_equal() && exp->type() != Node::Type::MultipleAssignment) {
1174
+ throw_unexpected("multiple assignment on left-hand-side");
1175
+ }
1176
+
1177
+ if (exp->type() == Node::Type::MultipleAssignment) {
1178
+ auto multiple_assignment_node = exp.static_cast_as<MultipleAssignmentNode>();
1179
+ if (multiple_assignment_node->increment_depth() > 1) {
1180
+ auto wrapper = new MultipleAssignmentNode { token };
1181
+ wrapper->increment_depth();
1182
+ wrapper->add_node(exp);
1183
+ exp = wrapper;
1184
+ }
1185
+ }
1186
+
1187
+ return exp;
1188
+ };
1189
+
1190
+ SharedPtr<Node> Parser::parse_hash(LocalsHashmap &locals) {
1191
+ expect(Token::Type::LCurlyBrace, "hash opening curly brace");
1192
+ auto token = current_token();
1193
+ advance();
1194
+ SharedPtr<Node> hash;
1195
+ if (current_token().type() == Token::Type::RCurlyBrace)
1196
+ hash = new HashNode { token };
1197
+ else
1198
+ hash = parse_hash_inner(locals, Precedence::HASH, Token::Type::RCurlyBrace);
1199
+ expect(Token::Type::RCurlyBrace, "hash closing curly brace");
1200
+ advance();
1201
+ return hash;
1202
+ }
1203
+
1204
+ SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, SharedPtr<Node> first_key) {
1205
+ auto token = current_token();
1206
+ SharedPtr<HashNode> hash = new HashNode { token };
1207
+ if (!first_key)
1208
+ first_key = parse_expression(precedence, locals);
1209
+ hash->add_node(first_key);
1210
+ if (!first_key->is_symbol_key()) {
1211
+ expect(Token::Type::HashRocket, "hash rocket");
1212
+ advance();
1213
+ }
1214
+ hash->add_node(parse_expression(precedence, locals));
1215
+ while (current_token().is_comma()) {
1216
+ advance();
1217
+ if (current_token().type() == closing_token_type)
1218
+ break;
1219
+ if (current_token().type() == Token::Type::StarStar) // **kwsplat
1220
+ break;
1221
+ auto key = parse_expression(precedence, locals);
1222
+ hash->add_node(key);
1223
+ if (!key->is_symbol_key()) {
1224
+ expect(Token::Type::HashRocket, "hash rocket");
1225
+ advance();
1226
+ }
1227
+ hash->add_node(parse_expression(precedence, locals));
1228
+ }
1229
+ return hash.static_cast_as<Node>();
1230
+ }
1231
+
1232
+ SharedPtr<Node> Parser::parse_identifier(LocalsHashmap &locals) {
1233
+ auto name = current_token().literal();
1234
+ assert(name);
1235
+ bool is_lvar = !!locals.get(name);
1236
+ auto identifier = new IdentifierNode { current_token(), is_lvar };
1237
+ advance();
1238
+ return identifier;
1239
+ };
1240
+
1241
+ SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
1242
+ auto token = current_token();
1243
+ advance();
1244
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
1245
+ if (current_token().type() == Token::Type::ThenKeyword) {
1246
+ advance(); // then
1247
+ } else {
1248
+ next_expression();
1249
+ }
1250
+ SharedPtr<Node> true_expr = parse_if_body(locals);
1251
+ SharedPtr<Node> false_expr;
1252
+ if (current_token().is_elsif_keyword()) {
1253
+ false_expr = parse_if(locals);
1254
+ return new IfNode { current_token(), condition, true_expr, false_expr };
1255
+ } else {
1256
+ if (current_token().is_else_keyword()) {
1257
+ advance();
1258
+ false_expr = parse_if_body(locals);
1259
+ } else {
1260
+ false_expr = new NilNode { current_token() };
1261
+ }
1262
+ expect(Token::Type::EndKeyword, "if end");
1263
+ advance();
1264
+ return new IfNode { token, condition, true_expr, false_expr };
1265
+ }
1266
+ }
1267
+
1268
+ SharedPtr<Node> Parser::parse_if_body(LocalsHashmap &locals) {
1269
+ skip_newlines();
1270
+ SharedPtr<BlockNode> body = new BlockNode { current_token() };
1271
+ validate_current_token();
1272
+ skip_newlines();
1273
+ auto is_divider = [&]() {
1274
+ auto token = current_token();
1275
+ return token.is_elsif_keyword() || token.is_else_keyword() || token.is_end_keyword();
1276
+ };
1277
+ while (!current_token().is_eof() && !is_divider()) {
1278
+ auto exp = parse_expression(Precedence::LOWEST, locals);
1279
+ body->add_node(exp);
1280
+ validate_current_token();
1281
+ if (!is_divider())
1282
+ next_expression();
1283
+ }
1284
+ if (!is_divider())
1285
+ throw_unexpected("if end");
1286
+ if (body->is_empty())
1287
+ return new NilNode { body->token() };
1288
+ else if (body->has_one_node())
1289
+ return body->take_first_node();
1290
+ else
1291
+ return body.static_cast_as<Node>();
1292
+ }
1293
+
1294
+ void Parser::parse_interpolated_body(LocalsHashmap &locals, InterpolatedNode &node, Token::Type end_token) {
1295
+ while (current_token().is_valid() && current_token().type() != end_token) {
1296
+ switch (current_token().type()) {
1297
+ case Token::Type::EvaluateToStringBegin: {
1298
+ advance(); // #{
1299
+ skip_newlines();
1300
+ SharedPtr<BlockNode> block = new BlockNode { current_token() };
1301
+ while (current_token().type() != Token::Type::EvaluateToStringEnd) {
1302
+ block->add_node(parse_expression(Precedence::LOWEST, locals));
1303
+ skip_newlines();
1304
+ }
1305
+ advance(); // }
1306
+ if (block->is_empty()) {
1307
+ node.add_node(new EvaluateToStringNode { current_token() });
1308
+ } else if (block->has_one_node()) {
1309
+ auto first = block->take_first_node();
1310
+ if (first->type() == Node::Type::String)
1311
+ node.add_node(first);
1312
+ else
1313
+ node.add_node(new EvaluateToStringNode { current_token(), first });
1314
+ } else {
1315
+ node.add_node(new EvaluateToStringNode { current_token(), block.static_cast_as<Node>() });
1316
+ }
1317
+ break;
1318
+ }
1319
+ case Token::Type::String:
1320
+ node.add_node(new StringNode { current_token(), current_token().literal_string() });
1321
+ advance();
1322
+ break;
1323
+ default:
1324
+ printf("token type = %d\n", (int)current_token().type());
1325
+ TM_UNREACHABLE();
1326
+ }
1327
+ }
1328
+ if (current_token().type() != end_token) {
1329
+ auto token = node.token();
1330
+ switch (current_token().type()) {
1331
+ case Token::Type::UnterminatedRegexp:
1332
+ case Token::Type::UnterminatedString:
1333
+ case Token::Type::UnterminatedWordArray:
1334
+ throw_unterminated_thing(token);
1335
+ default:
1336
+ // this shouldn't happen -- if it does, there is a bug in the Lexer
1337
+ TM_UNREACHABLE()
1338
+ }
1339
+ }
1340
+ };
1341
+
1342
+ SharedPtr<Node> Parser::parse_interpolated_regexp(LocalsHashmap &locals) {
1343
+ auto token = current_token();
1344
+ advance();
1345
+ if (current_token().type() == Token::Type::InterpolatedRegexpEnd) {
1346
+ auto regexp_node = new RegexpNode { token, new String };
1347
+ if (current_token().has_literal()) {
1348
+ auto str = current_token().literal_string().ref();
1349
+ int options_int = parse_regexp_options(str);
1350
+ regexp_node->set_options(options_int);
1351
+ }
1352
+ advance();
1353
+ return regexp_node;
1354
+ } else if (current_token().type() == Token::Type::String && peek_token().type() == Token::Type::InterpolatedRegexpEnd) {
1355
+ auto regexp_node = new RegexpNode { token, current_token().literal_string() };
1356
+ advance();
1357
+ if (current_token().has_literal()) {
1358
+ auto str = current_token().literal_string().ref();
1359
+ int options_int = parse_regexp_options(str);
1360
+ regexp_node->set_options(options_int);
1361
+ }
1362
+ advance();
1363
+ return regexp_node;
1364
+ } else {
1365
+ SharedPtr<InterpolatedRegexpNode> interpolated_regexp = new InterpolatedRegexpNode { token };
1366
+ parse_interpolated_body(locals, interpolated_regexp.ref(), Token::Type::InterpolatedRegexpEnd);
1367
+ if (current_token().has_literal()) {
1368
+ auto str = current_token().literal_string().ref();
1369
+ int options_int = parse_regexp_options(str);
1370
+ interpolated_regexp->set_options(options_int);
1371
+ }
1372
+ advance();
1373
+ return interpolated_regexp.static_cast_as<Node>();
1374
+ }
1375
+ };
1376
+
1377
+ int Parser::parse_regexp_options(String &options_string) {
1378
+ int options = 0;
1379
+ for (size_t i = 0; i < options_string.size(); ++i) {
1380
+ switch (options_string.at(i)) {
1381
+ case 'i':
1382
+ options |= 1;
1383
+ break;
1384
+ case 'x':
1385
+ options |= 2;
1386
+ break;
1387
+ case 'm':
1388
+ options |= 4;
1389
+ break;
1390
+ case 'e':
1391
+ case 's':
1392
+ case 'u':
1393
+ options |= 16;
1394
+ break;
1395
+ case 'n':
1396
+ options |= 32;
1397
+ break;
1398
+ default:
1399
+ break;
1400
+ }
1401
+ }
1402
+ return options;
1403
+ }
1404
+
1405
+ SharedPtr<Node> Parser::parse_interpolated_shell(LocalsHashmap &locals) {
1406
+ auto token = current_token();
1407
+ advance();
1408
+ if (current_token().type() == Token::Type::InterpolatedShellEnd) {
1409
+ auto shell = new ShellNode { token, new String("") };
1410
+ advance();
1411
+ return shell;
1412
+ } else if (current_token().type() == Token::Type::String && peek_token().type() == Token::Type::InterpolatedShellEnd) {
1413
+ auto shell = new ShellNode { token, current_token().literal_string() };
1414
+ advance();
1415
+ advance();
1416
+ return shell;
1417
+ } else {
1418
+ SharedPtr<InterpolatedNode> interpolated_shell = new InterpolatedShellNode { token };
1419
+ parse_interpolated_body(locals, interpolated_shell.ref(), Token::Type::InterpolatedShellEnd);
1420
+ advance();
1421
+ return interpolated_shell.static_cast_as<Node>();
1422
+ }
1423
+ };
1424
+
1425
+ static SharedPtr<Node> convert_string_to_symbol_key(SharedPtr<Node> string) {
1426
+ switch (string->type()) {
1427
+ case Node::Type::String: {
1428
+ auto token = string->token();
1429
+ auto name = string.static_cast_as<StringNode>()->string();
1430
+ return new SymbolKeyNode { token, name };
1431
+ }
1432
+ case Node::Type::InterpolatedString: {
1433
+ auto token = string->token();
1434
+ auto node = new InterpolatedSymbolKeyNode { *string.static_cast_as<InterpolatedStringNode>() };
1435
+ return node;
1436
+ }
1437
+ default:
1438
+ TM_UNREACHABLE();
1439
+ }
1440
+ }
1441
+
1442
+ SharedPtr<Node> Parser::parse_interpolated_string(LocalsHashmap &locals) {
1443
+ auto token = current_token();
1444
+ advance();
1445
+ SharedPtr<Node> string;
1446
+ if (current_token().type() == Token::Type::InterpolatedStringEnd) {
1447
+ string = new StringNode { token, new String };
1448
+ advance();
1449
+ } else if (current_token().type() == Token::Type::String && peek_token().type() == Token::Type::InterpolatedStringEnd) {
1450
+ string = new StringNode { token, current_token().literal_string() };
1451
+ advance();
1452
+ advance();
1453
+ } else {
1454
+ SharedPtr<InterpolatedNode> interpolated_string = new InterpolatedStringNode { token };
1455
+ parse_interpolated_body(locals, interpolated_string.ref(), Token::Type::InterpolatedStringEnd);
1456
+ advance();
1457
+ string = interpolated_string.static_cast_as<Node>();
1458
+ }
1459
+
1460
+ bool adjacent_strings_were_appended = false;
1461
+ if (m_precedence_stack.is_empty() || m_precedence_stack.last() != Precedence::WORD_ARRAY)
1462
+ string = concat_adjacent_strings(string, locals, adjacent_strings_were_appended);
1463
+
1464
+ if (!adjacent_strings_were_appended && current_token().type() == Token::Type::InterpolatedStringSymbolKey) {
1465
+ advance(); // :
1466
+ return convert_string_to_symbol_key(string);
1467
+ }
1468
+
1469
+ return string;
1470
+ };
1471
+
1472
+ SharedPtr<Node> Parser::parse_interpolated_symbol(LocalsHashmap &locals) {
1473
+ auto token = current_token();
1474
+ advance();
1475
+ if (current_token().type() == Token::Type::InterpolatedSymbolEnd) {
1476
+ auto symbol = new SymbolNode { token, new String };
1477
+ advance();
1478
+ return symbol;
1479
+ } else if (current_token().type() == Token::Type::String && peek_token().type() == Token::Type::InterpolatedSymbolEnd) {
1480
+ auto symbol = new SymbolNode { token, current_token().literal_string() };
1481
+ advance();
1482
+ advance();
1483
+ return symbol;
1484
+ } else {
1485
+ SharedPtr<InterpolatedNode> interpolated_symbol = new InterpolatedSymbolNode { token };
1486
+ parse_interpolated_body(locals, interpolated_symbol.ref(), Token::Type::InterpolatedSymbolEnd);
1487
+ advance();
1488
+ return interpolated_symbol.static_cast_as<Node>();
1489
+ }
1490
+ };
1491
+
1492
+ SharedPtr<Node> Parser::parse_lit(LocalsHashmap &) {
1493
+ auto token = current_token();
1494
+ switch (token.type()) {
1495
+ case Token::Type::Bignum:
1496
+ advance();
1497
+ return new BignumNode { token, token.literal_string() };
1498
+ case Token::Type::Fixnum:
1499
+ advance();
1500
+ return new FixnumNode { token, token.get_fixnum() };
1501
+ case Token::Type::Float:
1502
+ advance();
1503
+ return new FloatNode { token, token.get_double() };
1504
+ default:
1505
+ TM_UNREACHABLE();
1506
+ }
1507
+ };
1508
+
1509
+ SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
1510
+ auto token = current_token();
1511
+ advance();
1512
+ return new KeywordSplatNode { token, parse_expression(Precedence::SPLAT, locals) };
1513
+ }
1514
+
1515
+ SharedPtr<Node> Parser::parse_keyword_splat_wrapped_in_hash(LocalsHashmap &locals) {
1516
+ SharedPtr<HashNode> hash = new HashNode { current_token() };
1517
+ hash->add_node(parse_keyword_splat(locals));
1518
+ return hash.static_cast_as<Node>();
1519
+ }
1520
+
1521
+ SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
1522
+ SharedPtr<String> name = new String("");
1523
+ auto token = current_token();
1524
+ switch (token.type()) {
1525
+ case Token::Type::BareName:
1526
+ case Token::Type::Constant:
1527
+ name = current_token().literal_string();
1528
+ break;
1529
+ default:
1530
+ if (token.is_operator() || token.is_keyword())
1531
+ name = new String(current_token().type_value());
1532
+ else
1533
+ throw_unexpected("method name");
1534
+ }
1535
+ advance();
1536
+ return name;
1537
+ }
1538
+
1539
+ SharedPtr<Node> Parser::parse_module(LocalsHashmap &) {
1540
+ auto token = current_token();
1541
+ advance();
1542
+ LocalsHashmap our_locals { TM::HashType::TMString };
1543
+ SharedPtr<Node> name = parse_class_or_module_name(our_locals);
1544
+ SharedPtr<BlockNode> body = parse_body(our_locals, Precedence::LOWEST, Token::Type::EndKeyword, true);
1545
+ expect(Token::Type::EndKeyword, "module end");
1546
+ advance();
1547
+ return new ModuleNode { token, name, body };
1548
+ }
1549
+
1550
+ SharedPtr<Node> Parser::parse_next(LocalsHashmap &locals) {
1551
+ auto token = current_token();
1552
+ advance();
1553
+ if (current_token().is_lparen()) {
1554
+ advance();
1555
+ if (current_token().is_rparen()) {
1556
+ advance();
1557
+ return new NextNode { token, new NilSexpNode { token } };
1558
+ } else {
1559
+ SharedPtr<Node> arg = parse_expression(Precedence::BARE_CALL_ARG, locals);
1560
+ expect(Token::Type::RParen, "break closing paren");
1561
+ advance();
1562
+ return new NextNode { token, arg };
1563
+ }
1564
+ } else if (current_token().can_be_first_arg_of_implicit_call()) {
1565
+ auto value = parse_expression(Precedence::BARE_CALL_ARG, locals);
1566
+ if (current_token().is_comma()) {
1567
+ SharedPtr<ArrayNode> array = new ArrayNode { token };
1568
+ array->add_node(value);
1569
+ while (current_token().is_comma()) {
1570
+ advance();
1571
+ array->add_node(parse_expression(Precedence::BARE_CALL_ARG, locals));
1572
+ }
1573
+ value = array.static_cast_as<Node>();
1574
+ }
1575
+ return new NextNode { token, value };
1576
+ }
1577
+ return new NextNode { token };
1578
+ }
1579
+
1580
+ SharedPtr<Node> Parser::parse_nil(LocalsHashmap &) {
1581
+ auto token = current_token();
1582
+ advance();
1583
+ return new NilSexpNode { token };
1584
+ }
1585
+
1586
+ SharedPtr<Node> Parser::parse_not(LocalsHashmap &locals) {
1587
+ auto token = current_token();
1588
+ advance();
1589
+ auto precedence = get_precedence(token);
1590
+ auto node = new NotNode {
1591
+ token,
1592
+ parse_expression(precedence, locals),
1593
+ };
1594
+ return node;
1595
+ }
1596
+
1597
+ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
1598
+ auto token = current_token();
1599
+ advance();
1600
+ return new NthRefNode { token, token.get_fixnum() };
1601
+ }
1602
+
1603
+ void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1604
+ if (current_token().is_semicolon()) {
1605
+ parse_shadow_variables_in_args(args, locals);
1606
+ return;
1607
+ }
1608
+ parse_def_single_arg(args, locals);
1609
+ while (current_token().is_comma()) {
1610
+ advance();
1611
+ parse_def_single_arg(args, locals);
1612
+ }
1613
+ if (current_token().is_semicolon()) {
1614
+ parse_shadow_variables_in_args(args, locals);
1615
+ return;
1616
+ }
1617
+ }
1618
+
1619
+ SharedPtr<Node> Parser::parse_redo(LocalsHashmap &) {
1620
+ auto token = current_token();
1621
+ advance();
1622
+ return new RedoNode { token };
1623
+ }
1624
+
1625
+ SharedPtr<Node> Parser::parse_retry(LocalsHashmap &) {
1626
+ auto token = current_token();
1627
+ advance();
1628
+ return new RetryNode { token };
1629
+ }
1630
+
1631
+ SharedPtr<Node> Parser::parse_return(LocalsHashmap &locals) {
1632
+ auto token = current_token();
1633
+ advance();
1634
+ SharedPtr<Node> value;
1635
+ if (current_token().is_end_of_expression()) {
1636
+ value = new NilNode { token };
1637
+ } else {
1638
+ value = parse_expression(Precedence::BARE_CALL_ARG, locals);
1639
+ }
1640
+ if (value->is_symbol_key())
1641
+ throw_unexpected("argument (no keyword args)");
1642
+ if (current_token().is_hash_rocket()) {
1643
+ value = parse_call_hash_args(locals, true, Token::Type::RParen, value);
1644
+ } else if (current_token().is_comma()) {
1645
+ SharedPtr<ArrayNode> array = new ArrayNode { current_token() };
1646
+ array->add_node(value);
1647
+ while (current_token().is_comma()) {
1648
+ advance();
1649
+ auto item = parse_expression(Precedence::BARE_CALL_ARG, locals);
1650
+ if (current_token().is_hash_rocket() || item->is_symbol_key()) {
1651
+ array->add_node(parse_call_hash_args(locals, true, Token::Type::RParen, item));
1652
+ } else {
1653
+ array->add_node(item);
1654
+ }
1655
+ }
1656
+ value = array.static_cast_as<Node>();
1657
+ }
1658
+ return new ReturnNode { token, value };
1659
+ };
1660
+
1661
+ SharedPtr<Node> Parser::parse_sclass(LocalsHashmap &locals) {
1662
+ auto token = current_token();
1663
+ advance(); // class
1664
+ advance(); // <<
1665
+ SharedPtr<Node> klass = parse_expression(Precedence::BARE_CALL_ARG, locals);
1666
+ SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
1667
+ expect(Token::Type::EndKeyword, "sclass end");
1668
+ advance();
1669
+ return new SclassNode { token, klass, body };
1670
+ }
1671
+
1672
+ SharedPtr<Node> Parser::parse_self(LocalsHashmap &) {
1673
+ auto token = current_token();
1674
+ advance();
1675
+ return new SelfNode { token };
1676
+ }
1677
+
1678
+ void Parser::parse_shadow_variables_in_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1679
+ auto token = current_token();
1680
+ advance(); // ;
1681
+ SharedPtr<ShadowArgNode> shadow_arg = new ShadowArgNode { token };
1682
+ shadow_arg->add_name(parse_shadow_variable_single_arg());
1683
+ while (current_token().is_comma()) {
1684
+ advance(); // ,
1685
+ shadow_arg->add_name(parse_shadow_variable_single_arg());
1686
+ }
1687
+ shadow_arg->add_to_locals(locals);
1688
+ args.push(shadow_arg.static_cast_as<Node>());
1689
+ }
1690
+
1691
+ SharedPtr<String> Parser::parse_shadow_variable_single_arg() {
1692
+ auto token = current_token();
1693
+ switch (token.type()) {
1694
+ case Token::Type::BareName: {
1695
+ auto name = token.literal_string();
1696
+ advance();
1697
+ return name;
1698
+ }
1699
+ default:
1700
+ throw_unexpected("shadow local variable");
1701
+ }
1702
+ }
1703
+
1704
+ SharedPtr<Node> Parser::parse_splat(LocalsHashmap &locals) {
1705
+ auto token = current_token();
1706
+ advance();
1707
+ if (current_token().is_comma() || current_token().is_equal())
1708
+ // TODO: there are likely additional tokens other than comma that would trigger this.
1709
+ return new SplatNode { token };
1710
+ return new SplatNode { token, parse_expression(Precedence::SPLAT, locals) };
1711
+ };
1712
+
1713
+ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
1714
+ auto token = current_token();
1715
+ advance(); // ->
1716
+ bool has_args = false;
1717
+ auto args = Vector<SharedPtr<Node>> {};
1718
+ if (current_token().is_lparen()) {
1719
+ has_args = true;
1720
+ advance(); // (
1721
+ if (current_token().is_rparen()) {
1722
+ advance(); // )
1723
+ } else {
1724
+ parse_proc_args(args, locals);
1725
+ expect(Token::Type::RParen, "proc args closing paren");
1726
+ advance(); // )
1727
+ }
1728
+ } else if (current_token().is_bare_name() || current_token().type() == Token::Type::Star) {
1729
+ has_args = true;
1730
+ parse_proc_args(args, locals);
1731
+ }
1732
+ if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
1733
+ throw_unexpected("block");
1734
+ auto proc = new StabbyProcNode {
1735
+ token,
1736
+ has_args,
1737
+ args
1738
+ };
1739
+ return parse_iter_expression(proc, locals);
1740
+ };
1741
+
1742
+ SharedPtr<Node> Parser::parse_string(LocalsHashmap &locals) {
1743
+ auto string_token = current_token();
1744
+ SharedPtr<Node> string = new StringNode { string_token, string_token.literal_string() };
1745
+ advance();
1746
+
1747
+ bool adjacent_strings_were_appended = false;
1748
+ if (m_precedence_stack.is_empty() || m_precedence_stack.last() != Precedence::WORD_ARRAY)
1749
+ string = concat_adjacent_strings(string, locals, adjacent_strings_were_appended);
1750
+
1751
+ if (!adjacent_strings_were_appended && current_token().type() == Token::Type::InterpolatedStringSymbolKey) {
1752
+ advance(); // :
1753
+ return convert_string_to_symbol_key(string);
1754
+ }
1755
+
1756
+ return string;
1757
+ };
1758
+
1759
+ SharedPtr<Node> Parser::concat_adjacent_strings(SharedPtr<Node> string, LocalsHashmap &locals, bool &strings_were_appended) {
1760
+ auto token = current_token();
1761
+ while (token.type() == Token::Type::String || token.type() == Token::Type::InterpolatedStringBegin) {
1762
+ switch (token.type()) {
1763
+ case Token::Type::String: {
1764
+ auto next_string = parse_string(locals);
1765
+ string = append_string_nodes(string, next_string);
1766
+ break;
1767
+ }
1768
+ case Token::Type::InterpolatedStringBegin: {
1769
+ auto next_string = parse_interpolated_string(locals);
1770
+ string = append_string_nodes(string, next_string);
1771
+ break;
1772
+ }
1773
+ default:
1774
+ TM_UNREACHABLE();
1775
+ }
1776
+ token = current_token();
1777
+ strings_were_appended = true;
1778
+ }
1779
+ return string;
1780
+ }
1781
+
1782
+ SharedPtr<Node> Parser::append_string_nodes(SharedPtr<Node> string1, SharedPtr<Node> string2) {
1783
+ if (!string2->can_be_concatenated_to_a_string())
1784
+ throw_unexpected("another string");
1785
+ switch (string1->type()) {
1786
+ case Node::Type::String: {
1787
+ auto string1_node = string1.static_cast_as<StringNode>();
1788
+ return string1_node->append_string_node(string2);
1789
+ }
1790
+ case Node::Type::InterpolatedString: {
1791
+ auto string1_node = string1.static_cast_as<InterpolatedStringNode>();
1792
+ return string1_node->append_string_node(string2);
1793
+ }
1794
+ default:
1795
+ throw_unexpected("string");
1796
+ }
1797
+ return string1;
1798
+ }
1799
+
1800
+ SharedPtr<Node> Parser::parse_super(LocalsHashmap &) {
1801
+ auto token = current_token();
1802
+ advance();
1803
+ auto node = new SuperNode { token };
1804
+ if (current_token().is_lparen())
1805
+ node->set_parens(true);
1806
+ return node;
1807
+ };
1808
+
1809
+ SharedPtr<Node> Parser::parse_symbol(LocalsHashmap &) {
1810
+ auto token = current_token();
1811
+ auto symbol = new SymbolNode { token, current_token().literal_string() };
1812
+ advance();
1813
+ return symbol;
1814
+ };
1815
+
1816
+ SharedPtr<Node> Parser::parse_symbol_key(LocalsHashmap &) {
1817
+ auto token = current_token();
1818
+ auto symbol = new SymbolKeyNode { token, current_token().literal_string() };
1819
+ advance();
1820
+ return symbol;
1821
+ };
1822
+
1823
+ SharedPtr<Node> Parser::parse_top_level_constant(LocalsHashmap &) {
1824
+ auto token = current_token();
1825
+ advance();
1826
+ auto name_token = current_token();
1827
+ SharedPtr<String> name;
1828
+ switch (name_token.type()) {
1829
+ case Token::Type::BareName:
1830
+ case Token::Type::Constant:
1831
+ advance();
1832
+ name = name_token.literal_string();
1833
+ break;
1834
+ default:
1835
+ throw_unexpected(name_token, ":: identifier name");
1836
+ }
1837
+ return new Colon3Node { token, name };
1838
+ }
1839
+
1840
+ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1841
+ auto token = current_token();
1842
+ advance();
1843
+ auto precedence = get_precedence(token);
1844
+ auto receiver = parse_expression(precedence, locals);
1845
+ if ((token.type() == Token::Type::Minus || token.type() == Token::Type::Plus) && receiver->is_numeric()) {
1846
+ switch (receiver->type()) {
1847
+ case Node::Type::Bignum: {
1848
+ if (token.type() == Token::Type::Minus) {
1849
+ auto num = receiver.static_cast_as<BignumNode>();
1850
+ num->negate();
1851
+ return num.static_cast_as<Node>();
1852
+ }
1853
+ return receiver;
1854
+ }
1855
+ case Node::Type::Fixnum: {
1856
+ if (token.type() == Token::Type::Minus) {
1857
+ auto num = receiver.static_cast_as<FixnumNode>();
1858
+ num->negate();
1859
+ return num.static_cast_as<Node>();
1860
+ }
1861
+ return receiver;
1862
+ }
1863
+ case Node::Type::Float: {
1864
+ if (token.type() == Token::Type::Minus) {
1865
+ auto num = receiver.static_cast_as<FloatNode>();
1866
+ num->negate();
1867
+ return num.static_cast_as<Node>();
1868
+ }
1869
+ return receiver;
1870
+ }
1871
+ default:
1872
+ TM_UNREACHABLE();
1873
+ }
1874
+ }
1875
+ SharedPtr<String> message = new String("");
1876
+ switch (token.type()) {
1877
+ case Token::Type::Minus:
1878
+ *message = "-@";
1879
+ break;
1880
+ case Token::Type::Plus:
1881
+ *message = "+@";
1882
+ break;
1883
+ case Token::Type::Tilde:
1884
+ *message = "~";
1885
+ break;
1886
+ default:
1887
+ TM_UNREACHABLE();
1888
+ }
1889
+ return new UnaryOpNode { token, message, receiver };
1890
+ }
1891
+
1892
+ SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
1893
+ auto undef_token = current_token();
1894
+ advance();
1895
+ auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
1896
+ switch (token.type()) {
1897
+ case Token::Type::BareName:
1898
+ case Token::Type::Constant:
1899
+ advance();
1900
+ return new SymbolNode { token, token.literal_string() };
1901
+ case Token::Type::Symbol:
1902
+ return parse_symbol(locals);
1903
+ case Token::Type::InterpolatedSymbolBegin: {
1904
+ return parse_interpolated_symbol(locals);
1905
+ }
1906
+ default:
1907
+ throw_unexpected("method name for undef");
1908
+ }
1909
+ };
1910
+ SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
1911
+ auto token = current_token();
1912
+ undef_node->add_arg(symbol_from_token(token));
1913
+ if (current_token().is_comma()) {
1914
+ SharedPtr<BlockNode> block = new BlockNode { undef_token };
1915
+ block->add_node(undef_node.static_cast_as<Node>());
1916
+ while (current_token().is_comma()) {
1917
+ advance();
1918
+ token = current_token();
1919
+ SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
1920
+ undef_node->add_arg(symbol_from_token(token));
1921
+ block->add_node(undef_node.static_cast_as<Node>());
1922
+ }
1923
+ return block.static_cast_as<Node>();
1924
+ }
1925
+ return undef_node.static_cast_as<Node>();
1926
+ };
1927
+
1928
+ SharedPtr<Node> Parser::parse_word_array(LocalsHashmap &locals) {
1929
+ auto token = current_token();
1930
+ SharedPtr<ArrayNode> array = new ArrayNode { token };
1931
+ advance();
1932
+ while (!current_token().is_eof() && !current_token().is_rbracket()) {
1933
+ if (current_token().type() == Token::Type::UnterminatedWordArray)
1934
+ throw_unterminated_thing(current_token(), token);
1935
+ array->add_node(parse_expression(Precedence::WORD_ARRAY, locals));
1936
+ }
1937
+ expect(Token::Type::RBracket, "closing array bracket");
1938
+ advance();
1939
+ return array.static_cast_as<Node>();
1940
+ }
1941
+
1942
+ SharedPtr<Node> Parser::parse_word_symbol_array(LocalsHashmap &locals) {
1943
+ auto token = current_token();
1944
+ SharedPtr<ArrayNode> array = new ArrayNode { token };
1945
+ advance();
1946
+ while (!current_token().is_eof() && !current_token().is_rbracket()) {
1947
+ auto string = parse_expression(Precedence::WORD_ARRAY, locals);
1948
+ SharedPtr<Node> symbol_node;
1949
+ switch (string->type()) {
1950
+ case Node::Type::String:
1951
+ symbol_node = string.static_cast_as<StringNode>()->to_symbol_node().static_cast_as<Node>();
1952
+ break;
1953
+ case Node::Type::InterpolatedString:
1954
+ symbol_node = string.static_cast_as<InterpolatedStringNode>()->to_symbol_node().static_cast_as<Node>();
1955
+ break;
1956
+ default:
1957
+ TM_UNREACHABLE();
1958
+ }
1959
+ array->add_node(symbol_node);
1960
+ }
1961
+ expect(Token::Type::RBracket, "closing array bracket");
1962
+ advance();
1963
+ return array.static_cast_as<Node>();
1964
+ }
1965
+
1966
+ SharedPtr<Node> Parser::parse_yield(LocalsHashmap &) {
1967
+ auto token = current_token();
1968
+ advance();
1969
+ return new YieldNode { token };
1970
+ };
1971
+
1972
+ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
1973
+ return parse_assignment_expression(left, locals, true);
1974
+ }
1975
+
1976
+ SharedPtr<Node> Parser::parse_assignment_expression_without_multiple_values(SharedPtr<Node> left, LocalsHashmap &locals) {
1977
+ return parse_assignment_expression(left, locals, false);
1978
+ }
1979
+
1980
+ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, LocalsHashmap &locals, bool allow_multiple) {
1981
+ auto token = current_token();
1982
+ if (left->type() == Node::Type::Splat) {
1983
+ return parse_multiple_assignment_expression(left, locals);
1984
+ }
1985
+ switch (left->type()) {
1986
+ case Node::Type::Identifier: {
1987
+ auto left_identifier = left.static_cast_as<IdentifierNode>();
1988
+ left_identifier->add_to_locals(locals);
1989
+ advance();
1990
+ auto value = parse_assignment_expression_value(false, locals, allow_multiple);
1991
+ return new AssignmentNode { token, left, value };
1992
+ }
1993
+ case Node::Type::Call:
1994
+ case Node::Type::Colon2:
1995
+ case Node::Type::Colon3:
1996
+ case Node::Type::SafeCall: {
1997
+ advance();
1998
+ auto value = parse_assignment_expression_value(false, locals, allow_multiple);
1999
+ return new AssignmentNode { token, left, value };
2000
+ }
2001
+ case Node::Type::MultipleAssignment: {
2002
+ left.static_cast_as<MultipleAssignmentNode>()->add_locals(locals);
2003
+ advance();
2004
+ auto value = parse_assignment_expression_value(true, locals, allow_multiple);
2005
+ return new AssignmentNode { token, left, value };
2006
+ }
2007
+ default:
2008
+ throw_unexpected(left->token(), "left side of assignment");
2009
+ }
2010
+ };
2011
+
2012
+ SharedPtr<Node> Parser::parse_assignment_expression_value(bool to_array, LocalsHashmap &locals, bool allow_multiple) {
2013
+ auto token = current_token();
2014
+ auto value = parse_expression(Precedence::ASSIGNMENT_RHS, locals);
2015
+ bool is_splat;
2016
+
2017
+ if (allow_multiple && current_token().type() == Token::Type::Comma) {
2018
+ SharedPtr<ArrayNode> array = new ArrayNode { token };
2019
+ array->add_node(value);
2020
+ while (current_token().type() == Token::Type::Comma) {
2021
+ advance();
2022
+ array->add_node(parse_expression(Precedence::ASSIGNMENT_RHS, locals));
2023
+ }
2024
+ value = array.static_cast_as<Node>();
2025
+ is_splat = true;
2026
+ } else if (value->type() == Node::Type::Splat) {
2027
+ is_splat = true;
2028
+ } else {
2029
+ is_splat = false;
2030
+ }
2031
+
2032
+ if (value->type() == Node::Type::Block) {
2033
+ auto block_node = value.static_cast_as<BlockNode>();
2034
+ if (block_node->has_one_node())
2035
+ value = value.static_cast_as<BlockNode>()->take_first_node();
2036
+ }
2037
+
2038
+ if (is_splat) {
2039
+ if (to_array) {
2040
+ return value;
2041
+ } else {
2042
+ return new SplatValueNode { token, value };
2043
+ }
2044
+ } else {
2045
+ if (to_array) {
2046
+ return new ToArrayNode { token, value };
2047
+ } else {
2048
+ return value;
2049
+ }
2050
+ }
2051
+ }
2052
+
2053
+ SharedPtr<Node> Parser::parse_iter_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2054
+ auto token = current_token();
2055
+ LocalsHashmap our_locals { locals }; // copy!
2056
+ bool curly_brace = current_token().type() == Token::Type::LCurlyBrace;
2057
+ bool has_args = false;
2058
+ auto args = Vector<SharedPtr<Node>> {};
2059
+ if (left->type() == Node::Type::StabbyProc) {
2060
+ advance(); // { or do
2061
+ auto stabby_proc_node = left.static_cast_as<StabbyProcNode>();
2062
+ has_args = stabby_proc_node->has_args();
2063
+ for (auto arg : stabby_proc_node->args())
2064
+ args.push(arg);
2065
+ } else if (left->can_accept_a_block()) {
2066
+ if (left->has_block_pass())
2067
+ throw SyntaxError { "Both block arg and actual block given." };
2068
+ advance(); // { or do
2069
+ if (current_token().type() == Token::Type::PipePipe) {
2070
+ has_args = true;
2071
+ advance(); // ||
2072
+ } else if (current_token().is_block_arg_delimiter()) {
2073
+ has_args = true;
2074
+ advance(); // |
2075
+ if (current_token().is_block_arg_delimiter()) {
2076
+ advance(); // |
2077
+ } else {
2078
+ parse_iter_args(args, our_locals);
2079
+ expect(Token::Type::Pipe, "end of block args");
2080
+ advance(); // |
2081
+ }
2082
+ }
2083
+ } else {
2084
+ throw_unexpected(left->token(), "call to accept block");
2085
+ }
2086
+ SharedPtr<BlockNode> body = parse_iter_body(our_locals, curly_brace);
2087
+ auto end_token_type = curly_brace ? Token::Type::RCurlyBrace : Token::Type::EndKeyword;
2088
+ expect(end_token_type, curly_brace ? "}" : "end");
2089
+ advance();
2090
+ return new IterNode {
2091
+ token,
2092
+ left,
2093
+ has_args,
2094
+ args,
2095
+ body
2096
+ };
2097
+ }
2098
+
2099
+ void Parser::parse_iter_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
2100
+ if (current_token().is_semicolon()) {
2101
+ parse_shadow_variables_in_args(args, locals);
2102
+ return;
2103
+ }
2104
+ parse_def_single_arg(args, locals);
2105
+ while (current_token().is_comma()) {
2106
+ advance();
2107
+ if (current_token().is_block_arg_delimiter()) {
2108
+ // trailing comma with no additional arg
2109
+ args.push(new NilNode { current_token() });
2110
+ break;
2111
+ }
2112
+ parse_def_single_arg(args, locals);
2113
+ }
2114
+ if (current_token().is_semicolon()) {
2115
+ parse_shadow_variables_in_args(args, locals);
2116
+ return;
2117
+ }
2118
+ }
2119
+
2120
+ SharedPtr<BlockNode> Parser::parse_iter_body(LocalsHashmap &locals, bool curly_brace) {
2121
+ auto end_token_type = curly_brace ? Token::Type::RCurlyBrace : Token::Type::EndKeyword;
2122
+ return parse_body(locals, Precedence::LOWEST, end_token_type, true); // FIXME: allow_rescue only for do/end
2123
+ }
2124
+
2125
+ SharedPtr<Node> Parser::parse_call_expression_with_parens(SharedPtr<Node> left, LocalsHashmap &locals) {
2126
+ auto token = current_token(); // (
2127
+ SharedPtr<NodeWithArgs> call_node = to_node_with_args(left);
2128
+ advance();
2129
+ if (current_token().is_rparen()) {
2130
+ // foo () vs foo()
2131
+ if (token.whitespace_precedes())
2132
+ call_node->add_arg(new NilSexpNode { current_token() });
2133
+ } else {
2134
+ parse_call_args(call_node.ref(), locals, false);
2135
+ }
2136
+ expect(Token::Type::RParen, "call rparen");
2137
+ advance();
2138
+ return call_node.static_cast_as<Node>();
2139
+ }
2140
+
2141
+ SharedPtr<NodeWithArgs> Parser::to_node_with_args(SharedPtr<Node> node) {
2142
+ switch (node->type()) {
2143
+ case Node::Type::Identifier: {
2144
+ auto identifier = node.static_cast_as<IdentifierNode>();
2145
+ auto call_node = new CallNode {
2146
+ identifier->token(),
2147
+ new NilNode { identifier->token() },
2148
+ identifier->name(),
2149
+ };
2150
+ return call_node;
2151
+ }
2152
+ case Node::Type::Call:
2153
+ case Node::Type::SafeCall:
2154
+ case Node::Type::Super:
2155
+ case Node::Type::Undef:
2156
+ case Node::Type::Yield:
2157
+ return node.static_cast_as<NodeWithArgs>();
2158
+ default:
2159
+ throw_unexpected(current_token(), nullptr, "left-hand-side is not callable");
2160
+ }
2161
+ }
2162
+
2163
+ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bare, Token::Type closing_token_type) {
2164
+ if (node.can_accept_a_block())
2165
+ m_call_depth.last()++;
2166
+ auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2167
+ if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
2168
+ node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
2169
+ } else {
2170
+ node.add_arg(arg);
2171
+ while (current_token().is_comma()) {
2172
+ advance();
2173
+ auto token = current_token();
2174
+ if (token.type() == closing_token_type) {
2175
+ // trailing comma with no additional arg
2176
+ break;
2177
+ }
2178
+ arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2179
+ if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
2180
+ node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
2181
+ break;
2182
+ } else {
2183
+ node.add_arg(arg);
2184
+ }
2185
+ }
2186
+ }
2187
+ if (node.can_accept_a_block())
2188
+ m_call_depth.last()--;
2189
+ }
2190
+
2191
+ SharedPtr<Node> Parser::parse_call_hash_args(LocalsHashmap &locals, bool bare, Token::Type closing_token_type, SharedPtr<Node> first_arg) {
2192
+ SharedPtr<Node> hash;
2193
+ if (bare)
2194
+ hash = parse_hash_inner(locals, Precedence::BARE_CALL_ARG, closing_token_type, first_arg);
2195
+ else
2196
+ hash = parse_hash_inner(locals, Precedence::CALL_ARG, closing_token_type, first_arg);
2197
+ if (current_token().type() == Token::Type::StarStar)
2198
+ hash.static_cast_as<HashNode>()->add_node(parse_keyword_splat(locals));
2199
+ return hash;
2200
+ }
2201
+
2202
+ SharedPtr<Node> Parser::parse_call_expression_without_parens(SharedPtr<Node> left, LocalsHashmap &locals) {
2203
+ auto token = current_token();
2204
+ SharedPtr<NodeWithArgs> call_node = to_node_with_args(left);
2205
+ switch (token.type()) {
2206
+ case Token::Type::Comma:
2207
+ case Token::Type::Eof:
2208
+ case Token::Type::Newline:
2209
+ case Token::Type::RBracket:
2210
+ case Token::Type::RCurlyBrace:
2211
+ case Token::Type::RParen:
2212
+ case Token::Type::Semicolon:
2213
+ break;
2214
+ default:
2215
+ parse_call_args(call_node.ref(), locals, true);
2216
+ }
2217
+ return call_node.static_cast_as<Node>();
2218
+ }
2219
+
2220
+ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2221
+ auto token = current_token();
2222
+ advance();
2223
+ auto name_token = current_token();
2224
+ SharedPtr<Node> node;
2225
+ switch (name_token.type()) {
2226
+ case Token::Type::BareName:
2227
+ advance();
2228
+ node = new CallNode { name_token, left, name_token.literal_string() };
2229
+ break;
2230
+ case Token::Type::Constant:
2231
+ advance();
2232
+ node = new Colon2Node { name_token, left, name_token.literal_string() };
2233
+ break;
2234
+ case Token::Type::LParen: {
2235
+ advance();
2236
+ SharedPtr<CallNode> call_node = new CallNode { name_token, left, new String("call") };
2237
+ if (!current_token().is_rparen())
2238
+ parse_call_args(call_node.ref(), locals, false);
2239
+ expect(Token::Type::RParen, "::() call right paren");
2240
+ advance();
2241
+ node = call_node.static_cast_as<Node>();
2242
+ break;
2243
+ }
2244
+ default:
2245
+ throw_unexpected(name_token, ":: identifier name");
2246
+ }
2247
+ return node;
2248
+ }
2249
+
2250
+ SharedPtr<Node> Parser::parse_infix_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2251
+ auto token = current_token();
2252
+ auto op = current_token();
2253
+ auto precedence = get_precedence(token, left);
2254
+ advance();
2255
+ auto right = parse_expression(precedence, locals);
2256
+ auto *node = new InfixOpNode {
2257
+ token,
2258
+ left,
2259
+ new String(op.type_value()),
2260
+ right,
2261
+ };
2262
+ return node;
2263
+ };
2264
+
2265
+ SharedPtr<Node> Parser::parse_logical_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2266
+ auto token = current_token();
2267
+ switch (token.type()) {
2268
+ case Token::Type::AmpersandAmpersand: {
2269
+ advance();
2270
+ auto right = parse_expression(Precedence::LOGICAL_AND, locals);
2271
+ if (left->type() == Node::Type::LogicalAnd) {
2272
+ return regroup<LogicalAndNode>(token, left, right);
2273
+ } else {
2274
+ return new LogicalAndNode { token, left, right };
2275
+ }
2276
+ }
2277
+ case Token::Type::AndKeyword: {
2278
+ advance();
2279
+ auto right = parse_expression(Precedence::COMPOSITION, locals);
2280
+ if (left->type() == Node::Type::LogicalAnd) {
2281
+ return regroup<LogicalAndNode>(token, left, right);
2282
+ } else {
2283
+ return new LogicalAndNode { token, left, right };
2284
+ }
2285
+ }
2286
+ case Token::Type::PipePipe: {
2287
+ advance();
2288
+ auto right = parse_expression(Precedence::LOGICAL_OR, locals);
2289
+ if (left->type() == Node::Type::LogicalOr) {
2290
+ return regroup<LogicalOrNode>(token, left, right);
2291
+ } else {
2292
+ return new LogicalOrNode { token, left, right };
2293
+ }
2294
+ }
2295
+ case Token::Type::OrKeyword: {
2296
+ advance();
2297
+ auto right = parse_expression(Precedence::COMPOSITION, locals);
2298
+ if (left->type() == Node::Type::LogicalOr) {
2299
+ return regroup<LogicalOrNode>(token, left, right);
2300
+ } else {
2301
+ return new LogicalOrNode { token, left, right };
2302
+ }
2303
+ }
2304
+ default:
2305
+ TM_UNREACHABLE();
2306
+ }
2307
+ }
2308
+
2309
+ SharedPtr<Node> Parser::parse_match_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2310
+ auto token = current_token();
2311
+ advance();
2312
+ auto arg = parse_expression(Precedence::EQUALITY, locals);
2313
+ if (left->type() == Node::Type::Regexp) {
2314
+ return new MatchNode { token, left.static_cast_as<RegexpNode>(), arg, true };
2315
+ } else if (arg->type() == Node::Type::Regexp) {
2316
+ return new MatchNode { token, arg.static_cast_as<RegexpNode>(), left, false };
2317
+ } else {
2318
+ auto *node = new CallNode {
2319
+ token,
2320
+ left,
2321
+ new String("=~"),
2322
+ };
2323
+ node->add_arg(arg);
2324
+ return node;
2325
+ }
2326
+ }
2327
+
2328
+ SharedPtr<Node> Parser::parse_not_match_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2329
+ auto token = current_token();
2330
+ left = parse_match_expression(left, locals);
2331
+ return new NotMatchNode { token, left };
2332
+ }
2333
+
2334
+ SharedPtr<Node> Parser::parse_op_assign_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2335
+ if (left->type() == Node::Type::Call || left->type() == Node::Type::SafeCall)
2336
+ return parse_op_attr_assign_expression(left, locals);
2337
+ switch (left->type()) {
2338
+ case Node::Type::Identifier: {
2339
+ auto identifier = left.static_cast_as<IdentifierNode>();
2340
+ identifier->set_is_lvar(true);
2341
+ identifier->add_to_locals(locals);
2342
+ break;
2343
+ }
2344
+ case Node::Type::Constant:
2345
+ case Node::Type::Colon2:
2346
+ case Node::Type::Colon3:
2347
+ break;
2348
+ default:
2349
+ throw_unexpected(left->token(), "variable or constant");
2350
+ }
2351
+ auto token = current_token();
2352
+ advance();
2353
+ switch (token.type()) {
2354
+ case Token::Type::AmpersandAmpersandEqual:
2355
+ return new OpAssignAndNode { token, left, parse_expression(Precedence::ASSIGNMENT_RHS, locals) };
2356
+ case Token::Type::PipePipeEqual:
2357
+ return new OpAssignOrNode { token, left, parse_expression(Precedence::ASSIGNMENT_RHS, locals) };
2358
+ case Token::Type::AmpersandEqual:
2359
+ case Token::Type::CaretEqual:
2360
+ case Token::Type::LeftShiftEqual:
2361
+ case Token::Type::MinusEqual:
2362
+ case Token::Type::PercentEqual:
2363
+ case Token::Type::PipeEqual:
2364
+ case Token::Type::PlusEqual:
2365
+ case Token::Type::RightShiftEqual:
2366
+ case Token::Type::SlashEqual:
2367
+ case Token::Type::StarEqual:
2368
+ case Token::Type::StarStarEqual: {
2369
+ auto op = new String(token.type_value());
2370
+ op->chomp();
2371
+ return new OpAssignNode { token, op, left, parse_expression(Precedence::ASSIGNMENT_RHS, locals) };
2372
+ }
2373
+ default:
2374
+ TM_UNREACHABLE();
2375
+ }
2376
+ }
2377
+
2378
+ SharedPtr<Node> Parser::parse_op_attr_assign_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2379
+ if (left->type() != Node::Type::Call && left->type() != Node::Type::SafeCall)
2380
+ throw_unexpected(left->token(), "call");
2381
+ auto left_call = left.static_cast_as<CallNode>();
2382
+ auto token = current_token();
2383
+ advance();
2384
+ auto value = parse_expression(Precedence::OP_ASSIGNMENT, locals);
2385
+
2386
+ if (*left_call->message() != "[]") {
2387
+ if (token.type() == Token::Type::AmpersandAmpersandEqual) {
2388
+ return new OpAssignAndNode {
2389
+ token,
2390
+ left,
2391
+ value,
2392
+ };
2393
+ } else if (token.type() == Token::Type::PipePipeEqual) {
2394
+ return new OpAssignOrNode {
2395
+ token,
2396
+ left,
2397
+ value,
2398
+ };
2399
+ }
2400
+ }
2401
+ auto op = new String(token.type_value());
2402
+ op->chomp();
2403
+ SharedPtr<String> message = new String(left_call->message().ref());
2404
+ message->append_char('=');
2405
+ auto op_node = new OpAssignAccessorNode {
2406
+ token,
2407
+ op,
2408
+ left_call->receiver(),
2409
+ message,
2410
+ value,
2411
+ left_call->args(),
2412
+ };
2413
+ if (left->type() == Node::Type::SafeCall)
2414
+ op_node->set_safe(true);
2415
+ return op_node;
2416
+ }
2417
+
2418
+ SharedPtr<Node> Parser::parse_proc_call_expression(SharedPtr<Node> left, LocalsHashmap &) {
2419
+ auto token = current_token();
2420
+ advance(); // .
2421
+ SharedPtr<Node> call_node = new CallNode {
2422
+ token,
2423
+ left,
2424
+ new String("call"),
2425
+ };
2426
+ return call_node.static_cast_as<Node>();
2427
+ }
2428
+
2429
+ SharedPtr<Node> Parser::parse_range_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2430
+ auto token = current_token();
2431
+ advance();
2432
+ SharedPtr<Node> right;
2433
+ try {
2434
+ right = parse_expression(Precedence::RANGE, locals);
2435
+ } catch (SyntaxError &e) {
2436
+ // NOTE: I'm not sure if this is the "right" way to handle an endless range,
2437
+ // but it seems to be effective for the tests I threw at it. ¯\_(ツ)_/¯
2438
+ right = new NilNode { token };
2439
+ }
2440
+ return new RangeNode { token, left, right, token.type() == Token::Type::DotDotDot };
2441
+ }
2442
+
2443
+ SharedPtr<Node> Parser::parse_ref_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2444
+ auto token = current_token();
2445
+ advance();
2446
+ SharedPtr<CallNode> call_node = new CallNode {
2447
+ token,
2448
+ left,
2449
+ new String("[]"),
2450
+ };
2451
+ if (token.type() == Token::Type::LBracketRBracket) {
2452
+ return call_node.static_cast_as<Node>();
2453
+ }
2454
+ if (!current_token().is_rbracket())
2455
+ parse_call_args(call_node.ref(), locals, false, Token::Type::RBracket);
2456
+ expect(Token::Type::RBracket, "element reference right bracket");
2457
+ advance();
2458
+ return call_node.static_cast_as<Node>();
2459
+ }
2460
+
2461
+ SharedPtr<Node> Parser::parse_rescue_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2462
+ auto token = current_token();
2463
+ advance(); // rescue
2464
+ auto value = parse_expression(Precedence::LOWEST, locals);
2465
+ auto body = new BlockNode { left->token(), left };
2466
+ auto begin_node = new BeginNode { token, body };
2467
+ auto rescue_node = new BeginRescueNode { token };
2468
+ rescue_node->set_body(new BlockNode { value->token(), value });
2469
+ begin_node->add_rescue_node(rescue_node);
2470
+ return begin_node;
2471
+ }
2472
+
2473
+ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsHashmap &) {
2474
+ auto token = current_token();
2475
+ advance(); // &.
2476
+ auto name_token = current_token();
2477
+ SharedPtr<String> name;
2478
+ switch (name_token.type()) {
2479
+ case Token::Type::LParen:
2480
+ name = new String("call");
2481
+ break;
2482
+ case Token::Type::BareName:
2483
+ case Token::Type::Constant:
2484
+ name = name_token.literal_string();
2485
+ advance();
2486
+ break;
2487
+ default:
2488
+ if (name_token.is_operator() || name_token.is_keyword()) {
2489
+ name = new String(name_token.type_value());
2490
+ advance();
2491
+ } else {
2492
+ throw_unexpected("safe navigation method name");
2493
+ }
2494
+ }
2495
+ return new SafeCallNode {
2496
+ token,
2497
+ left,
2498
+ name,
2499
+ };
2500
+ }
2501
+
2502
+ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashmap &) {
2503
+ auto dot_token = current_token();
2504
+ advance();
2505
+ auto name_token = current_token();
2506
+ SharedPtr<String> name;
2507
+ switch (name_token.type()) {
2508
+ case Token::Type::BareName:
2509
+ case Token::Type::Constant:
2510
+ name = name_token.literal_string();
2511
+ advance();
2512
+ break;
2513
+ default:
2514
+ if (name_token.is_operator() || name_token.is_keyword()) {
2515
+ name = new String(name_token.type_value());
2516
+ advance();
2517
+ } else {
2518
+ throw_unexpected("send method name");
2519
+ }
2520
+ };
2521
+ return new CallNode {
2522
+ dot_token,
2523
+ left,
2524
+ name,
2525
+ };
2526
+ }
2527
+
2528
+ SharedPtr<Node> Parser::parse_ternary_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2529
+ auto token = current_token();
2530
+ expect(Token::Type::TernaryQuestion, "ternary question");
2531
+ advance();
2532
+ SharedPtr<Node> true_expr = parse_expression(Precedence::TERNARY_TRUE, locals);
2533
+ expect(Token::Type::TernaryColon, "ternary colon");
2534
+ advance();
2535
+ auto false_expr = parse_expression(Precedence::TERNARY_FALSE, locals);
2536
+ return new IfNode { token, left, true_expr, false_expr };
2537
+ }
2538
+
2539
+ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2540
+ auto token = current_token();
2541
+ advance();
2542
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2543
+ next_expression();
2544
+ SharedPtr<Node> false_expr = parse_if_body(locals);
2545
+ SharedPtr<Node> true_expr;
2546
+ if (current_token().is_else_keyword()) {
2547
+ advance();
2548
+ true_expr = parse_if_body(locals);
2549
+ } else {
2550
+ true_expr = new NilNode { current_token() };
2551
+ }
2552
+ expect(Token::Type::EndKeyword, "unless end");
2553
+ advance();
2554
+ return new IfNode { token, condition, true_expr, false_expr };
2555
+ }
2556
+
2557
+ SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
2558
+ auto token = current_token();
2559
+ advance();
2560
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2561
+ next_expression();
2562
+ SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
2563
+ expect(Token::Type::EndKeyword, "while end");
2564
+ advance();
2565
+ switch (token.type()) {
2566
+ case Token::Type::UntilKeyword:
2567
+ return new UntilNode { token, condition, body, true };
2568
+ case Token::Type::WhileKeyword:
2569
+ return new WhileNode { token, condition, body, true };
2570
+ default:
2571
+ TM_UNREACHABLE();
2572
+ }
2573
+ }
2574
+
2575
+ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2576
+ using Type = Token::Type;
2577
+ switch (type) {
2578
+ case Type::AliasKeyword:
2579
+ return &Parser::parse_alias;
2580
+ case Type::LBracket:
2581
+ case Type::LBracketRBracket:
2582
+ return &Parser::parse_array;
2583
+ case Type::BackRef:
2584
+ return &Parser::parse_back_ref;
2585
+ case Type::BeginKeyword:
2586
+ return &Parser::parse_begin;
2587
+ case Type::BEGINKeyword:
2588
+ return &Parser::parse_begin_block;
2589
+ case Type::Ampersand:
2590
+ return &Parser::parse_block_pass;
2591
+ case Type::TrueKeyword:
2592
+ case Type::FalseKeyword:
2593
+ return &Parser::parse_bool;
2594
+ case Type::BreakKeyword:
2595
+ return &Parser::parse_break;
2596
+ case Type::CaseKeyword:
2597
+ return &Parser::parse_case;
2598
+ case Type::ClassKeyword:
2599
+ return &Parser::parse_class;
2600
+ case Type::DefKeyword:
2601
+ return &Parser::parse_def;
2602
+ case Type::DefinedKeyword:
2603
+ return &Parser::parse_defined;
2604
+ case Type::DotDot:
2605
+ case Type::DotDotDot:
2606
+ return &Parser::parse_beginless_range;
2607
+ case Type::ENCODINGKeyword:
2608
+ return &Parser::parse_encoding;
2609
+ case Type::ENDKeyword:
2610
+ return &Parser::parse_end_block;
2611
+ case Type::FILEKeyword:
2612
+ return &Parser::parse_file_constant;
2613
+ case Type::LParen:
2614
+ return &Parser::parse_group;
2615
+ case Type::LCurlyBrace:
2616
+ return &Parser::parse_hash;
2617
+ case Type::BareName:
2618
+ case Type::ClassVariable:
2619
+ case Type::Constant:
2620
+ case Type::GlobalVariable:
2621
+ case Type::InstanceVariable:
2622
+ return &Parser::parse_identifier;
2623
+ case Type::IfKeyword:
2624
+ return &Parser::parse_if;
2625
+ case Type::InterpolatedRegexpBegin:
2626
+ return &Parser::parse_interpolated_regexp;
2627
+ case Type::InterpolatedShellBegin:
2628
+ return &Parser::parse_interpolated_shell;
2629
+ case Type::InterpolatedStringBegin:
2630
+ return &Parser::parse_interpolated_string;
2631
+ case Type::InterpolatedSymbolBegin:
2632
+ return &Parser::parse_interpolated_symbol;
2633
+ case Type::StarStar:
2634
+ return &Parser::parse_keyword_splat_wrapped_in_hash;
2635
+ case Type::Bignum:
2636
+ case Type::Fixnum:
2637
+ case Type::Float:
2638
+ return &Parser::parse_lit;
2639
+ case Type::ModuleKeyword:
2640
+ return &Parser::parse_module;
2641
+ case Type::NextKeyword:
2642
+ return &Parser::parse_next;
2643
+ case Type::NilKeyword:
2644
+ return &Parser::parse_nil;
2645
+ case Type::Not:
2646
+ case Type::NotKeyword:
2647
+ return &Parser::parse_not;
2648
+ case Type::NthRef:
2649
+ return &Parser::parse_nth_ref;
2650
+ case Type::RedoKeyword:
2651
+ return &Parser::parse_redo;
2652
+ case Type::RetryKeyword:
2653
+ return &Parser::parse_retry;
2654
+ case Type::ReturnKeyword:
2655
+ return &Parser::parse_return;
2656
+ case Type::SelfKeyword:
2657
+ return &Parser::parse_self;
2658
+ case Type::Star:
2659
+ return &Parser::parse_splat;
2660
+ case Type::Arrow:
2661
+ return &Parser::parse_stabby_proc;
2662
+ case Type::String:
2663
+ return &Parser::parse_string;
2664
+ case Type::SuperKeyword:
2665
+ return &Parser::parse_super;
2666
+ case Type::Symbol:
2667
+ return &Parser::parse_symbol;
2668
+ case Type::SymbolKey:
2669
+ return &Parser::parse_symbol_key;
2670
+ case Type::ConstantResolution:
2671
+ return &Parser::parse_top_level_constant;
2672
+ case Type::Minus:
2673
+ case Type::Plus:
2674
+ case Type::Tilde:
2675
+ return &Parser::parse_unary_operator;
2676
+ case Type::UndefKeyword:
2677
+ return &Parser::parse_undef;
2678
+ case Type::UnlessKeyword:
2679
+ return &Parser::parse_unless;
2680
+ case Type::UntilKeyword:
2681
+ case Type::WhileKeyword:
2682
+ return &Parser::parse_while;
2683
+ case Type::PercentLowerI:
2684
+ case Type::PercentUpperI:
2685
+ return &Parser::parse_word_symbol_array;
2686
+ case Type::PercentLowerW:
2687
+ case Type::PercentUpperW:
2688
+ return &Parser::parse_word_array;
2689
+ case Type::YieldKeyword:
2690
+ return &Parser::parse_yield;
2691
+ default:
2692
+ return {};
2693
+ }
2694
+ }
2695
+
2696
+ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left, Precedence precedence) {
2697
+ using Type = Token::Type;
2698
+ switch (token.type()) {
2699
+ case Type::Equal:
2700
+ if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2701
+ return &Parser::parse_assignment_expression_without_multiple_values;
2702
+ else
2703
+ return &Parser::parse_assignment_expression;
2704
+ case Type::LParen:
2705
+ if (!token.whitespace_precedes())
2706
+ return &Parser::parse_call_expression_with_parens;
2707
+ break;
2708
+ case Type::ConstantResolution:
2709
+ return &Parser::parse_constant_resolution_expression;
2710
+ case Type::Ampersand:
2711
+ case Type::Caret:
2712
+ case Type::Comparison:
2713
+ case Type::EqualEqual:
2714
+ case Type::EqualEqualEqual:
2715
+ case Type::GreaterThan:
2716
+ case Type::GreaterThanOrEqual:
2717
+ case Type::LeftShift:
2718
+ case Type::LessThan:
2719
+ case Type::LessThanOrEqual:
2720
+ case Type::Minus:
2721
+ case Type::NotEqual:
2722
+ case Type::Percent:
2723
+ case Type::Pipe:
2724
+ case Type::Plus:
2725
+ case Type::RightShift:
2726
+ case Type::Slash:
2727
+ case Type::Star:
2728
+ case Type::StarStar:
2729
+ return &Parser::parse_infix_expression;
2730
+ case Type::DoKeyword:
2731
+ case Type::LCurlyBrace:
2732
+ return &Parser::parse_iter_expression;
2733
+ case Type::AmpersandAmpersand:
2734
+ case Type::AndKeyword:
2735
+ case Type::OrKeyword:
2736
+ case Type::PipePipe:
2737
+ return &Parser::parse_logical_expression;
2738
+ case Type::Match:
2739
+ return &Parser::parse_match_expression;
2740
+ case Type::IfKeyword:
2741
+ case Type::UnlessKeyword:
2742
+ case Type::WhileKeyword:
2743
+ case Type::UntilKeyword:
2744
+ return &Parser::parse_modifier_expression;
2745
+ case Type::Comma:
2746
+ return &Parser::parse_multiple_assignment_expression;
2747
+ case Type::NotMatch:
2748
+ return &Parser::parse_not_match_expression;
2749
+ case Type::AmpersandAmpersandEqual:
2750
+ case Type::AmpersandEqual:
2751
+ case Type::CaretEqual:
2752
+ case Type::LeftShiftEqual:
2753
+ case Type::MinusEqual:
2754
+ case Type::PipePipeEqual:
2755
+ case Type::PercentEqual:
2756
+ case Type::PipeEqual:
2757
+ case Type::PlusEqual:
2758
+ case Type::RightShiftEqual:
2759
+ case Type::SlashEqual:
2760
+ case Type::StarEqual:
2761
+ case Type::StarStarEqual:
2762
+ return &Parser::parse_op_assign_expression;
2763
+ case Type::DotDot:
2764
+ case Type::DotDotDot:
2765
+ return &Parser::parse_range_expression;
2766
+ case Type::LBracket:
2767
+ case Type::LBracketRBracket: {
2768
+ if (treat_left_bracket_as_element_reference(left, token))
2769
+ return &Parser::parse_ref_expression;
2770
+ break;
2771
+ }
2772
+ case Type::RescueKeyword:
2773
+ return &Parser::parse_rescue_expression;
2774
+ case Type::SafeNavigation:
2775
+ return &Parser::parse_safe_send_expression;
2776
+ case Type::Dot:
2777
+ if (peek_token().is_lparen()) {
2778
+ return &Parser::parse_proc_call_expression;
2779
+ } else {
2780
+ return &Parser::parse_send_expression;
2781
+ }
2782
+ case Type::TernaryQuestion:
2783
+ return &Parser::parse_ternary_expression;
2784
+ default:
2785
+ break;
2786
+ }
2787
+ if (is_first_arg_of_call_without_parens(left, token))
2788
+ return &Parser::parse_call_expression_without_parens;
2789
+ return {};
2790
+ }
2791
+
2792
+ bool Parser::is_first_arg_of_call_without_parens(SharedPtr<Node> left, Token &token) {
2793
+ return left->is_callable() && token.can_be_first_arg_of_implicit_call();
2794
+ }
2795
+
2796
+ Token &Parser::current_token() const {
2797
+ if (m_index < m_tokens->size()) {
2798
+ return m_tokens->at(m_index);
2799
+ } else {
2800
+ return Token::invalid();
2801
+ }
2802
+ }
2803
+
2804
+ Token &Parser::peek_token() const {
2805
+ if (m_index + 1 < m_tokens->size()) {
2806
+ return (*m_tokens)[m_index + 1];
2807
+ } else {
2808
+ return Token::invalid();
2809
+ }
2810
+ }
2811
+
2812
+ void Parser::next_expression() {
2813
+ auto token = current_token();
2814
+ if (!token.is_end_of_expression())
2815
+ throw_unexpected("end-of-line");
2816
+ skip_newlines();
2817
+ }
2818
+
2819
+ void Parser::skip_newlines() {
2820
+ while (current_token().is_end_of_line())
2821
+ advance();
2822
+ }
2823
+
2824
+ void Parser::expect(Token::Type type, const char *expected) {
2825
+ if (current_token().type() != type)
2826
+ throw_unexpected(expected);
2827
+ }
2828
+
2829
+ void Parser::throw_unexpected(const Token &token, const char *expected, const char *error) {
2830
+ auto file = token.file() ? String(*token.file()) : String("(unknown)");
2831
+ auto line = token.line() + 1;
2832
+ auto type = token.type_value();
2833
+ auto literal = token.literal();
2834
+ const char *help = nullptr;
2835
+ const char *help_description = nullptr;
2836
+ if (error) {
2837
+ assert(!expected);
2838
+ help = error;
2839
+ help_description = "error";
2840
+ } else {
2841
+ assert(expected);
2842
+ help = expected;
2843
+ help_description = "expected";
2844
+ }
2845
+ String message;
2846
+ if (token.type() == Token::Type::Invalid) {
2847
+ message = String::format("{}#{}: syntax error, unexpected '{}' ({}: '{}')", file, line, token.literal(), help_description, help);
2848
+ } else if (!type) {
2849
+ message = String::format("{}#{}: syntax error, {} '{}' (token type: {})", file, line, help_description, help, (long long)token.type());
2850
+ } else if (token.type() == Token::Type::Eof) {
2851
+ auto indent = String { token.column(), ' ' };
2852
+ message = String::format(
2853
+ "{}#{}: syntax error, unexpected end-of-input ({}: '{}')\n"
2854
+ "{}\n"
2855
+ "{}^ here, {} '{}'",
2856
+ file, line, help_description, help, current_line(), indent, help_description, help);
2857
+ } else if (literal) {
2858
+ auto indent = String { token.column(), ' ' };
2859
+ message = String::format(
2860
+ "{}#{}: syntax error, unexpected {} '{}' ({}: '{}')\n"
2861
+ "{}\n"
2862
+ "{}^ here, {} '{}'",
2863
+ file, line, type, literal, help_description, help, current_line(), indent, help_description, help);
2864
+ } else {
2865
+ auto indent = String { token.column(), ' ' };
2866
+ message = String::format(
2867
+ "{}#{}: syntax error, unexpected '{}' ({}: '{}')\n"
2868
+ "{}\n"
2869
+ "{}^ here, {} '{}'",
2870
+ file, line, type, help_description, help, current_line(), indent, help_description, help);
2871
+ }
2872
+ throw SyntaxError { message };
2873
+ }
2874
+
2875
+ void Parser::throw_unexpected(const char *expected) {
2876
+ throw_unexpected(current_token(), expected);
2877
+ }
2878
+
2879
+ void Parser::throw_unterminated_thing(Token token, Token start_token) {
2880
+ if (!start_token) start_token = token;
2881
+ auto indent = String { start_token.column(), ' ' };
2882
+ String expected;
2883
+ const char *lit = start_token.literal();
2884
+ assert(lit);
2885
+ if (strcmp(lit, "(") == 0)
2886
+ expected = "')'";
2887
+ else if (strcmp(lit, "[") == 0)
2888
+ expected = "']'";
2889
+ else if (strcmp(lit, "{") == 0)
2890
+ expected = "'}'";
2891
+ else if (strcmp(lit, "<") == 0)
2892
+ expected = "'>'";
2893
+ else if (strcmp(lit, "'") == 0)
2894
+ expected = "\"'\"";
2895
+ else
2896
+ expected = String::format("'{}'", lit);
2897
+ assert(!expected.is_empty());
2898
+ const char *thing = nullptr;
2899
+ switch (token.type()) {
2900
+ case Token::Type::InterpolatedRegexpBegin:
2901
+ case Token::Type::UnterminatedRegexp:
2902
+ thing = "regexp";
2903
+ break;
2904
+ case Token::Type::InterpolatedShellBegin:
2905
+ thing = "shell";
2906
+ break;
2907
+ case Token::Type::InterpolatedStringBegin:
2908
+ case Token::Type::String:
2909
+ case Token::Type::UnterminatedString:
2910
+ thing = "string";
2911
+ break;
2912
+ case Token::Type::InterpolatedSymbolBegin:
2913
+ thing = "symbol";
2914
+ break;
2915
+ case Token::Type::UnterminatedWordArray:
2916
+ thing = "word array";
2917
+ break;
2918
+ default:
2919
+ printf("unhandled unterminated thing (token type = %d)\n", (int)token.type());
2920
+ TM_UNREACHABLE();
2921
+ }
2922
+ auto file = start_token.file() ? String(*start_token.file()) : String("(unknown)");
2923
+ auto line = start_token.line() + 1;
2924
+ auto code = code_line(start_token.line());
2925
+ auto message = String::format(
2926
+ "{}#{}: syntax error, unterminated {} meets end of file (expected: {})\n"
2927
+ "{}\n"
2928
+ "{}^ starts here, expected closing {} somewhere after",
2929
+ file, line, thing, expected, code, indent, expected);
2930
+ throw SyntaxError { message };
2931
+ }
2932
+
2933
+ String Parser::code_line(size_t number) {
2934
+ size_t line = 0;
2935
+ String buf;
2936
+ for (size_t i = 0; i < m_code->size(); ++i) {
2937
+ char c = (*m_code)[i];
2938
+ if (line == number && c != '\n')
2939
+ buf.append_char(c);
2940
+ else if (line > number)
2941
+ break;
2942
+ if (c == '\n')
2943
+ line++;
2944
+ }
2945
+ return buf;
2946
+ }
2947
+
2948
+ String Parser::current_line() {
2949
+ return code_line(current_token().line());
2950
+ }
2951
+
2952
+ void Parser::validate_current_token() {
2953
+ auto token = current_token();
2954
+ switch (token.type()) {
2955
+ case Token::Type::Invalid:
2956
+ throw Parser::SyntaxError { String::format("{}: syntax error, unexpected '{}'", token.line() + 1, token.literal_or_blank()) };
2957
+ case Token::Type::InvalidUnicodeEscape:
2958
+ throw Parser::SyntaxError { String::format("{}: invalid Unicode escape", token.line() + 1) };
2959
+ case Token::Type::InvalidCharacterEscape:
2960
+ throw Parser::SyntaxError { String::format("{}: invalid character escape", token.line() + 1) };
2961
+ case Token::Type::UnterminatedRegexp:
2962
+ case Token::Type::UnterminatedString:
2963
+ case Token::Type::UnterminatedWordArray: {
2964
+ throw_unterminated_thing(current_token());
2965
+ }
2966
+ default:
2967
+ assert(token.type_value()); // all other types should return a string for type_value()
2968
+ return;
2969
+ }
2970
+ }
2971
+
2972
+ }