natalie_parser 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }
@@ -354,11 +357,17 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
354
357
  return {};
355
358
  };
356
359
  auto ret = add_node();
357
- if (ret) return ret;
360
+ if (ret) {
361
+ m_call_depth.pop();
362
+ return ret;
363
+ }
358
364
  while (current_token().is_comma()) {
359
365
  advance();
360
366
  ret = add_node();
361
- if (ret) return ret;
367
+ if (ret) {
368
+ m_call_depth.pop();
369
+ return ret;
370
+ }
362
371
  }
363
372
  expect(Token::Type::RBracket, "array closing bracket");
364
373
  advance(); // ]
@@ -495,7 +504,7 @@ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
495
504
  SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
496
505
  auto token = current_token();
497
506
  advance();
498
- auto value = parse_expression(Precedence::UNARY_PLUS, locals);
507
+ auto value = parse_expression(Precedence::LOWEST, locals);
499
508
  return new BlockPassNode { token, value };
500
509
  }
501
510
 
@@ -859,15 +868,19 @@ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
859
868
  }
860
869
 
861
870
  SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
862
- Token name_token;
863
- if (current_token().type() == Token::Type::ConstantResolution) {
864
- name_token = peek_token();
865
- } else {
866
- name_token = current_token();
867
- }
868
- if (name_token.type() != Token::Type::Constant)
871
+ auto name_token = current_token();
872
+ auto exp = parse_expression(Precedence::LESS_GREATER, locals);
873
+ switch (exp->type()) {
874
+ case Node::Type::Colon2:
875
+ case Node::Type::Colon3:
876
+ return exp;
877
+ case Node::Type::Identifier:
878
+ if (name_token.type() == Token::Type::Constant)
879
+ return exp;
880
+ [[fallthrough]];
881
+ default:
869
882
  throw SyntaxError { "class/module name must be CONSTANT" };
870
- return parse_expression(Precedence::LESS_GREATER, locals);
883
+ }
871
884
  }
872
885
 
873
886
  SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
@@ -985,21 +998,24 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
985
998
  auto token = current_token();
986
999
  switch (token.type()) {
987
1000
  case Token::Type::BareName:
988
- if (peek_token().type() == Token::Type::Dot) {
1001
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
989
1002
  self_node = parse_identifier(locals);
990
1003
  advance(); // dot
991
1004
  }
992
1005
  name = parse_method_name(locals);
993
1006
  break;
994
1007
  case Token::Type::Constant:
995
- if (peek_token().type() == Token::Type::Dot) {
1008
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
996
1009
  self_node = parse_constant(locals);
997
1010
  advance(); // dot
998
1011
  }
999
1012
  name = parse_method_name(locals);
1000
1013
  break;
1014
+ case Token::Type::OperatorName:
1015
+ name = parse_method_name(locals);
1016
+ break;
1001
1017
  case Token::Type::SelfKeyword:
1002
- if (peek_token().type() == Token::Type::Dot) {
1018
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
1003
1019
  self_node = new SelfNode { current_token() };
1004
1020
  advance(); // self
1005
1021
  advance(); // dot
@@ -1017,10 +1033,6 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1017
1033
  }
1018
1034
  }
1019
1035
  }
1020
- if (current_token().is_equal() && !current_token().whitespace_precedes()) {
1021
- advance();
1022
- name->append_char('=');
1023
- }
1024
1036
  auto args = Vector<SharedPtr<Node>> {};
1025
1037
  if (current_token().is_lparen()) {
1026
1038
  advance();
@@ -1031,7 +1043,7 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1031
1043
  expect(Token::Type::RParen, "args closing paren");
1032
1044
  advance();
1033
1045
  }
1034
- } else if (current_token().is_bare_name() || current_token().is_splat() || current_token().is_symbol_key()) {
1046
+ } else if (current_token().can_be_first_arg_of_def()) {
1035
1047
  parse_def_args(args, our_locals);
1036
1048
  }
1037
1049
  SharedPtr<BlockNode> body;
@@ -1058,7 +1070,16 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1058
1070
  SharedPtr<Node> Parser::parse_defined(LocalsHashmap &locals) {
1059
1071
  auto token = current_token();
1060
1072
  advance();
1061
- auto arg = parse_expression(Precedence::BARE_CALL_ARG, locals);
1073
+ bool bare = true;
1074
+ if (current_token().is_lparen()) {
1075
+ advance();
1076
+ bare = false;
1077
+ }
1078
+ auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
1079
+ if (!bare) {
1080
+ expect(Token::Type::RParen, "defined? closing paren");
1081
+ advance();
1082
+ }
1062
1083
  return new DefinedNode { token, arg };
1063
1084
  }
