natalie_parser 1.0.0 → 1.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +11 -4
  4. data/Rakefile +12 -5
  5. data/ext/natalie_parser/mri_creator.hpp +25 -7
  6. data/include/natalie_parser/creator/debug_creator.hpp +13 -3
  7. data/include/natalie_parser/creator.hpp +4 -2
  8. data/include/natalie_parser/node/array_pattern_node.hpp +20 -2
  9. data/include/natalie_parser/node/bignum_node.hpp +5 -1
  10. data/include/natalie_parser/node/case_in_node.hpp +5 -2
  11. data/include/natalie_parser/node/complex_node.hpp +49 -0
  12. data/include/natalie_parser/node/fixnum_node.hpp +5 -1
  13. data/include/natalie_parser/node/float_node.hpp +4 -0
  14. data/include/natalie_parser/node/forward_args_node.hpp +26 -0
  15. data/include/natalie_parser/node/hash_pattern_node.hpp +1 -0
  16. data/include/natalie_parser/node/infix_op_node.hpp +1 -1
  17. data/include/natalie_parser/node/iter_node.hpp +1 -1
  18. data/include/natalie_parser/node/keyword_rest_pattern_node.hpp +43 -0
  19. data/include/natalie_parser/node/node.hpp +7 -1
  20. data/include/natalie_parser/node/nth_ref_node.hpp +1 -1
  21. data/include/natalie_parser/node/rational_node.hpp +45 -0
  22. data/include/natalie_parser/node.hpp +4 -0
  23. data/include/natalie_parser/parser.hpp +14 -1
  24. data/include/natalie_parser/token.hpp +62 -13
  25. data/lib/natalie_parser/version.rb +1 -1
  26. data/src/lexer/interpolated_string_lexer.cpp +9 -9
  27. data/src/lexer/regexp_lexer.cpp +7 -7
  28. data/src/lexer/word_array_lexer.cpp +13 -13
  29. data/src/lexer.cpp +210 -181
  30. data/src/node/begin_rescue_node.cpp +1 -1
  31. data/src/node/interpolated_regexp_node.cpp +1 -1
  32. data/src/node/node.cpp +7 -0
  33. data/src/node/node_with_args.cpp +1 -0
  34. data/src/parser.cpp +261 -91
  35. metadata +6 -2
