natalie_parser 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ SharedPtr<Node> BeginRescueNode::name_to_node() const {
10
10
  token(),
11
11
  m_name.static_cast_as<Node>(),
12
12
  new IdentifierNode {
13
- Token { Token::Type::GlobalVariable, "$!", file(), line(), column() },
13
+ Token { Token::Type::GlobalVariable, "$!", file(), line(), column(), false },
14
14
  false },
15
15
  };
16
16
  }
data/src/node/node.cpp CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "natalie_parser/node.hpp"
2
+ #include "natalie_parser/creator/debug_creator.hpp"
2
3
 
3
4
  namespace NatalieParser {
4
5
 
@@ -7,4 +8,10 @@ BlockNode &Node::as_block_node() {
7
8
  return *static_cast<BlockNode *>(this);
8
9
  }
9
10
 
11
+ void Node::debug() {
12
+ DebugCreator creator;
13
+ transform(&creator);
14
+ printf("DEBUG[type=%d]: %s\n", (int)type(), creator.to_string().c_str());
15
+ }
16
+
10
17
  }
data/src/parser.cpp CHANGED
@@ -249,7 +249,7 @@ SharedPtr<BlockNode> Parser::parse_body(LocalsHashmap &locals, Precedence preced
249
249
  validate_current_token();
250
250
  skip_newlines();
251
251
  while (!current_token().is_eof() && !is_end(current_token().type())) {
252
- if (allow_rescue && current_token().type() == Token::Type::RescueKeyword) {
252
+ if (allow_rescue && (current_token().is_rescue() || current_token().is_ensure())) {
253
253
  auto token = body->token();
254
254
  SharedPtr<BeginNode> begin_node = new BeginNode { body->token(), body };
255
255
  parse_rest_of_begin(begin_node.ref(), locals);
@@ -313,7 +313,7 @@ SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char
313
313
  // def bar; end
314
314
  //
315
315
  // So, we'll put the newline back.
316
- 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() });
317
317
  }
318
318
  return new SymbolNode { token, new String(token.type_value()) };
319
319
  } else {
@@ -337,7 +337,7 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
337
337
  return array.static_cast_as<Node>();
338
338
  }
339
339
  if (token.type() == Token::Type::SymbolKey) {
340
- array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket));
340
+ array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, true));
341
341
  expect(Token::Type::RBracket, "array closing bracket");
342
342
  advance();
343
343
  return array.static_cast_as<Node>();
@@ -345,7 +345,7 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
345
345
  auto value = parse_expression(Precedence::ARRAY, locals);
346
346
  token = current_token();
347
347
  if (token.is_hash_rocket()) {
348
- array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, value));
348
+ array->add_node(parse_hash_inner(locals, Precedence::HASH, Token::Type::RBracket, true, value));
349
349
  expect(Token::Type::RBracket, "array closing bracket");
350
350
  advance();
351
351
  return array.static_cast_as<Node>();
@@ -624,6 +624,11 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
624
624
  advance();
625
625
  node = new IdentifierNode { token, true };
626
626
  break;
627
+ case Token::Type::Bignum:
628
+ case Token::Type::Fixnum:
629
+ case Token::Type::Float:
630
+ node = parse_lit(locals);
631
+ break;
627
632
  case Token::Type::Caret:
628
633
  advance();
629
634
  expect(Token::Type::BareName, "pinned variable name");
@@ -633,12 +638,18 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
633
638
  case Token::Type::Constant:
634
639
  node = parse_constant(locals);
635
640
  break;
641
+ case Token::Type::DotDot:
642
+ case Token::Type::DotDotDot:
643
+ node = parse_beginless_range(locals);
644
+ break;
636
645
  case Token::Type::LBracketRBracket:
637
646
  advance();
638
647
  node = new ArrayPatternNode { token };
639
648
  break;