1064
1085
 
@@ -1070,7 +1091,21 @@ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals
1070
1091
  }
1071
1092
  }
1072
1093
 
1073
- void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context) {
1094
+ SharedPtr<Node> Parser::parse_arg_default_value(LocalsHashmap &locals, IterAllow iter_allow) {
1095
+ auto token = current_token();
1096
+ if (token.is_bare_name() && peek_token().is_equal()) {
1097
+ SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
1098
+ advance();
1099
+ advance(); // =
1100
+ arg->add_to_locals(locals);
1101
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1102
+ return arg.static_cast_as<Node>();
1103
+ } else {
1104
+ return parse_expression(Precedence::DEF_ARG, locals, iter_allow);
1105
+ }
1106
+ }
1107
+
1108
+ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context, IterAllow iter_allow) {
1074
1109
  auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
1075
1110
  auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
1076
1111
 
@@ -1090,7 +1125,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1090
1125
  if (args_have_any_splat())
1091
1126
  throw_error(token, "default value after splat");
1092
1127
  advance(); // =
1093
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1128
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1094
1129
  }
1095
1130
  args.push(arg.static_cast_as<Node>());
1096
1131
  return;
@@ -1161,8 +1196,12 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1161
1196
  case Token::Type::RParen:
1162
1197
  case Token::Type::Semicolon:
1163
1198
  break;
1199
+ case Token::Type::LCurlyBrace:
1200
+ if (iter_allow < IterAllow::CURLY_ONLY)
1201
+ break;
1202
+ [[fallthrough]];
1164
1203
  default:
1165
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1204
+ arg->set_value(parse_expression(Precedence::DEF_ARG, locals, iter_allow));
1166
1205
  }
1167
1206
  arg->add_to_locals(locals);
1168
1207
  args.push(arg.static_cast_as<Node>());
@@ -1248,6 +1287,31 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
1248
1287
  return new StringNode { token, token.file() };
1249
1288
  }
1250
1289
 