data/src/parser.cpp CHANGED
@@ -16,7 +16,7 @@ enum class Parser::Precedence {
16
16
  INLINE_RESCUE, // foo rescue 2
17
17
  ITER_BLOCK, // do |n| ... end
18
18
  BARE_CALL_ARG, // foo (_), b
19
- OP_ASSIGNMENT, // += -= *= **= /= %= |= &= ^= >>= <<= ||= &&=
19
+ OP_ASSIGNMENT_RHS, // x += (_)
20
20
  TERNARY_TRUE, // _ ? (_) : _
21
21
  CALL_ARG, // foo( (_), b )
22
22
  TERNARY_QUESTION, // (_) ? _ : _
@@ -28,6 +28,7 @@ enum class Parser::Precedence {
28
28
  LOGICAL_NOT, // not
29
29
  EQUALITY, // <=> == === != =~ !~
30
30
  LESS_GREATER, // <= < > >=
31
+ OP_ASSIGNMENT_LHS, // (_) += 1
31
32
  BITWISE_OR, // ^ |
32
33
  BITWISE_AND, // &
33
34
  BITWISE_SHIFT, // << >>
@@ -112,7 +113,7 @@ Parser::Precedence Parser::get_precedence(Token &token, SharedPtr<Node> left) {
112
113
  case Token::Type::SlashEqual:
113
114
  case Token::Type::StarEqual:
114
115
  case Token::Type::StarStarEqual:
115
- return Precedence::OP_ASSIGNMENT;
116
+ return Precedence::OP_ASSIGNMENT_LHS;
116
117
  case Token::Type::Ampersand:
117
118
  return Precedence::BITWISE_AND;
118
119
  case Token::Type::Caret:
@@ -248,7 +249,7 @@ SharedPtr<BlockNode> Parser::parse_body(LocalsHashmap &locals, Precedence preced
248
249
  validate_current_token();
249
250
  skip_newlines();
250
251
  while (!current_token().is_eof() && !is_end(current_token().type())) {
251
- if (allow_rescue && current_token().type() == Token::Type::RescueKeyword) {
252
+ if (allow_rescue && (current_token().is_rescue() || current_token().is_ensure())) {
252
253
  auto token = body->token();
253
254
  SharedPtr<BeginNode> begin_node = new BeginNode { body->token(), body };
254
255
  parse_rest_of_begin(begin_node.ref(), locals);
@@ -312,7 +313,7 @@ SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char
312
313
  // def bar; end
313
314
  //
314
315
  // So, we'll put the newline back.
315
- m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column() });
316
+ m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column(), token.whitespace_precedes() });
316
317
  }
317
318
  return new SymbolNode { token, new String(token.type_value()) };
318
319
  } else {
@@ -623,6 +624,11 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
623
624
  advance();
624
625
  node = new IdentifierNode { token, true };
625
626
  break;
627
+ case Token::Type::Bignum:
628
+ case Token::Type::Fixnum:
629
+ case Token::Type::Float:
630
+ node = parse_lit(locals);
631
+ break;
626
632
  case Token::Type::Caret:
627
633
  advance();
628
634
  expect(Token::Type::BareName, "pinned variable name");
@@ -632,12 +638,18 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
632
638
  case Token::Type::Constant:
633
639
  node = parse_constant(locals);
634
640
  break;
641
+ case Token::Type::DotDot:
642
+ case Token::Type::DotDotDot:
643
+ node = parse_beginless_range(locals);
644
+ break;
635
645
  case Token::Type::LBracketRBracket:
636
646
  advance();
637
647
  node = new ArrayPatternNode { token };
638
648
  break;
649
+ case Token::Type::InterpolatedStringBegin:
650
+ node = parse_interpolated_string(locals);
651
+ break;
639
652
  case Token::Type::LBracket: {
640
- // TODO: might need to keep track of and pass along precedence value?
641
653
  advance();
642
654
  SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
643
655
  if (current_token().is_rbracket()) {
@@ -648,7 +660,10 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
648
660
  array->add_node(parse_case_in_pattern(locals));
649
661
  while (current_token().is_comma()) {
650
662
  advance();
651
- array->add_node(parse_case_in_pattern(locals));
663
+ if (current_token().is_rbracket())
664
+ array->add_node(new SplatNode { current_token() });
665
+ else
666
+ array->add_node(parse_case_in_pattern(locals));
652
667
  }
653
668
  expect(Token::Type::RBracket, "array pattern closing bracket");
654
669
  advance();
@@ -658,47 +673,71 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
658
673
  case Token::Type::LCurlyBrace: {
659
674
  advance();
660
675
  SharedPtr<HashPatternNode> hash = new HashPatternNode { token };
676
+ node = hash.static_cast_as<Node>();
661
677
  if (current_token().type() == Token::Type::RCurlyBrace) {
662
678
  advance();
663
679
  node = hash.static_cast_as<Node>();
664
680
  break;
665
681
  }
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));
682
+
683
+ auto add_pair = [&]() {
684
+ auto key = parse_case_in_pattern_hash_symbol_key(locals);
685
+ hash->add_node(key);
686
+ if (key->type() == Node::Type::KeywordRestPattern) {
687
+ // nothing else to do
688
+ } else if (current_token().type() == Token::Type::RCurlyBrace || current_token().type() == Token::Type::Comma) {
689
+ if (key->type() == Node::Type::SymbolKey)
690
+ locals.set(key.static_cast_as<SymbolKeyNode>()->name().ref());
691
+ hash->add_node(new NilNode { current_token() });
692
+ } else {
693
+ hash->add_node(parse_case_in_pattern(locals));
694
+ }
695
+ };
696
+
697
+ add_pair();
698
+
669
699
  while (current_token().is_comma()) {
670
700
  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));
701
+ add_pair();
674
702
  }
703
+
675
704
  expect(Token::Type::RCurlyBrace, "hash pattern closing brace");
676
705
  advance();
677
- node = hash.static_cast_as<Node>();
678
706
  break;
679
707
  }
