natalie_parser 2.1.0 → 2.3.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.
data/src/parser.cpp CHANGED
@@ -46,7 +46,7 @@ enum class Parser::Precedence {
46
46
  REF, // foo[1] / foo[1] = 2
47
47
  };
48
48
 
49
- bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence current_precedence) {
49
+ bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence current_precedence, IterAllow iter_allow) {
50
50
  auto next_precedence = get_precedence(token, left);
51
51
 
52
52
  // printf("token %d, left %d, current_precedence %d, next_precedence %d\n", (int)token.type(), (int)left->type(), (int)current_precedence, (int)next_precedence);
@@ -83,11 +83,11 @@ bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence cu
83
83
  // NOTE: `m_call_depth` should probably be called
84
84
  // `m_call_that_can_accept_a_block_depth`, but that's a bit long.
85
85
  //
86
- return m_call_depth.last() == 0;
86
+ return iter_allow == IterAllow::CURLY_AND_BLOCK && m_call_depth.last() == 0;
87
87
  }
88
88
 
89
89
  if (next_precedence == Precedence::ITER_CURLY)
90
- return left->is_callable();
90
+ return iter_allow >= IterAllow::CURLY_ONLY && left->is_callable();
91
91
 
92
92
  return next_precedence > current_precedence;
93
93
  }
@@ -198,7 +198,7 @@ Parser::Precedence Parser::get_precedence(Token &token, SharedPtr<Node> left) {
198
198
  return Precedence::LOWEST;
199
199
  }
200
200
 
201
- SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHashmap &locals) {
201
+ SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHashmap &locals, IterAllow iter_allow) {
202
202
  skip_newlines();
203
203
 
204
204
  m_precedence_stack.push(precedence);
@@ -211,7 +211,7 @@ SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHa
211
211
 
212
212
  while (current_token().is_valid()) {
213
213
  auto token = current_token();
214
- if (!higher_precedence(token, left, precedence))
214
+ if (!higher_precedence(token, left, precedence, iter_allow))
215
215
  break;
216
216
  auto left_fn = left_denotation(token, left, precedence);
217
217
  if (!left_fn)
@@ -278,44 +278,47 @@ SharedPtr<BlockNode> Parser::parse_def_body(LocalsHashmap &locals) {
278
278
  return parse_body(locals, Precedence::LOWEST, Token::Type::EndKeyword, true);
279
279
  }
280
280
 
281
+ void Parser::reinsert_collapsed_newline() {
282
+ auto token = previous_token();
283
+ if (token.can_precede_collapsible_newline()) {
284
+ // Some operators at the end of a line cause the newlines to be collapsed:
285
+ //
286
+ // foo <<
287
+ // bar
288
+ //
289
+ // ...but in this case (an alias), collapsing the newline was a mistake:
290
+ //
291
+ // alias foo <<
292
+ // def bar; end
293
+ //
294
+ // So, we'll put the newline back.
295
+ m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column(), token.whitespace_precedes() });
296
+ }
297
+ }
298
+
281
299
  SharedPtr<Node> Parser::parse_alias(LocalsHashmap &locals) {
282
300
  auto token = current_token();
283
301
  advance();
284
- SharedPtr<SymbolNode> new_name = parse_alias_arg(locals, "alias new name (first argument)", false);
285
- auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)", true);
302
+ auto new_name = parse_alias_arg(locals, "alias new name (first argument)");
303
+ auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)");
304
+ reinsert_collapsed_newline();
286
305
  return new AliasNode { token, new_name, existing_name };
287
306
  }
288
307
 
