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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +11 -4
- data/Rakefile +12 -5
- data/ext/natalie_parser/mri_creator.hpp +25 -7
- data/include/natalie_parser/creator/debug_creator.hpp +13 -3
- data/include/natalie_parser/creator.hpp +4 -2
- data/include/natalie_parser/node/array_pattern_node.hpp +20 -2
- data/include/natalie_parser/node/bignum_node.hpp +5 -1
- data/include/natalie_parser/node/case_in_node.hpp +5 -2
- data/include/natalie_parser/node/complex_node.hpp +49 -0
- data/include/natalie_parser/node/fixnum_node.hpp +5 -1
- data/include/natalie_parser/node/float_node.hpp +4 -0
- data/include/natalie_parser/node/forward_args_node.hpp +26 -0
- data/include/natalie_parser/node/hash_pattern_node.hpp +1 -0
- data/include/natalie_parser/node/infix_op_node.hpp +1 -1
- data/include/natalie_parser/node/iter_node.hpp +1 -1
- data/include/natalie_parser/node/keyword_rest_pattern_node.hpp +43 -0
- data/include/natalie_parser/node/node.hpp +7 -1
- data/include/natalie_parser/node/nth_ref_node.hpp +1 -1
- data/include/natalie_parser/node/rational_node.hpp +45 -0
- data/include/natalie_parser/node.hpp +4 -0
- data/include/natalie_parser/parser.hpp +14 -1
- data/include/natalie_parser/token.hpp +62 -13
- data/lib/natalie_parser/version.rb +1 -1
- data/src/lexer/interpolated_string_lexer.cpp +9 -9
- data/src/lexer/regexp_lexer.cpp +7 -7
- data/src/lexer/word_array_lexer.cpp +13 -13
- data/src/lexer.cpp +210 -181
- data/src/node/begin_rescue_node.cpp +1 -1
- data/src/node/interpolated_regexp_node.cpp +1 -1
- data/src/node/node.cpp +7 -0
- data/src/node/node_with_args.cpp +1 -0
- data/src/parser.cpp +261 -91
- 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
|
-
|
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::
|
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().
|
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
|
-
|
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
|
-
|
667
|
-
|
668
|
-
|
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
|
-
|
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::
|
681
|
-
|
682
|
-
|
683
|
-
|
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
|
-
|
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
|
-
|
693
|
-
|
694
|
-
|
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
|
-
|
732
|
+
node = new SplatNode { current_token() };
|
700
733
|
}
|
701
|
-
|
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
|
-
|
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
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
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 {
|
939
|
+
node = new SplatNode { token, id };
|
856
940
|
} else {
|
857
|
-
node = new SplatNode {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1604
|
+
node = new BignumNode { token, token.literal_string() };
|
1605
|
+
break;
|
1498
1606
|
case Token::Type::Fixnum:
|
1499
1607
|
advance();
|
1500
|
-
|
1608
|
+
node = new FixnumNode { token, token.get_fixnum() };
|
1609
|
+
break;
|
1501
1610
|
case Token::Type::Float:
|
1502
1611
|
advance();
|
1503
|
-
|
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
|
-
|
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::
|
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
|
-
|
2583
|
+
if (current_token().can_be_range_arg_token()) {
|
2434
2584
|
right = parse_expression(Precedence::RANGE, locals);
|
2435
|
-
}
|
2436
|
-
//
|
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::
|
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
|
-
|
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
|
-
|
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
|
-
|
2885
|
-
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
2891
|
-
|
2892
|
-
|
2893
|
-
|
2894
|
-
|
2895
|
-
|
2896
|
-
|
2897
|
-
|
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:
|