natalie_parser 2.0.0 → 2.2.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
  }
@@ -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