289
- SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char *expected_message, bool reinsert_collapsed_newline) {
308
+ SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char *expected_message) {
290
309
  auto token = current_token();
291
310
  switch (token.type()) {
292
- // TODO: handle Constant too
293
- case Token::Type::BareName: {
294
- advance();
295
- return new SymbolNode { token, token.literal_string() };
296
- }
311
+ case Token::Type::BareName:
312
+ case Token::Type::Constant:
313
+ case Token::Type::OperatorName:
314
+ return new SymbolNode { token, parse_method_name(locals) };
297
315
  case Token::Type::Symbol:
298
316
  return parse_symbol(locals).static_cast_as<SymbolNode>();
299
317
  case Token::Type::InterpolatedSymbolBegin:
300
318
  return parse_interpolated_symbol(locals).static_cast_as<SymbolNode>();
301
319
  default:
302
320
  if (token.is_operator() || token.is_keyword()) {
303
- advance();
304
- if (token.can_precede_collapsible_newline() && reinsert_collapsed_newline) {
305
- // Some operators at the end of a line cause the newlines to be collapsed:
306
- //
307
- // foo <<
308
- // bar
309
- //
310
- // ...but in this case (an alias), collapsing the newline was a mistake:
311
- //
312
- // alias foo <<
313
- // def bar; end
314
- //
315
- // So, we'll put the newline back.
316
- m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column(), token.whitespace_precedes() });
317
- }
318
- return new SymbolNode { token, new String(token.type_value()) };
321
+ return new SymbolNode { token, parse_method_name(locals) };
319
322
  } else {
320
323
  throw_unexpected(expected_message);
321
324
  }
@@ -431,16 +434,8 @@ void Parser::parse_rest_of_begin(BeginNode &begin_node, LocalsHashmap &locals) {
431
434
  }
432
435
  if (current_token().is_hash_rocket()) {
433
436
  advance();
434
- SharedPtr<IdentifierNode> name;
435
- switch (current_token().type()) {
436
- case Token::Type::BareName:
437
- name = new IdentifierNode { current_token(), current_token().literal_string() };
438
- advance();
439
- break;
440
- default:
441
- throw_unexpected("exception name");
442
- }
443
- name->add_to_locals(locals);
437
+ auto name = parse_expression(Precedence::ASSIGNMENT_LHS, locals);
438
+ add_assignment_locals(name, locals);
444
439
  rescue_node->set_exception_name(name);
445
440
  }
446
441
  next_expression();
@@ -501,7 +496,7 @@ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
501
496
  SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
502
497
  auto token = current_token();
503
498
  advance();
504
- auto value = parse_expression(Precedence::UNARY_PLUS, locals);
499
+ auto value = parse_expression(Precedence::LOWEST, locals);
505
500
  return new BlockPassNode { token, value };
506
501
  }
507
502
 
@@ -865,15 +860,19 @@ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
865
860
  }
866
861
 
867
862
  SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
868
- Token name_token;
869
- if (current_token().type() == Token::Type::ConstantResolution) {
870
- name_token = peek_token();
871
- } else {
872
- name_token = current_token();
873
- }
874
- if (name_token.type() != Token::Type::Constant)
863
+ auto name_token = current_token();
864
+ auto exp = parse_expression(Precedence::LESS_GREATER, locals);
865
+ switch (exp->type()) {
866
+ case Node::Type::Colon2:
867
+ case Node::Type::Colon3:
868
+ return exp;
869
+ case Node::Type::Identifier:
870
+ if (name_token.type() == Token::Type::Constant)
871
+ return exp;
872
+ [[fallthrough]];
873
+ default:
875
874
  throw SyntaxError { "class/module name must be CONSTANT" };
876
- return parse_expression(Precedence::LESS_GREATER, locals);
875
+ }
877
876
  }
878
877
 
879
878
  SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