680
- case Token::Type::Bignum:
681
- case Token::Type::Fixnum:
682
- case Token::Type::Float:
683
- node = parse_lit(locals);
708
+ case Token::Type::LParen:
709
+ advance(); // (
710
+ node = parse_case_in_pattern(locals);
711
+ expect(Token::Type::RParen, "closing paren for pattern");
712
+ advance();
713
+ break;
714
+ case Token::Type::Minus:
715
+ node = parse_unary_operator(locals);
716
+ break;
717
+ case Token::Type::NilKeyword:
718
+ node = parse_nil(locals);
684
719
  break;
685
720
  case Token::Type::Star: {
686
- auto splat_token = current_token();
687
- advance();
688
- SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
721
+ advance(); // *
689
722
  switch (current_token().type()) {
690
723
  case Token::Type::BareName:
691
724
  case Token::Type::Constant: {
692
- auto name = new IdentifierNode { current_token(), true };
693
- name->prepend_to_name('*');
694
- array->add_node(name);
725
+ SharedPtr<String> name = current_token().literal_string();
726
+ auto symbol = new SymbolNode { current_token(), name };
727
+ node = new SplatNode { symbol->token(), symbol };
695
728
  advance();
696
729
  break;
697
730
  }
698
731
  default:
699
- array->add_node(new SplatNode { splat_token });
732
+ node = new SplatNode { current_token() };
700
733
  }
701
- node = array.static_cast_as<Node>();
734
+ break;
735
+ }
736
+ case Token::Type::StarStar: {
737
+ SharedPtr<HashPatternNode> hash = new HashPatternNode { token };
738
+ auto key = parse_case_in_pattern_hash_symbol_key(locals);
739
+ hash->add_node(key);
740
+ node = hash.static_cast_as<Node>();
702
741
  break;
703
742
  }
704
743
  case Token::Type::String:
@@ -710,6 +749,11 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
710
749
  default:
711
750
  throw_unexpected("case in pattern");
712
751
  }
752
+
753
+ if (current_token().type() == Token::Type::DotDot || current_token().type() == Token::Type::DotDotDot) {
754
+ node = parse_range_expression(node, locals);
755
+ }
756
+
713
757
  token = current_token();
714
758
  if (token.is_hash_rocket()) {
715
759
  advance();
@@ -722,26 +766,67 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
722
766
  return node;
723
767
  }
724
768
 