1290
+ SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
1291
+ auto token = current_token();
1292
+ advance();
1293
+ return new FixnumNode { token, static_cast<long long>(token.line() + 1) };
1294
+ }
1295
+
1296
+ SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
1297
+ auto token = current_token();
1298
+ advance();
1299
+ auto vars = parse_assignment_identifier(false, locals);
1300
+ if (current_token().type() == Token::Type::Comma) {
1301
+ vars = parse_multiple_assignment_expression(vars, locals);
1302
+ }
1303
+ expect(Token::Type::InKeyword, "for in");
1304
+ advance();
1305
+ auto expr = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
1306
+ if (current_token().type() == Token::Type::DoKeyword) {
1307
+ advance();
1308
+ }
1309
+ auto body = parse_body(locals, Precedence::LOWEST);
1310
+ expect(Token::Type::EndKeyword, "for end");
1311
+ advance();
1312
+ return new ForNode { token, expr, vars, body };
1313
+ }
1314
+
1251
1315
  SharedPtr<Node> Parser::parse_forward_args(LocalsHashmap &locals) {
1252
1316
  auto token = current_token();
1253
1317
  if (!locals.get("..."))
@@ -1310,28 +1374,35 @@ SharedPtr<Node> Parser::parse_hash(LocalsHashmap &locals) {
1310
1374
  SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, bool bare, SharedPtr<Node> first_key) {
1311
1375
  auto token = current_token();
1312
1376
  SharedPtr<HashNode> hash = new HashNode { token, bare };
1377
+
1378
+ auto add_value = [&](SharedPtr<Node> key) {
1379
+ if (key->is_symbol_key()) {
1380
+ hash->add_node(parse_expression(precedence, locals));
1381
+ } else if (key->type() == Node::Type::KeywordSplat) {
1382
+ // kwsplat already added
1383
+ } else {
1384
+ expect(Token::Type::HashRocket, "hash rocket");
1385
+ advance(); // =>
1386
+ hash->add_node(parse_expression(precedence, locals));
1387
+ }
1388
+ };
1389
+
1313
1390
  if (!first_key)
1314
1391
  first_key = parse_expression(precedence, locals);
1315
1392
  hash->add_node(first_key);
1316
- if (!first_key->is_symbol_key()) {
1317
- expect(Token::Type::HashRocket, "hash rocket");
1318
- advance();
1319
- }
1320
- hash->add_node(parse_expression(precedence, locals));
1393
+ add_value(first_key);
1394
+
1321
1395
  while (current_token().is_comma()) {
1322
1396
  advance();
1323
1397
  if (current_token().type() == closing_token_type)
1324
1398
  break;
1325
- if (current_token().type() == Token::Type::StarStar) // **kwsplat
1399
+ if (current_token().type() == Token::Type::Ampersand) // &block
1326
1400
  break;
1327
1401
  auto key = parse_expression(precedence, locals);
1328
1402
  hash->add_node(key);
1329
- if (!key->is_symbol_key()) {
1330
- expect(Token::Type::HashRocket, "hash rocket");
1331
- advance();
1332
- }
1333
- hash->add_node(parse_expression(precedence, locals));
1403
+ add_value(key);
1334
1404
  }
1405
+
1335
1406
  return hash.static_cast_as<Node>();
1336
1407
  }
1337
1408
 
@@ -1345,9 +1416,16 @@ SharedPtr<Node> Parser::parse_identifier(LocalsHashmap &locals) {
1345
1416
  };
1346
1417
 
1347
1418
  SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
1419
+ return parse_if_branch(locals, true);
1420
+ }
1421
+
1422
+ SharedPtr<Node> Parser::parse_if_branch(LocalsHashmap &locals, bool parse_match_condition) {
1348
1423
  auto token = current_token();
1349
1424
  advance();
1350
1425
  SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
1426
+ if (parse_match_condition && condition->type() == Node::Type::Regexp) {
1427
+ condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
1428
+ }
1351
1429
  if (current_token().type() == Token::Type::ThenKeyword) {
1352
1430
  advance(); // then
1353
1431
  } else {
@@ -1356,7 +1434,7 @@ SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
1356
1434
  SharedPtr<Node> true_expr = parse_if_body(locals);
1357
1435
  SharedPtr<Node> false_expr;
1358
1436
  if (current_token().is_elsif_keyword()) {
1359
- false_expr = parse_if(locals);
1437
+ false_expr = parse_if_branch(locals, false);
1360
1438
  return new IfNode { current_token(), condition, true_expr, false_expr };
1361
1439
  } else {
1362
1440
  if (current_token().is_else_keyword()) {
@@ -1639,25 +1717,21 @@ SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
1639
1717
  return new KeywordSplatNode { token, parse_expression(Precedence::SPLAT, locals) };
1640
1718
  }
1641
1719
 
1642
- SharedPtr<Node> Parser::parse_keyword_splat_wrapped_in_hash(LocalsHashmap &locals) {
1643
- SharedPtr<HashNode> hash = new HashNode { current_token(), true };
1644
- hash->add_node(parse_keyword_splat(locals));
1645
- return hash.static_cast_as<Node>();
1646
- }
1647
-
1648
1720
  SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
1649
1721
  SharedPtr<String> name = new String("");
1650
1722
  auto token = current_token();
1651
1723
  switch (token.type()) {
1652
1724
  case Token::Type::BareName:
1653
1725
  case Token::Type::Constant:
1726
+ case Token::Type::OperatorName:
1654
1727
  name = current_token().literal_string();
1655
1728
  break;
1656
1729
  default:
1657
- if (token.is_operator() || token.is_keyword())
1730
+ if (token.is_operator() || token.is_keyword()) {
1658
1731
  name = new String(current_token().type_value());
1659
- else
1732
+ } else {
1660
1733
  throw_unexpected("method name");
1734
+ }
1661
1735
  }
1662
1736
  advance();
1663
1737
  return name;
@@ -1727,15 +1801,15 @@ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
1727
1801
  return new NthRefNode { token, token.get_fixnum() };
1728
1802
  }
1729
1803
 
1730
- void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1804
+ void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, IterAllow iter_allow) {
1731
1805
  if (current_token().is_semicolon()) {
1732
1806
  parse_shadow_variables_in_args(args, locals);
1733
1807
  return;
1734
1808
  }
1735
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1809
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1736
1810
  while (current_token().is_comma()) {
1737
1811
  advance();
1738
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1812
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1739
1813
  }
1740
1814
  if (current_token().is_semicolon()) {
1741
1815
  parse_shadow_variables_in_args(args, locals);
@@ -1848,13 +1922,13 @@ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
1848
1922
  if (current_token().is_rparen()) {
1849
1923
  advance(); // )
1850
1924
  } else {
1851
- parse_proc_args(args, locals);
1925
+ parse_proc_args(args, locals, IterAllow::CURLY_AND_BLOCK);
1852
1926
  expect(Token::Type::RParen, "proc args closing paren");
1853
1927
  advance(); // )
1854
1928
  }
1855
- } else if (current_token().is_bare_name() || current_token().type() == Token::Type::Star) {
1929
+ } else if (current_token().can_be_first_arg_of_def()) {
1856
1930
  has_args = true;
1857
- parse_proc_args(args, locals);
1931
+ parse_proc_args(args, locals, IterAllow::NONE);
1858
1932
  }
1859
1933
  if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
1860
1934
  throw_unexpected("block");
@@ -2026,36 +2100,23 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
2026
2100
  SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
2027
2101
  auto undef_token = current_token();
2028
2102
  advance();
2029
- auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
2030
- switch (token.type()) {
2031
- case Token::Type::BareName:
2032
- case Token::Type::Constant:
2033
- advance();
2034
- return new SymbolNode { token, token.literal_string() };
2035
- case Token::Type::Symbol:
2036
- return parse_symbol(locals);
2037
- case Token::Type::InterpolatedSymbolBegin: {
2038
- return parse_interpolated_symbol(locals);
2039
- }
2040
- default:
2041
- throw_unexpected("method name for undef");
2042
- }
2043
- };
2044
2103
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2045
- auto token = current_token();
2046
- undef_node->add_arg(symbol_from_token(token));
2104
+ auto arg = parse_alias_arg(locals, "method name for undef");
2105
+ undef_node->add_arg(arg.static_cast_as<Node>());
2047
2106
  if (current_token().is_comma()) {
2048
2107
  SharedPtr<BlockNode> block = new BlockNode { undef_token };
2049
2108
  block->add_node(undef_node.static_cast_as<Node>());
2050
2109
  while (current_token().is_comma()) {
2051
2110
  advance();
2052
- token = current_token();
2053
2111
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2054
- undef_node->add_arg(symbol_from_token(token));
2112
+ auto arg = parse_alias_arg(locals, "method name for undef");
2113
+ undef_node->add_arg(arg.static_cast_as<Node>());
2055
2114
  block->add_node(undef_node.static_cast_as<Node>());
2056
2115
  }
2116
+ reinsert_collapsed_newline();
2057
2117
  return block.static_cast_as<Node>();
2058
2118
  }
2119
+ reinsert_collapsed_newline();
2059
2120
  return undef_node.static_cast_as<Node>();
2060
2121
  };