@@ -903,7 +902,7 @@ SharedPtr<Node> Parser::parse_multiple_assignment_expression(SharedPtr<Node> lef
903
902
  list->add_node(left);
904
903
  while (current_token().is_comma()) {
905
904
  advance();
906
- if (current_token().is_rparen() || current_token().is_equal()) {
905
+ if (current_token().is_rparen() || current_token().is_equal() || current_token().type() == Token::Type::InKeyword) {
907
906
  // trailing comma with no additional identifier
908
907
  break;
909
908
  }
@@ -991,21 +990,24 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
991
990
  auto token = current_token();
992
991
  switch (token.type()) {
993
992
  case Token::Type::BareName:
994
- if (peek_token().type() == Token::Type::Dot) {
993
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
995
994
  self_node = parse_identifier(locals);
996
995
  advance(); // dot
997
996
  }
998
997
  name = parse_method_name(locals);
999
998
  break;
1000
999
  case Token::Type::Constant:
1001
- if (peek_token().type() == Token::Type::Dot) {
1000
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
1002
1001
  self_node = parse_constant(locals);
1003
1002
  advance(); // dot
1004
1003
  }
1005
1004
  name = parse_method_name(locals);
1006
1005
  break;
1006
+ case Token::Type::OperatorName:
1007
+ name = parse_method_name(locals);
1008
+ break;
1007
1009
  case Token::Type::SelfKeyword:
1008
- if (peek_token().type() == Token::Type::Dot) {
1010
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
1009
1011
  self_node = new SelfNode { current_token() };
1010
1012
  advance(); // self
1011
1013
  advance(); // dot
@@ -1023,10 +1025,6 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1023
1025
  }
1024
1026
  }
1025
1027
  }
1026
- if (current_token().is_equal() && !current_token().whitespace_precedes()) {
1027
- advance();
1028
- name->append_char('=');
1029
- }
1030
1028
  auto args = Vector<SharedPtr<Node>> {};
1031
1029
  if (current_token().is_lparen()) {
1032
1030
  advance();
@@ -1037,7 +1035,7 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1037
1035
  expect(Token::Type::RParen, "args closing paren");
1038
1036
  advance();
1039
1037
  }
1040
- } else if (current_token().is_bare_name() || current_token().is_splat() || current_token().is_symbol_key()) {
1038
+ } else if (current_token().can_be_first_arg_of_def()) {
1041
1039
  parse_def_args(args, our_locals);
1042
1040
  }
1043
1041
  SharedPtr<BlockNode> body;
@@ -1085,7 +1083,21 @@ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals
1085
1083
  }
1086
1084
  }
1087
1085
 
1088
- void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context) {
1086
+ SharedPtr<Node> Parser::parse_arg_default_value(LocalsHashmap &locals, IterAllow iter_allow) {
1087
+ auto token = current_token();
1088
+ if (token.is_bare_name() && peek_token().is_equal()) {
1089
+ SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
1090
+ advance();
1091
+ advance(); // =
1092
+ arg->add_to_locals(locals);
1093
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1094
+ return arg.static_cast_as<Node>();
1095
+ } else {
1096
+ return parse_expression(Precedence::DEF_ARG, locals, iter_allow);
1097
+ }
1098
+ }
1099
+
1100
+ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context, IterAllow iter_allow) {
1089
1101
  auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
1090
1102
  auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
1091
1103
 
@@ -1105,7 +1117,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1105
1117
  if (args_have_any_splat())
1106
1118
  throw_error(token, "default value after splat");
1107
1119
  advance(); // =
1108
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1120
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1109
1121
  }
1110
1122
  args.push(arg.static_cast_as<Node>());
1111
1123
  return;
@@ -1176,8 +1188,12 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1176
1188
  case Token::Type::RParen:
1177
1189
  case Token::Type::Semicolon:
1178
1190
  break;
1191
+ case Token::Type::LCurlyBrace:
1192
+ if (iter_allow < IterAllow::CURLY_ONLY)
1193
+ break;
1194
+ [[fallthrough]];
1179
1195
  default:
1180
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1196
+ arg->set_value(parse_expression(Precedence::DEF_ARG, locals, iter_allow));
1181
1197
  }
1182
1198
  arg->add_to_locals(locals);
1183
1199
  args.push(arg.static_cast_as<Node>());
@@ -1263,16 +1279,28 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
1263
1279
  return new StringNode { token, token.file() };
1264
1280
  }
1265
1281
 