649
+ case Token::Type::InterpolatedStringBegin:
650
+ node = parse_interpolated_string(locals);
651
+ break;
640
652
  case Token::Type::LBracket: {
641
- // TODO: might need to keep track of and pass along precedence value?
642
653
  advance();
643
654
  SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
644
655
  if (current_token().is_rbracket()) {
@@ -649,7 +660,10 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
649
660
  array->add_node(parse_case_in_pattern(locals));
650
661
  while (current_token().is_comma()) {
651
662
  advance();
652
- 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));
653
667
  }
654
668
  expect(Token::Type::RBracket, "array pattern closing bracket");
655
669
  advance();
@@ -659,47 +673,71 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
659
673
  case Token::Type::LCurlyBrace: {
660
674
  advance();
661
675
  SharedPtr<HashPatternNode> hash = new HashPatternNode { token };
676
+ node = hash.static_cast_as<Node>();
662
677
  if (current_token().type() == Token::Type::RCurlyBrace) {
663
678
  advance();
664
679
  node = hash.static_cast_as<Node>();
665
680
  break;
666
681
  }
667
- expect(Token::Type::SymbolKey, "hash pattern symbol key");
668
- hash->add_node(parse_symbol(locals));
669
- 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
+
670
699
  while (current_token().is_comma()) {
671
700
  advance(); // ,
672
- expect(Token::Type::Symbol, "hash pattern symbol");
673
- hash->add_node(parse_symbol(locals));
674
- hash->add_node(parse_case_in_pattern(locals));
701
+ add_pair();
675
702
  }
703
+
676
704
  expect(Token::Type::RCurlyBrace, "hash pattern closing brace");
677
705
  advance();
678
- node = hash.static_cast_as<Node>();
679
706
  break;
680
707
  }
681
- case Token::Type::Bignum:
682
- case Token::Type::Fixnum:
683
- case Token::Type::Float:
684
- 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);
685
719
  break;
686
720
  case Token::Type::Star: {
687
- auto splat_token = current_token();
688
- advance();
689
- SharedPtr<ArrayPatternNode> array = new ArrayPatternNode { token };
721
+ advance(); // *
690
722
  switch (current_token().type()) {
691
723
  case Token::Type::BareName:
692
724
  case Token::Type::Constant: {
693
- auto name = new IdentifierNode { current_token(), true };
694
- name->prepend_to_name('*');
695
- 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 };
696
728
  advance();
697
729
  break;
698
730
  }
699
731
  default:
700
- array->add_node(new SplatNode { splat_token });
732
+ node = new SplatNode { current_token() };
701
733
  }
702
- 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>();
703
741
  break;
704
742
  }
705
743
  case Token::Type::String:
@@ -711,6 +749,11 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
711
749
  default:
712
750
  throw_unexpected("case in pattern");
713
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
+
714
757
  token = current_token();
715
758
  if (token.is_hash_rocket()) {
716
759
  advance();
@@ -723,26 +766,67 @@ SharedPtr<Node> Parser::parse_case_in_pattern(LocalsHashmap &locals) {
723
766
  return node;
724
767
  }
725
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
+
726
818
  SharedPtr<Node> Parser::parse_case_in_patterns(LocalsHashmap &locals) {
727
819
  Vector<SharedPtr<Node>> patterns;
728
- 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);
729
824
  while (current_token().type() == Token::Type::Pipe) {
730
825
  advance();
731
- patterns.push(parse_case_in_pattern(locals));
732
- }
733
- while (current_token().is_comma()) {
734
- advance();
735
- auto last_pattern = patterns.last();
736
- auto next_pattern = parse_case_in_pattern(locals);
737
- if (last_pattern->type() == Node::Type::ArrayPattern) {
738
- last_pattern.static_cast_as<ArrayPatternNode>()->add_node(next_pattern);
739
- } else {
740
- patterns.pop();
741
- auto array_pattern = new ArrayPatternNode { last_pattern->token() };
742
- array_pattern->add_node(last_pattern);
743
- array_pattern->add_node(next_pattern);
744
- patterns.push(array_pattern);
745
- }
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);
746
830
  }
747
831
  assert(patterns.size() > 0);
748
832
  if (patterns.size() == 1) {
@@ -849,13 +933,12 @@ SharedPtr<Node> Parser::parse_assignment_identifier(bool allow_splat, LocalsHash
849
933
  case Token::Type::Star: {
850
934
  if (!allow_splat)
851
935
  expect(Token::Type::BareName, "assignment identifier");
852
- auto splat_token = current_token();
853
936
  advance();
854
937
  if (current_token().is_assignable()) {
855
938
  auto id = parse_assignment_identifier(false, locals);
856
- node = new SplatNode { splat_token, id };
939
+ node = new SplatNode { token, id };
857
940
  } else {
858
- node = new SplatNode { splat_token };
941
+ node = new SplatNode { token };
859
942
  }
860
943
  break;
861
944
  }
@@ -1216,17 +1299,17 @@ SharedPtr<Node> Parser::parse_hash(LocalsHashmap &locals) {
1216
1299
  advance();
1217
1300
  SharedPtr<Node> hash;
1218
1301
  if (current_token().type() == Token::Type::RCurlyBrace)
1219
- hash = new HashNode { token };
1302
+ hash = new HashNode { token, false };
1220
1303
  else
1221
- hash = parse_hash_inner(locals, Precedence::HASH, Token::Type::RCurlyBrace);
1304
+ hash = parse_hash_inner(locals, Precedence::HASH, Token::Type::RCurlyBrace, false);
1222
1305
  expect(Token::Type::RCurlyBrace, "hash closing curly brace");
1223
1306
  advance();
1224
1307
  return hash;
1225
1308
  }
1226
1309
 
1227
- SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, SharedPtr<Node> first_key) {
1310
+ SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, bool bare, SharedPtr<Node> first_key) {
1228
1311
  auto token = current_token();
1229
- SharedPtr<HashNode> hash = new HashNode { token };
1312
+ SharedPtr<HashNode> hash = new HashNode { token, bare };
1230
1313
  if (!first_key)
1231
1314
  first_key = parse_expression(precedence, locals);
1232
1315
  hash->add_node(first_key);
@@ -1557,7 +1640,7 @@ SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
1557
1640
  }
1558
1641
 
1559
1642
  SharedPtr<Node> Parser::parse_keyword_splat_wrapped_in_hash(LocalsHashmap &locals) {
1560
- SharedPtr<HashNode> hash = new HashNode { current_token() };
1643
+ SharedPtr<HashNode> hash = new HashNode { current_token(), true };
1561
1644
  hash->add_node(parse_keyword_splat(locals));
1562
1645
  return hash.static_cast_as<Node>();
1563
1646
  }
@@ -1886,11 +1969,14 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1886
1969
  advance();
1887
1970
  auto precedence = get_precedence(token);
1888
1971
  auto receiver = parse_expression(precedence, locals);
1972
+
1889
1973
  if ((token.type() == Token::Type::Minus || token.type() == Token::Type::Plus) && receiver->is_numeric()) {
1890
1974
  switch (receiver->type()) {
1891
1975
  case Node::Type::Bignum: {
1892
1976
  if (token.type() == Token::Type::Minus) {
1893
1977
  auto num = receiver.static_cast_as<BignumNode>();
1978
+ if (num->negative())
1979
+ break;
1894
1980
  num->negate();
1895
1981
  return num.static_cast_as<Node>();
1896
1982
  }
@@ -1899,6 +1985,8 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1899
1985
  case Node::Type::Fixnum: {
1900
1986
  if (token.type() == Token::Type::Minus) {
1901
1987
  auto num = receiver.static_cast_as<FixnumNode>();
1988
+ if (num->negative())
1989
+ break;
1902
1990
  num->negate();
1903
1991
  return num.static_cast_as<Node>();
1904
1992
  }
@@ -1907,6 +1995,8 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
1907
1995
  case Node::Type::Float: {
1908
1996
  if (token.type() == Token::Type::Minus) {
1909
1997
  auto num = receiver.static_cast_as<FloatNode>();
1998
+ if (num->negative())
1999
+ break;
1910
2000
  num->negate();
1911
2001
  return num.static_cast_as<Node>();
1912
2002
  }
@@ -2100,6 +2190,12 @@ SharedPtr<Node> Parser::parse_iter_expression(SharedPtr<Node> left, LocalsHashma
2100
2190
  bool curly_brace = current_token().type() == Token::Type::LCurlyBrace;
2101
2191
  bool has_args = false;
2102
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
+
2103
2199
  if (left->type() == Node::Type::StabbyProc) {
2104
2200
  advance(); // { or do
2105
2201
  auto stabby_proc_node = left.static_cast_as<StabbyProcNode>();
@@ -2193,6 +2289,15 @@ SharedPtr<NodeWithArgs> Parser::to_node_with_args(SharedPtr<Node> node) {
2193
2289
  };
2194
2290
  return call_node;
2195
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
+ }
2196
2301
  case Node::Type::Call:
2197
2302
  case Node::Type::SafeCall:
2198
2303
  case Node::Type::Super:
@@ -2205,7 +2310,7 @@ SharedPtr<NodeWithArgs> Parser::to_node_with_args(SharedPtr<Node> node) {
2205
2310
  }
2206
2311
 
2207
2312
  void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bare, Token::Type closing_token_type) {
2208
- if (node.can_accept_a_block())
2313
+ if (bare && node.can_accept_a_block())
2209
2314
  m_call_depth.last()++;
2210
2315
  auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
2211
2316
  if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
@@ -2228,16 +2333,17 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
2228
2333
  }
2229
2334
  }
2230
2335
  }
2231
- if (node.can_accept_a_block())
2336
+ if (bare && node.can_accept_a_block())
2232
2337
  m_call_depth.last()--;
2233
2338
  }
2234
2339
 
2235
- SharedPtr<Node> Parser::parse_call_hash_args(LocalsHashmap &locals, bool bare, Token::Type closing_token_type, SharedPtr<Node> first_arg) {
2340
+ SharedPtr<Node> Parser::parse_call_hash_args(LocalsHashmap &locals, bool bare_call, Token::Type closing_token_type, SharedPtr<Node> first_arg) {
2341
+ bool bare_hash = true; // we got here via foo(1, a: 'b') so it's always a "bare" hash
2236
2342
  SharedPtr<Node> hash;
2237
- if (bare)
2238
- hash = parse_hash_inner(locals, Precedence::BARE_CALL_ARG, closing_token_type, first_arg);
2343
+ if (bare_call)
2344
+ hash = parse_hash_inner(locals, Precedence::BARE_CALL_ARG, closing_token_type, bare_hash, first_arg);
2239
2345
  else
2240
- hash = parse_hash_inner(locals, Precedence::CALL_ARG, closing_token_type, first_arg);
2346
+ hash = parse_hash_inner(locals, Precedence::CALL_ARG, closing_token_type, bare_hash, first_arg);
2241
2347
  if (current_token().type() == Token::Type::StarStar)
2242
2348
  hash.static_cast_as<HashNode>()->add_node(parse_keyword_splat(locals));
2243
2349
  return hash;
@@ -2472,15 +2578,19 @@ SharedPtr<Node> Parser::parse_proc_call_expression(SharedPtr<Node> left, LocalsH
2472
2578
 
2473
2579
  SharedPtr<Node> Parser::parse_range_expression(SharedPtr<Node> left, LocalsHashmap &locals) {
2474
2580
  auto token = current_token();
2475
- advance();
2581
+ advance(); // .. or ...
2582
+ skip_newlines();
2476
2583
  SharedPtr<Node> right;
2477
- try {
2584
+ if (current_token().can_be_range_arg_token()) {
2478
2585
  right = parse_expression(Precedence::RANGE, locals);
2479
- } catch (SyntaxError &e) {
2480
- // NOTE: I'm not sure if this is the "right" way to handle an endless range,
2481
- // but it seems to be effective for the tests I threw at it. ¯\_(ツ)_/¯
2586
+ } else {
2587
+ // endless range
2482
2588
  right = new NilNode { token };
2589
+ // HACK: insert a newline here so subsequent expressions parse ok
2590
+ if (!current_token().can_follow_collapsible_newline())
2591
+ m_tokens->insert(m_index, Token { Token::Type::Newline, current_token().file(), current_token().line(), current_token().column(), current_token().whitespace_precedes() });
2483
2592
  }
2593
+
2484
2594
  return new RangeNode { token, left, right, token.type() == Token::Type::DotDotDot };
2485
2595
  }
2486
2596
 
@@ -2767,16 +2877,19 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
2767
2877
  case Type::LeftShift:
2768
2878
  case Type::LessThan:
2769
2879
  case Type::LessThanOrEqual:
2770
- case Type::Minus:
2771
2880
  case Type::NotEqual:
2772
2881
  case Type::Percent:
2773
2882
  case Type::Pipe:
2774
- case Type::Plus:
2775
2883
  case Type::RightShift:
2776
2884
  case Type::Slash:
2777
2885
  case Type::Star:
2778
2886
  case Type::StarStar:
2779
2887
  return &Parser::parse_infix_expression;
2888
+ case Type::Minus:
2889
+ case Type::Plus:
2890
+ if (!token.whitespace_precedes() || peek_token().whitespace_precedes() || !left->is_callable())
2891
+ return &Parser::parse_infix_expression;
2892
+ break;
2780
2893
  case Type::DoKeyword:
2781
2894
  case Type::LCurlyBrace:
2782
2895
  return &Parser::parse_iter_expression;
@@ -2843,20 +2956,22 @@ bool Parser::is_first_arg_of_call_without_parens(SharedPtr<Node> left, Token &to
2843
2956
  return left->is_callable() && token.can_be_first_arg_of_implicit_call();
2844
2957
  }
2845
2958
 
2959
+ Token &Parser::previous_token() const {
2960
+ if (m_index > 0)
2961
+ return (*m_tokens)[m_index - 1];
2962
+ return Token::invalid();
2963
+ }
2964
+
2846
2965
  Token &Parser::current_token() const {
2847
- if (m_index < m_tokens->size()) {
2966
+ if (m_index < m_tokens->size())
2848
2967
  return m_tokens->at(m_index);
2849
- } else {
2850
- return Token::invalid();
2851
- }
2968
+ return Token::invalid();
2852
2969
  }
2853
2970
 
2854
2971
  Token &Parser::peek_token() const {
2855
- if (m_index + 1 < m_tokens->size()) {
2972
+ if (m_index + 1 < m_tokens->size())
2856
2973
  return (*m_tokens)[m_index + 1];
2857
- } else {
2858
- return Token::invalid();
2859
- }
2974
+ return Token::invalid();
2860
2975
  }
2861
2976
 
2862
2977
  void Parser::next_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: 1.1.1
4
+ version: 2.0.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-04 00:00:00.000000000 Z
11
+ date: 2022-06-24 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.
@@ -79,6 +79,7 @@ files:
79
79
  - include/natalie_parser/node/interpolated_symbol_node.hpp
80
80
  - include/natalie_parser/node/iter_node.hpp
81
81
  - include/natalie_parser/node/keyword_arg_node.hpp
82
+ - include/natalie_parser/node/keyword_rest_pattern_node.hpp
82
83
  - include/natalie_parser/node/keyword_splat_node.hpp
83
84
  - include/natalie_parser/node/logical_and_node.hpp
84
85
  - include/natalie_parser/node/logical_or_node.hpp