2061
2122
 
@@ -2313,7 +2374,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
2313
2374
  if (bare && node.can_accept_a_block())
2314
2375
  m_call_depth.last()++;
2315
2376
  auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2316
- if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
2377
+ if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
2317
2378
  node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
2318
2379
  } else {
2319
2380
  node.add_arg(arg);
@@ -2325,7 +2386,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
2325
2386
  break;
2326
2387
  }
2327
2388
  arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2328
- if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
2389
+ if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
2329
2390
  node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
2330
2391
  break;
2331
2392
  } else {
@@ -2333,6 +2394,9 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
2333
2394
  }
2334
2395
  }
2335
2396
  }
2397
+ if (current_token().type() == Token::Type::Ampersand) {
2398
+ node.add_arg(parse_block_pass(locals));
2399
+ }
2336
2400
  if (bare && node.can_accept_a_block())
2337
2401
  m_call_depth.last()--;
2338
2402
  }
@@ -2374,6 +2438,7 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2374
2438
  SharedPtr<Node> node;
2375
2439
  switch (name_token.type()) {
2376
2440
  case Token::Type::BareName:
2441
+ case Token::Type::OperatorName:
2377
2442
  advance();
2378
2443
  node = new CallNode { name_token, left, name_token.literal_string() };
2379
2444
  break;
@@ -2392,7 +2457,12 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2392
2457
  break;
2393
2458
  }
2394
2459
  default:
2395
- throw_unexpected(name_token, ":: identifier name");
2460
+ if (name_token.is_operator() || name_token.is_keyword()) {
2461
+ advance();
2462
+ node = new CallNode { name_token, left, new String(name_token.type_value()) };
2463
+ } else {
2464
+ throw_unexpected(name_token, ":: identifier name");
2465
+ }
2396
2466
  }
2397
2467
  return node;
2398
2468
  }
@@ -2635,6 +2705,7 @@ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsH
2635
2705
  break;
2636
2706
  case Token::Type::BareName:
2637
2707
  case Token::Type::Constant:
2708
+ case Token::Type::OperatorName:
2638
2709
  name = name_token.literal_string();
2639
2710
  advance();
2640
2711
  break;
@@ -2661,6 +2732,7 @@ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashma
2661
2732
  switch (name_token.type()) {
2662
2733
  case Token::Type::BareName:
2663
2734
  case Token::Type::Constant:
2735
+ case Token::Type::OperatorName:
2664
2736
  name = name_token.literal_string();
2665
2737
  advance();
2666
2738
  break;
@@ -2700,7 +2772,14 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2700
2772
  auto token = current_token();
2701
2773
  advance();
2702
2774
  SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2703
- next_expression();
2775
+ if (condition->type() == Node::Type::Regexp) {
2776
+ condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2777
+ }
2778
+ if (current_token().type() == Token::Type::ThenKeyword) {
2779
+ advance(); // then
2780
+ } else {
2781
+ next_expression();
2782
+ }
2704
2783
  SharedPtr<Node> false_expr = parse_if_body(locals);
2705
2784
  SharedPtr<Node> true_expr;
2706
2785
  if (current_token().is_else_keyword()) {
@@ -2717,8 +2796,15 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2717
2796
  SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
2718
2797
  auto token = current_token();
2719
2798
  advance();
2720
- SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2721
- next_expression();
2799
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
2800
+ if (condition->type() == Node::Type::Regexp) {
2801
+ condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2802
+ }
2803
+ if (current_token().type() == Token::Type::DoKeyword) {
2804
+ advance();
2805
+ } else {
2806
+ next_expression();
2807
+ }
2722
2808
  SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
2723
2809
  expect(Token::Type::EndKeyword, "while end");
2724
2810
  advance();
@@ -2770,10 +2856,14 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2770
2856
  return &Parser::parse_end_block;
2771
2857
  case Type::FILEKeyword:
2772
2858
  return &Parser::parse_file_constant;
2859
+ case Type::ForKeyword:
2860
+ return &Parser::parse_for;
2773
2861
  case Type::LParen:
2774
2862
  return &Parser::parse_group;
2775
2863
  case Type::LCurlyBrace:
2776
2864
  return &Parser::parse_hash;
2865
+ case Type::LINEKeyword:
2866
+ return &Parser::parse_line_constant;
2777
2867
  case Type::BareName:
2778
2868
  case Type::ClassVariable:
2779
2869
  case Type::Constant:
@@ -2791,7 +2881,7 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2791
2881
  case Type::InterpolatedSymbolBegin:
2792
2882
  return &Parser::parse_interpolated_symbol;
2793
2883
  case Type::StarStar:
2794
- return &Parser::parse_keyword_splat_wrapped_in_hash;
2884
+ return &Parser::parse_keyword_splat;
2795
2885
  case Type::Bignum:
2796
2886
  case Type::Fixnum:
2797
2887
  case Type::Float:
@@ -2857,7 +2947,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
2857
2947
  using Type = Token::Type;
2858
2948
  switch (token.type()) {
2859
2949
  case Type::Equal:
2860
- if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2950
+ if (precedence == Precedence::ARRAY || precedence == Precedence::HASH || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2861
2951
  return &Parser::parse_assignment_expression_without_multiple_values;
2862
2952
  else
2863
2953
  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.0.0
4
+ version: 2.2.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-06-24 00:00:00.000000000 Z
11
+ date: 2022-10-25 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.
@@ -65,6 +65,7 @@ files:
65
65
  - include/natalie_parser/node/false_node.hpp
66
66
  - include/natalie_parser/node/fixnum_node.hpp
67
67
  - include/natalie_parser/node/float_node.hpp
68
+ - include/natalie_parser/node/for_node.hpp
68
69
  - include/natalie_parser/node/forward_args_node.hpp
69
70
  - include/natalie_parser/node/hash_node.hpp
70
71
  - include/natalie_parser/node/hash_pattern_node.hpp