1282
+ SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
1283
+ auto token = current_token();
1284
+ advance();
1285
+ return new FixnumNode { token, static_cast<long long>(token.line() + 1) };
1286
+ }
1287
+
1266
1288
  SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
1267
1289
  auto token = current_token();
1268
1290
  advance();
1269
- auto vars = parse_assignment_identifier(false, locals);
1270
- if (current_token().type() == Token::Type::Comma) {
1291
+ auto vars = parse_assignment_identifier(true, locals);
1292
+ if (current_token().is_comma() || vars->type() == Node::Type::Splat) {
1271
1293
  vars = parse_multiple_assignment_expression(vars, locals);
1272
1294
  }
1295
+ add_assignment_locals(vars, locals);
1273
1296
  expect(Token::Type::InKeyword, "for in");
1274
1297
  advance();
1275
- auto expr = parse_expression(Precedence::LOWEST, locals);
1298
+ m_call_depth.last()++;
1299
+ auto expr = parse_expression(Precedence::BARE_CALL_ARG, locals, IterAllow::CURLY_ONLY);
1300
+ m_call_depth.last()--;
1301
+ if (current_token().type() == Token::Type::DoKeyword) {
1302
+ advance();
1303
+ }
1276
1304
  auto body = parse_body(locals, Precedence::LOWEST);
1277
1305
  expect(Token::Type::EndKeyword, "for end");
1278
1306
  advance();
@@ -1690,13 +1718,15 @@ SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
1690
1718
  switch (token.type()) {
1691
1719
  case Token::Type::BareName:
1692
1720
  case Token::Type::Constant:
1721
+ case Token::Type::OperatorName:
1693
1722
  name = current_token().literal_string();
1694
1723
  break;
1695
1724
  default:
1696
- if (token.is_operator() || token.is_keyword())
1725
+ if (token.is_operator() || token.is_keyword()) {
1697
1726
  name = new String(current_token().type_value());
1698
- else
1727
+ } else {
1699
1728
  throw_unexpected("method name");
1729
+ }
1700
1730
  }
1701
1731
  advance();
1702
1732
  return name;
@@ -1766,15 +1796,15 @@ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
1766
1796
  return new NthRefNode { token, token.get_fixnum() };
1767
1797
  }
1768
1798
 
1769
- void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1799
+ void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, IterAllow iter_allow) {
1770
1800
  if (current_token().is_semicolon()) {
1771
1801
  parse_shadow_variables_in_args(args, locals);
1772
1802
  return;
1773
1803
  }
1774
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1804
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1775
1805
  while (current_token().is_comma()) {
1776
1806
  advance();
1777
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1807
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1778
1808
  }
1779
1809
  if (current_token().is_semicolon()) {
1780
1810
  parse_shadow_variables_in_args(args, locals);
@@ -1887,13 +1917,13 @@ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
1887
1917
  if (current_token().is_rparen()) {
1888
1918
  advance(); // )
1889
1919
  } else {
1890
- parse_proc_args(args, locals);
1920
+ parse_proc_args(args, locals, IterAllow::CURLY_AND_BLOCK);
1891
1921
  expect(Token::Type::RParen, "proc args closing paren");
1892
1922
  advance(); // )
1893
1923
  }
1894
- } else if (current_token().is_bare_name() || current_token().type() == Token::Type::Star) {
1924
+ } else if (current_token().can_be_first_arg_of_def()) {
1895
1925
  has_args = true;
1896
- parse_proc_args(args, locals);
1926
+ parse_proc_args(args, locals, IterAllow::NONE);
1897
1927
  }
1898
1928
  if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
1899
1929
  throw_unexpected("block");
@@ -2065,36 +2095,23 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
2065
2095
  SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
2066
2096
  auto undef_token = current_token();
2067
2097
  advance();
2068
- auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
2069
- switch (token.type()) {
2070
- case Token::Type::BareName:
2071
- case Token::Type::Constant:
2072
- advance();
2073
- return new SymbolNode { token, token.literal_string() };
2074
- case Token::Type::Symbol:
2075
- return parse_symbol(locals);
2076
- case Token::Type::InterpolatedSymbolBegin: {
2077
- return parse_interpolated_symbol(locals);
2078
- }
2079
- default:
2080
- throw_unexpected("method name for undef");
2081
- }
2082
- };
2083
2098
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2084
- auto token = current_token();
2085
- undef_node->add_arg(symbol_from_token(token));
2099
+ auto arg = parse_alias_arg(locals, "method name for undef");
2100
+ undef_node->add_arg(arg.static_cast_as<Node>());
2086
2101
  if (current_token().is_comma()) {
2087
2102
  SharedPtr<BlockNode> block = new BlockNode { undef_token };
2088
2103
  block->add_node(undef_node.static_cast_as<Node>());
2089
2104
  while (current_token().is_comma()) {
2090
2105
  advance();
2091
- token = current_token();
2092
2106
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2093
- undef_node->add_arg(symbol_from_token(token));
2107
+ auto arg = parse_alias_arg(locals, "method name for undef");
2108
+ undef_node->add_arg(arg.static_cast_as<Node>());
2094
2109
  block->add_node(undef_node.static_cast_as<Node>());
2095
2110
  }
2111
+ reinsert_collapsed_newline();
2096
2112
  return block.static_cast_as<Node>();
2097
2113
  }