769
+ SharedPtr<Node> Parser::parse_case_in_pattern_alternation(LocalsHashmap &locals) {
770
+ SharedPtr<ArrayPatternNode> array_pattern = new ArrayPatternNode { current_token() };
771
+ array_pattern->add_node(parse_case_in_pattern(locals));
772
+ while (current_token().is_comma()) {
773
+ advance();
774
+ array_pattern->add_node(parse_case_in_pattern(locals));
775
+ }
776
+ if (array_pattern->nodes().size() == 1)
777
+ return array_pattern->nodes().first();
778
+ return array_pattern.static_cast_as<Node>();
779
+ }
780
+
781
+ SharedPtr<Node> Parser::parse_case_in_pattern_hash_symbol_key(LocalsHashmap &locals) {
782
+ auto token = current_token();
783
+ SharedPtr<Node> node;
784
+ switch (token.type()) {
785
+ case Token::Type::InterpolatedStringBegin:
786
+ node = parse_interpolated_string(locals);
787
+ if (node->type() != Node::Type::SymbolKey)
788
+ throw_unexpected(token, "hash pattern symbol key");
789
+ break;
790
+ case Token::Type::StarStar:
791
+ advance(); // **
792
+ switch (current_token().type()) {
793
+ case Token::Type::NilKeyword:
794
+ node = new KeywordRestPatternNode { token, current_token().type_value() };
795
+ advance();
796
+ break;
797
+ case Token::Type::BareName: {
798
+ auto name = current_token().literal_string();
799
+ node = new KeywordRestPatternNode { token, name };
800
+ locals.set(name.ref());
801
+ advance();
802
+ break;
803
+ }
804
+ default:
805
+ node = new KeywordRestPatternNode { token };
806
+ break;
807
+ }
808
+ break;
809
+ case Token::Type::SymbolKey:
810
+ node = parse_symbol_key(locals);
811
+ break;
812
+ default:
813
+ throw_unexpected("hash pattern symbol key");
814
+ }
815
+ return node;
816
+ }
817
+
725
818
  SharedPtr<Node> Parser::parse_case_in_patterns(LocalsHashmap &locals) {
726
819
  Vector<SharedPtr<Node>> patterns;
727
- patterns.push(parse_case_in_pattern(locals));
820
+ auto pattern = parse_case_in_pattern_alternation(locals);
821
+ if (pattern->type() == Node::Type::Splat)
822
+ pattern = new ArrayPatternNode { pattern->token(), pattern };
823
+ patterns.push(pattern);
728
824
  while (current_token().type() == Token::Type::Pipe) {
729
825
  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
- }
826
+ auto pattern = parse_case_in_pattern_alternation(locals);
827
+ if (pattern->type() == Node::Type::Splat)
828
+ pattern = new ArrayPatternNode { pattern->token(), pattern };
829
+ patterns.push(pattern);
745
830
  }
746
831
  assert(patterns.size() > 0);