2114
+ reinsert_collapsed_newline();
2098
2115
  return undef_node.static_cast_as<Node>();
2099
2116
  };
2100
2117
 
@@ -2155,32 +2172,34 @@ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, Locals
2155
2172
  if (left->type() == Node::Type::Splat) {
2156
2173
  return parse_multiple_assignment_expression(left, locals);
2157
2174
  }
2175
+ add_assignment_locals(left, locals);
2176
+ advance();
2177
+ bool to_array = left->type() == Node::Type::MultipleAssignment;
2178
+ auto value = parse_assignment_expression_value(to_array, locals, allow_multiple);
2179
+ return new AssignmentNode { token, left, value };
2180
+ }
2181
+
2182
+ void Parser::add_assignment_locals(SharedPtr<Node> left, LocalsHashmap &locals) {
2158
2183
  switch (left->type()) {
2159
2184
  case Node::Type::Identifier: {
2160
2185
  auto left_identifier = left.static_cast_as<IdentifierNode>();
2161
2186
  left_identifier->add_to_locals(locals);
2162
- advance();
2163
- auto value = parse_assignment_expression_value(false, locals, allow_multiple);
2164
- return new AssignmentNode { token, left, value };
2187
+ break;
2165
2188
  }
2166
2189
  case Node::Type::Call:
2167
2190
  case Node::Type::Colon2:
2168
2191
  case Node::Type::Colon3:
2169
2192
  case Node::Type::SafeCall: {
2170
- advance();
2171
- auto value = parse_assignment_expression_value(false, locals, allow_multiple);
2172
- return new AssignmentNode { token, left, value };
2193
+ break;
2173
2194
  }
2174
2195
  case Node::Type::MultipleAssignment: {
2175
2196
  left.static_cast_as<MultipleAssignmentNode>()->add_locals(locals);
2176
- advance();
2177
- auto value = parse_assignment_expression_value(true, locals, allow_multiple);
2178
- return new AssignmentNode { token, left, value };
2197
+ break;
2179
2198
  }
2180
2199
  default:
2181
- throw_unexpected(left->token(), "left side of assignment");
2200
+ throw_unexpected(left->token(), "assignment identifier");
2182
2201
  }
2183
- };
2202
+ }
2184
2203
 
2185
2204
  SharedPtr<Node> Parser::parse_assignment_expression_value(bool to_array, LocalsHashmap &locals, bool allow_multiple) {
2186
2205
  auto token = current_token();
@@ -2416,6 +2435,7 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2416
2435
  SharedPtr<Node> node;
2417
2436
  switch (name_token.type()) {
2418
2437
  case Token::Type::BareName:
2438
+ case Token::Type::OperatorName:
2419
2439
  advance();
2420
2440
  node = new CallNode { name_token, left, name_token.literal_string() };
2421
2441
  break;
@@ -2434,7 +2454,12 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2434
2454
  break;
2435
2455
  }
2436
2456
  default:
2437
- throw_unexpected(name_token, ":: identifier name");
2457
+ if (name_token.is_operator() || name_token.is_keyword()) {
2458
+ advance();
2459
+ node = new CallNode { name_token, left, new String(name_token.type_value()) };
2460
+ } else {
2461
+ throw_unexpected(name_token, ":: identifier name");
2462
+ }
2438
2463
  }
2439
2464
  return node;
2440
2465
  }
@@ -2677,6 +2702,7 @@ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsH
2677
2702
  break;
2678
2703
  case Token::Type::BareName:
2679
2704
  case Token::Type::Constant:
2705
+ case Token::Type::OperatorName:
2680
2706
  name = name_token.literal_string();
2681
2707
  advance();
2682
2708
  break;
@@ -2703,6 +2729,7 @@ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashma
2703
2729
  switch (name_token.type()) {
2704
2730
  case Token::Type::BareName:
2705
2731
  case Token::Type::Constant:
2732
+ case Token::Type::OperatorName:
2706
2733
  name = name_token.literal_string();
2707
2734
  advance();
2708
2735
  break;
@@ -2745,7 +2772,11 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2745
2772
  if (condition->type() == Node::Type::Regexp) {
2746
2773
  condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2747
2774
  }
2748
- next_expression();
2775
+ if (current_token().type() == Token::Type::ThenKeyword) {
2776
+ advance(); // then
2777
+ } else {
2778
+ next_expression();
2779
+ }
2749
2780
  SharedPtr<Node> false_expr = parse_if_body(locals);
2750
2781
  SharedPtr<Node> true_expr;
2751
2782
  if (current_token().is_else_keyword()) {
@@ -2762,11 +2793,15 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2762
2793
  SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
2763
2794
  auto token = current_token();
2764
2795
  advance();
2765
- SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2796
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
2766
2797
  if (condition->type() == Node::Type::Regexp) {
2767
2798
  condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2768
2799
  }
2769
- next_expression();
2800
+ if (current_token().type() == Token::Type::DoKeyword) {
2801
+ advance();
2802
+ } else {
2803
+ next_expression();
2804
+ }
2770
2805
  SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
2771
2806
  expect(Token::Type::EndKeyword, "while end");
2772
2807
  advance();
@@ -2824,6 +2859,8 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2824
2859
  return &Parser::parse_group;
2825
2860
  case Type::LCurlyBrace:
2826
2861
  return &Parser::parse_hash;
2862
+ case Type::LINEKeyword:
2863
+ return &Parser::parse_line_constant;
2827
2864
  case Type::BareName:
2828
2865
  case Type::ClassVariable:
2829
2866
  case Type::Constant:
@@ -2907,7 +2944,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
2907
2944
  using Type = Token::Type;
2908
2945
  switch (token.type()) {
2909
2946
  case Type::Equal:
2910
- if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2947
+ if (precedence == Precedence::ARRAY || precedence == Precedence::HASH || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2911
2948
  return &Parser::parse_assignment_expression_without_multiple_values;
2912
2949
  else
2913
2950
  return &Parser::parse_assignment_expression;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natalie_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Morgan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-12 00:00:00.000000000 Z
11
+ date: 2022-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: NatalieParser is a zero-dependency, from-scratch, hand-written recursive
14
14
  descent parser for the Ruby Programming Language.
@@ -151,6 +151,7 @@ files:
151
151
  - src/node/begin_node.cpp
152
152
  - src/node/begin_rescue_node.cpp
153
153
  - src/node/class_node.cpp
154
+ - src/node/for_node.cpp
154
155
  - src/node/interpolated_regexp_node.cpp
155
156
  - src/node/interpolated_shell_node.cpp
156
157
  - src/node/interpolated_string_node.cpp