747
832
  if (patterns.size() == 1) {
@@ -848,13 +933,12 @@ SharedPtr<Node> Parser::parse_assignment_identifier(bool allow_splat, LocalsHash
848
933
  case Token::Type::Star: {
849
934
  if (!allow_splat)
850
935
  expect(Token::Type::BareName, "assignment identifier");
851
- auto splat_token = current_token();
852
936
  advance();
853
937
  if (current_token().is_assignable()) {
854
938
  auto id = parse_assignment_identifier(false, locals);
855
- node = new SplatNode { splat_token, id };
939
+ node = new SplatNode { token, id };
856
940
  } else {
857
- node = new SplatNode { splat_token };
941
+ node = new SplatNode { token };
858
942
  }
859
943
  break;
860
944
  }
@@ -979,28 +1063,32 @@ SharedPtr<Node> Parser::parse_defined(LocalsHashmap &locals) {
979
1063
  }
980
1064
 
981
1065
  void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
982
- parse_def_single_arg(args, locals);
1066
+ parse_def_single_arg(args, locals, ArgsContext::Method);
983
1067
  while (current_token().is_comma()) {
984
1068
  advance();
985
- parse_def_single_arg(args, locals);
1069
+ parse_def_single_arg(args, locals, ArgsContext::Method);
986
1070
  }
987
1071
  }
988
1072
 
989
- void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1073
+ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context) {
990
1074
  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
1075
  auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
992
1076
 
993
1077
  auto token = current_token();
1078
+
1079
+ if (!args.is_empty() && args.last()->type() == Node::Type::ForwardArgs)
1080
+ throw_error(token, "anything after arg forwarding (...) shorthand");
1081
+
994
1082
  switch (token.type()) {
995
1083
  case Token::Type::BareName: {
996
1084
  if (args_have_keyword())
997
- throw_unexpected(token, nullptr, "normal arg after keyword arg");
1085
+ throw_error(token, "normal arg after keyword arg");
998
1086
  SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
999
1087
  advance();
1000
1088
  arg->add_to_locals(locals);
1001
1089
  if (current_token().is_equal()) {
1002
1090
  if (args_have_any_splat())
1003
- throw_unexpected(token, nullptr, "default value after splat");
1091
+ throw_error(token, "default value after splat");
1004
1092
  advance(); // =
1005
1093
  arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1006
1094
  }
@@ -1022,7 +1110,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1022
1110
  }
1023
1111
  case Token::Type::Star: {
1024
1112
  if (args_have_any_splat())
1025
- throw_unexpected(token, nullptr, "splat after keyword splat");
1113
+ throw_error(token, "splat after keyword splat");
1026
1114
  advance();
1027
1115
  SharedPtr<ArgNode> arg;
1028
1116
  if (current_token().is_bare_name()) {
@@ -1080,6 +1168,15 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1080
1168
  args.push(arg.static_cast_as<Node>());
1081
1169
  return;
1082
1170
  }
1171
+ case Token::Type::DotDotDot: {
1172
+ if (context == ArgsContext::Block)
1173
+ throw_error(token, "arg forwarding (...) shorthand not allowed in block");
1174
+ SharedPtr<ForwardArgsNode> arg = new ForwardArgsNode { token };
1175
+ advance();
1176
+ arg->add_to_locals(locals);
1177
+ args.push(arg.static_cast_as<Node>());
1178
+ return;
1179
+ }
1083
1180
  default:
1084
1181
  throw_unexpected("argument");
1085
1182
  }
@@ -1151,6 +1248,15 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
1151
1248
  return new StringNode { token, token.file() };
1152
1249
  }
1153
1250
 
1251
+ SharedPtr<Node> Parser::parse_forward_args(LocalsHashmap &locals) {
1252
+ auto token = current_token();
1253
+ if (!locals.get("..."))
1254
+ throw_error(token, "forwarding args without ... shorthand in method definition");
1255
+ advance(); // ...
1256
+ SharedPtr<ForwardArgsNode> node = new ForwardArgsNode { token };
1257
+ return node.static_cast_as<Node>();
1258
+ }
1259
+
1154
1260
  SharedPtr<Node> Parser::parse_group(LocalsHashmap &locals) {
1155
1261
  auto token = current_token();
1156
1262
  advance(); // (
@@ -1491,19 +1597,40 @@ SharedPtr<Node> Parser::parse_interpolated_symbol(LocalsHashmap &locals) {
1491
1597
 
1492
1598
  SharedPtr<Node> Parser::parse_lit(LocalsHashmap &) {
1493
1599
  auto token = current_token();
1600
+ SharedPtr<Node> node;
1494
1601
  switch (token.type()) {
1495
1602
  case Token::Type::Bignum:
1496
1603
  advance();
1497
- return new BignumNode { token, token.literal_string() };
1604
+ node = new BignumNode { token, token.literal_string() };
1605
+ break;
1498
1606
  case Token::Type::Fixnum:
1499
1607
  advance();
1500
- return new FixnumNode { token, token.get_fixnum() };
1608
+ node = new FixnumNode { token, token.get_fixnum() };
1609
+ break;
1501
1610
  case Token::Type::Float:
1502
1611
  advance();
1503
- return new FloatNode { token, token.get_double() };
1612
+ node = new FloatNode { token, token.get_double() };
1613
+ break;
1504
1614
  default:
1505
1615
  TM_UNREACHABLE();
1506
1616
  }
1617
+ switch (current_token().type()) {
1618
+ case Token::Type::Complex:
1619
+ advance();
1620
+ node = new ComplexNode { token, node };
1621
+ break;
1622
+ case Token::Type::Rational:
1623
+ advance();
1624
+ node = new RationalNode { token, node };
1625
+ break;
1626
+ case Token::Type::RationalComplex:
1627
+ advance();
1628
+ node = new ComplexNode { token, new RationalNode { token, node } };
1629
+ break;
1630
+ default:
1631
+ break;
1632
+ }
1633
+ return node;
1507
1634
  };
1508
1635
 
1509
1636
  SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
@@ -1605,10 +1732,10 @@ void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &local
1605
1732
  parse_shadow_variables_in_args(args, locals);
1606
1733
  return;
1607
1734
  }
1608
- parse_def_single_arg(args, locals);
1735
+ parse_def_single_arg(args, locals, ArgsContext::Proc);
1609
1736
  while (current_token().is_comma()) {
1610
1737
  advance();
1611
- parse_def_single_arg(args, locals);
1738
+ parse_def_single_arg(args, locals, ArgsContext::Proc);
1612
1739
  }
1613
1740
  if (current_token().is_semicolon()) {
1614
1741
  parse_shadow_variables_in_args(args, locals);
@@ -1842,11 +1969,14 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1842
1969
  advance();
1843
1970
  auto precedence = get_precedence(token);
1844
1971
  auto receiver = parse_expression(precedence, locals);
1972
+
1845
1973
  if ((token.type() == Token::Type::Minus || token.type() == Token::Type::Plus) && receiver->is_numeric()) {
1846
1974
  switch (receiver->type()) {
1847
1975
  case Node::Type::Bignum: {
1848
1976
  if (token.type() == Token::Type::Minus) {
1849
1977
  auto num = receiver.static_cast_as<BignumNode>();
1978
+ if (num->negative())
1979
+ break;
1850
1980
  num->negate();
1851
1981
  return num.static_cast_as<Node>();
1852
1982
  }
@@ -1855,6 +1985,8 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1855
1985
  case Node::Type::Fixnum: {
1856
1986
  if (token.type() == Token::Type::Minus) {
1857
1987
  auto num = receiver.static_cast_as<FixnumNode>();
1988
+ if (num->negative())
1989
+ break;
1858
1990
  num->negate();
1859
1991
  return num.static_cast_as<Node>();
1860
1992
  }
@@ -1863,6 +1995,8 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1863
1995
  case Node::Type::Float: {
1864
1996
  if (token.type() == Token::Type::Minus) {
1865
1997
  auto num = receiver.static_cast_as<FloatNode>();
1998
+ if (num->negative())
1999
+ break;
1866
2000
  num->negate();
1867
2001
  return num.static_cast_as<Node>();
1868
2002
  }
@@ -2056,6 +2190,12 @@ SharedPtr<Node> Parser::parse_iter_expression(SharedPtr<Node> left, LocalsHashma
2056
2190
  bool curly_brace = current_token().type() == Token::Type::LCurlyBrace;
2057
2191
  bool has_args = false;
2058
2192
  auto args = Vector<SharedPtr<Node>> {};
2193
+
2194
+ if (curly_brace) {
2195
+ if (left->type() == Node::Type::Call && !left.static_cast_as<CallNode>()->args().is_empty() && !previous_token().is_rparen())
2196
+ throw_unexpected("nearest object cannot accept a { ... } block");
2197
+ }
2198
+
2059
2199
  if (left->type() == Node::Type::StabbyProc) {
2060
2200
  advance(); // { or do
2061
2201
  auto stabby_proc_node = left.static_cast_as<StabbyProcNode>();
@@ -2101,7 +2241,7 @@ void Parser::parse_iter_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &local
2101
2241
  parse_shadow_variables_in_args(args, locals);
2102
2242
  return;
2103
2243
  }
2104
- parse_def_single_arg(args, locals);
2244
+ parse_def_single_arg(args, locals, ArgsContext::Block);
2105
2245
  while (current_token().is_comma()) {
2106
2246
  advance();
2107
2247
  if (current_token().is_block_arg_delimiter()) {
@@ -2109,7 +2249,7 @@ void Parser::parse_iter_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &local
2109
2249
  args.push(new NilNode { current_token() });
2110
2250
  break;
2111
2251
  }
2112
- parse_def_single_arg(args, locals);
2252
+ parse_def_single_arg(args, locals, ArgsContext::Block);
2113
2253
  }
2114
2254
  if (current_token().is_semicolon()) {
2115
2255
  parse_shadow_variables_in_args(args, locals);
@@ -2149,6 +2289,15 @@ SharedPtr<NodeWithArgs> Parser::to_node_with_args(SharedPtr<Node> node) {
2149
2289
  };
2150
2290
  return call_node;
2151
2291
  }
2292
+ case Node::Type::Colon2: {
2293
+ auto colon2 = node.static_cast_as<Colon2Node>();
2294
+ auto call_node = new CallNode {
2295
+ colon2->token(),
2296
+ colon2->left(),
2297
+ colon2->name(),
2298
+ };
2299
+ return call_node;
2300
+ }
2152
2301
  case Node::Type::Call:
2153
2302
  case Node::Type::SafeCall:
2154
2303
  case Node::Type::Super:
@@ -2156,12 +2305,12 @@ SharedPtr<NodeWithArgs> Parser::to_node_with_args(SharedPtr<Node> node) {
2156
2305
  case Node::Type::Yield:
2157
2306
  return node.static_cast_as<NodeWithArgs>();
2158
2307
  default:
2159
- throw_unexpected(current_token(), nullptr, "left-hand-side is not callable");
2308
+ throw_error(current_token(), "left-hand-side is not callable");
2160
2309
  }
2161
2310
  }
2162
2311
 
2163
2312
  void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bare, Token::Type closing_token_type) {
2164
- if (node.can_accept_a_block())
2313
+ if (bare && node.can_accept_a_block())
2165
2314
  m_call_depth.last()++;
2166
2315
  auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2167
2316
  if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
@@ -2184,7 +2333,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
2184
2333
  }
2185
2334
  }
2186
2335
  }
2187
- if (node.can_accept_a_block())
2336
+ if (bare && node.can_accept_a_block())
2188
2337
  m_call_depth.last()--;
2189
2338
  }
2190
2339
 
@@ -2381,7 +2530,7 @@ SharedPtr<Node> Parser::parse_op_attr_assign_expression(SharedPtr<Node> left, Lo
2381
2530
  auto left_call = left.static_cast_as<CallNode>();
2382
2531
  auto token = current_token();
2383
2532
  advance();
2384
- auto value = parse_expression(Precedence::OP_ASSIGNMENT, locals);
2533
+ auto value = parse_expression(Precedence::OP_ASSIGNMENT_RHS, locals);
2385
2534
 
2386
2535
  if (*left_call->message() != "[]") {
2387
2536
  if (token.type() == Token::Type::AmpersandAmpersandEqual) {
@@ -2428,15 +2577,19 @@ SharedPtr<Node> Parser::parse_proc_call_expression(SharedPtr<Node> left, LocalsH
2428
2577
 
2429
2578
  SharedPtr<Node> Parser::parse_range_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2430
2579
  auto token = current_token();
2431
- advance();
2580
+ advance(); // .. or ...
2581
+ skip_newlines();
2432
2582
  SharedPtr<Node> right;
2433
- try {
2583
+ if (current_token().can_be_range_arg_token()) {
2434
2584
  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. ¯\_(ツ)_/¯
2585
+ } else {
2586
+ // endless range
2438
2587
  right = new NilNode { token };
2588
+ // HACK: insert a newline here so subsequent expressions parse ok
2589
+ if (!current_token().can_follow_collapsible_newline())
2590
+ m_tokens->insert(m_index, Token { Token::Type::Newline, current_token().file(), current_token().line(), current_token().column(), current_token().whitespace_precedes() });
2439
2591
  }
2592
+
2440
2593
  return new RangeNode { token, left, right, token.type() == Token::Type::DotDotDot };
2441
2594
  }
2442
2595
 
@@ -2536,6 +2689,12 @@ SharedPtr<Node> Parser::parse_ternary_expression(SharedPtr<Node> left, LocalsHas
2536
2689
  return new IfNode { token, left, true_expr, false_expr };
2537
2690
  }
2538
2691
 
2692
+ SharedPtr<Node> Parser::parse_triple_dot(LocalsHashmap &locals) {
2693
+ if (peek_token().is_rparen())
2694
+ return parse_forward_args(locals);
2695
+ return parse_beginless_range(locals);
2696
+ }
2697
+
2539
2698
  SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2540
2699
  auto token = current_token();
2541
2700
  advance();
@@ -2603,7 +2762,7 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2603
2762
  return &Parser::parse_defined;
2604
2763
  case Type::DotDot:
2605
2764
  case Type::DotDotDot:
2606
- return &Parser::parse_beginless_range;
2765
+ return &Parser::parse_triple_dot;
2607
2766
  case Type::ENCODINGKeyword:
2608
2767
  return &Parser::parse_encoding;
2609
2768
  case Type::ENDKeyword:
@@ -2717,16 +2876,19 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
2717
2876
  case Type::LeftShift:
2718
2877
  case Type::LessThan:
2719
2878
  case Type::LessThanOrEqual:
2720
- case Type::Minus:
2721
2879
  case Type::NotEqual:
2722
2880
  case Type::Percent:
2723
2881
  case Type::Pipe:
2724
- case Type::Plus:
2725
2882
  case Type::RightShift:
2726
2883
  case Type::Slash:
2727
2884
  case Type::Star:
2728
2885
  case Type::StarStar:
2729
2886
  return &Parser::parse_infix_expression;
2887
+ case Type::Minus:
2888
+ case Type::Plus:
2889
+ if (peek_token().whitespace_precedes() || !left->is_callable())
2890
+ return &Parser::parse_infix_expression;
2891
+ break;
2730
2892
  case Type::DoKeyword:
2731
2893
  case Type::LCurlyBrace:
2732
2894
  return &Parser::parse_iter_expression;
@@ -2793,20 +2955,22 @@ bool Parser::is_first_arg_of_call_without_parens(SharedPtr<Node> left, Token &to
2793
2955
  return left->is_callable() && token.can_be_first_arg_of_implicit_call();
2794
2956
  }
2795
2957
 
2958
+ Token &Parser::previous_token() const {
2959
+ if (m_index > 0)
2960
+ return (*m_tokens)[m_index - 1];
2961
+ return Token::invalid();
2962
+ }
2963
+
2796
2964
  Token &Parser::current_token() const {
2797
- if (m_index < m_tokens->size()) {
2965
+ if (m_index < m_tokens->size())
2798
2966
  return m_tokens->at(m_index);
2799
- } else {
2800
- return Token::invalid();
2801
- }
2967
+ return Token::invalid();
2802
2968
  }
2803
2969
 
2804
2970
  Token &Parser::peek_token() const {
2805
- if (m_index + 1 < m_tokens->size()) {
2971
+ if (m_index + 1 < m_tokens->size())
2806
2972
  return (*m_tokens)[m_index + 1];
2807
- } else {
2808
- return Token::invalid();
2809
- }
2973
+ return Token::invalid();
2810
2974
  }
2811
2975
 
2812
2976
  void Parser::next_expression() {
@@ -2826,6 +2990,10 @@ void Parser::expect(Token::Type type, const char *expected) {
2826
2990
  throw_unexpected(expected);
2827
2991
  }
2828
2992
 
2993
+ void Parser::throw_error(const Token &token, const char *error) {
2994
+ throw_unexpected(token, nullptr, error);
2995
+ }
2996
+
2829
2997
  void Parser::throw_unexpected(const Token &token, const char *expected, const char *error) {
2830
2998
  auto file = token.file() ? String(*token.file()) : String("(unknown)");
2831
2999
  auto line = token.line() + 1;
@@ -2881,20 +3049,22 @@ void Parser::throw_unterminated_thing(Token token, Token start_token) {
2881
3049
  auto indent = String { start_token.column(), ' ' };
2882
3050
  String expected;
2883
3051
  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());
3052
+ if (lit) {
3053
+ if (strcmp(lit, "(") == 0)
3054
+ expected = "')'";
3055
+ else if (strcmp(lit, "[") == 0)
3056
+ expected = "']'";
3057
+ else if (strcmp(lit, "{") == 0)
3058
+ expected = "'}'";
3059
+ else if (strcmp(lit, "<") == 0)
3060
+ expected = "'>'";
3061
+ else if (strcmp(lit, "'") == 0)
3062
+ expected = "\"'\"";
3063
+ else
3064
+ expected = String::format("'{}'", lit);
3065
+ } else {
3066
+ expected = "delimiter"; // FIXME: why do we not know what this delimiter is?
3067
+ }
2898
3068
  const char *thing = nullptr;
2899
3069
  switch (token.type()) {
2900
3070
  case Token::Type::InterpolatedRegexpBegin: