natalie_parser 2.1.0 → 2.3.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 +42 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -1
- data/README.md +1 -0
- data/Rakefile +2 -1
- data/ext/natalie_parser/mri_creator.hpp +5 -5
- data/ext/natalie_parser/natalie_parser.cpp +5 -4
- data/include/natalie_parser/lexer.hpp +17 -4
- data/include/natalie_parser/node/begin_rescue_node.hpp +4 -5
- data/include/natalie_parser/node/for_node.hpp +1 -16
- data/include/natalie_parser/parser.hpp +15 -5
- data/include/natalie_parser/token.hpp +21 -0
- data/lib/natalie_parser/version.rb +1 -1
- data/src/lexer/interpolated_string_lexer.cpp +1 -1
- data/src/lexer/regexp_lexer.cpp +1 -1
- data/src/lexer/word_array_lexer.cpp +1 -1
- data/src/lexer.cpp +189 -248
- data/src/node/begin_rescue_node.cpp +4 -3
- data/src/node/for_node.cpp +13 -0
- data/src/parser.cpp +143 -106
- metadata +3 -2
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
|
-
|
285
|
-
auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)"
|
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
|
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
|
-
|
293
|
-
case Token::Type::
|
294
|
-
|
295
|
-
return new SymbolNode { token,
|
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
|
-
|
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
|
}
|
@@ -431,16 +434,8 @@ void Parser::parse_rest_of_begin(BeginNode &begin_node, LocalsHashmap &locals) {
|
|
431
434
|
}
|
432
435
|
if (current_token().is_hash_rocket()) {
|
433
436
|
advance();
|
434
|
-
|
435
|
-
|
436
|
-
case Token::Type::BareName:
|
437
|
-
name = new IdentifierNode { current_token(), current_token().literal_string() };
|
438
|
-
advance();
|
439
|
-
break;
|
440
|
-
default:
|
441
|
-
throw_unexpected("exception name");
|
442
|
-
}
|
443
|
-
name->add_to_locals(locals);
|
437
|
+
auto name = parse_expression(Precedence::ASSIGNMENT_LHS, locals);
|
438
|
+
add_assignment_locals(name, locals);
|
444
439
|
rescue_node->set_exception_name(name);
|
445
440
|
}
|
446
441
|
next_expression();
|
@@ -501,7 +496,7 @@ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
|
|
501
496
|
SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
|
502
497
|
auto token = current_token();
|
503
498
|
advance();
|
504
|
-
auto value = parse_expression(Precedence::
|
499
|
+
auto value = parse_expression(Precedence::LOWEST, locals);
|
505
500
|
return new BlockPassNode { token, value };
|
506
501
|
}
|
507
502
|
|
@@ -865,15 +860,19 @@ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
|
|
865
860
|
}
|
866
861
|
|
867
862
|
SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
863
|
+
auto name_token = current_token();
|
864
|
+
auto exp = parse_expression(Precedence::LESS_GREATER, locals);
|
865
|
+
switch (exp->type()) {
|
866
|
+
case Node::Type::Colon2:
|
867
|
+
case Node::Type::Colon3:
|
868
|
+
return exp;
|
869
|
+
case Node::Type::Identifier:
|
870
|
+
if (name_token.type() == Token::Type::Constant)
|
871
|
+
return exp;
|
872
|
+
[[fallthrough]];
|
873
|
+
default:
|
875
874
|
throw SyntaxError { "class/module name must be CONSTANT" };
|
876
|
-
|
875
|
+
}
|
877
876
|
}
|
878
877
|
|
879
878
|
SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
|
@@ -903,7 +902,7 @@ SharedPtr<Node> Parser::parse_multiple_assignment_expression(SharedPtr<Node> lef
|
|
903
902
|
list->add_node(left);
|
904
903
|
while (current_token().is_comma()) {
|
905
904
|
advance();
|
906
|
-
if (current_token().is_rparen() || current_token().is_equal()) {
|
905
|
+
if (current_token().is_rparen() || current_token().is_equal() || current_token().type() == Token::Type::InKeyword) {
|
907
906
|
// trailing comma with no additional identifier
|
908
907
|
break;
|
909
908
|
}
|
@@ -991,21 +990,24 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
991
990
|
auto token = current_token();
|
992
991
|
switch (token.type()) {
|
993
992
|
case Token::Type::BareName:
|
994
|
-
if (peek_token().
|
993
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
995
994
|
self_node = parse_identifier(locals);
|
996
995
|
advance(); // dot
|
997
996
|
}
|
998
997
|
name = parse_method_name(locals);
|
999
998
|
break;
|
1000
999
|
case Token::Type::Constant:
|
1001
|
-
if (peek_token().
|
1000
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
1002
1001
|
self_node = parse_constant(locals);
|
1003
1002
|
advance(); // dot
|
1004
1003
|
}
|
1005
1004
|
name = parse_method_name(locals);
|
1006
1005
|
break;
|
1006
|
+
case Token::Type::OperatorName:
|
1007
|
+
name = parse_method_name(locals);
|
1008
|
+
break;
|
1007
1009
|
case Token::Type::SelfKeyword:
|
1008
|
-
if (peek_token().
|
1010
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
1009
1011
|
self_node = new SelfNode { current_token() };
|
1010
1012
|
advance(); // self
|
1011
1013
|
advance(); // dot
|
@@ -1023,10 +1025,6 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1023
1025
|
}
|
1024
1026
|
}
|
1025
1027
|
}
|
1026
|
-
if (current_token().is_equal() && !current_token().whitespace_precedes()) {
|
1027
|
-
advance();
|
1028
|
-
name->append_char('=');
|
1029
|
-
}
|
1030
1028
|
auto args = Vector<SharedPtr<Node>> {};
|
1031
1029
|
if (current_token().is_lparen()) {
|
1032
1030
|
advance();
|
@@ -1037,7 +1035,7 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1037
1035
|
expect(Token::Type::RParen, "args closing paren");
|
1038
1036
|
advance();
|
1039
1037
|
}
|
1040
|
-
} else if (current_token().
|
1038
|
+
} else if (current_token().can_be_first_arg_of_def()) {
|
1041
1039
|
parse_def_args(args, our_locals);
|
1042
1040
|
}
|
1043
1041
|
SharedPtr<BlockNode> body;
|
@@ -1085,7 +1083,21 @@ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals
|
|
1085
1083
|
}
|
1086
1084
|
}
|
1087
1085
|
|
1088
|
-
|
1086
|
+
SharedPtr<Node> Parser::parse_arg_default_value(LocalsHashmap &locals, IterAllow iter_allow) {
|
1087
|
+
auto token = current_token();
|
1088
|
+
if (token.is_bare_name() && peek_token().is_equal()) {
|
1089
|
+
SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
|
1090
|
+
advance();
|
1091
|
+
advance(); // =
|
1092
|
+
arg->add_to_locals(locals);
|
1093
|
+
arg->set_value(parse_arg_default_value(locals, iter_allow));
|
1094
|
+
return arg.static_cast_as<Node>();
|
1095
|
+
} else {
|
1096
|
+
return parse_expression(Precedence::DEF_ARG, locals, iter_allow);
|
1097
|
+
}
|
1098
|
+
}
|
1099
|
+
|
1100
|
+
void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context, IterAllow iter_allow) {
|
1089
1101
|
auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
|
1090
1102
|
auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
|
1091
1103
|
|
@@ -1105,7 +1117,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
|
|
1105
1117
|
if (args_have_any_splat())
|
1106
1118
|
throw_error(token, "default value after splat");
|
1107
1119
|
advance(); // =
|
1108
|
-
arg->set_value(
|
1120
|
+
arg->set_value(parse_arg_default_value(locals, iter_allow));
|
1109
1121
|
}
|
1110
1122
|
args.push(arg.static_cast_as<Node>());
|
1111
1123
|
return;
|
@@ -1176,8 +1188,12 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
|
|
1176
1188
|
case Token::Type::RParen:
|
1177
1189
|
case Token::Type::Semicolon:
|
1178
1190
|
break;
|
1191
|
+
case Token::Type::LCurlyBrace:
|
1192
|
+
if (iter_allow < IterAllow::CURLY_ONLY)
|
1193
|
+
break;
|
1194
|
+
[[fallthrough]];
|
1179
1195
|
default:
|
1180
|
-
arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
|
1196
|
+
arg->set_value(parse_expression(Precedence::DEF_ARG, locals, iter_allow));
|
1181
1197
|
}
|
1182
1198
|
arg->add_to_locals(locals);
|
1183
1199
|
args.push(arg.static_cast_as<Node>());
|
@@ -1263,16 +1279,28 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
|
|
1263
1279
|
return new StringNode { token, token.file() };
|
1264
1280
|
}
|
1265
1281
|
|
1282
|
+
SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
|
1283
|
+
auto token = current_token();
|
1284
|
+
advance();
|
1285
|
+
return new FixnumNode { token, static_cast<long long>(token.line() + 1) };
|
1286
|
+
}
|
1287
|
+
|
1266
1288
|
SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
|
1267
1289
|
auto token = current_token();
|
1268
1290
|
advance();
|
1269
|
-
auto vars = parse_assignment_identifier(
|
1270
|
-
if (current_token().type() ==
|
1291
|
+
auto vars = parse_assignment_identifier(true, locals);
|
1292
|
+
if (current_token().is_comma() || vars->type() == Node::Type::Splat) {
|
1271
1293
|
vars = parse_multiple_assignment_expression(vars, locals);
|
1272
1294
|
}
|
1295
|
+
add_assignment_locals(vars, locals);
|
1273
1296
|
expect(Token::Type::InKeyword, "for in");
|
1274
1297
|
advance();
|
1275
|
-
|
1298
|
+
m_call_depth.last()++;
|
1299
|
+
auto expr = parse_expression(Precedence::BARE_CALL_ARG, locals, IterAllow::CURLY_ONLY);
|
1300
|
+
m_call_depth.last()--;
|
1301
|
+
if (current_token().type() == Token::Type::DoKeyword) {
|
1302
|
+
advance();
|
1303
|
+
}
|
1276
1304
|
auto body = parse_body(locals, Precedence::LOWEST);
|
1277
1305
|
expect(Token::Type::EndKeyword, "for end");
|
1278
1306
|
advance();
|
@@ -1690,13 +1718,15 @@ SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
|
|
1690
1718
|
switch (token.type()) {
|
1691
1719
|
case Token::Type::BareName:
|
1692
1720
|
case Token::Type::Constant:
|
1721
|
+
case Token::Type::OperatorName:
|
1693
1722
|
name = current_token().literal_string();
|
1694
1723
|
break;
|
1695
1724
|
default:
|
1696
|
-
if (token.is_operator() || token.is_keyword())
|
1725
|
+
if (token.is_operator() || token.is_keyword()) {
|
1697
1726
|
name = new String(current_token().type_value());
|
1698
|
-
else
|
1727
|
+
} else {
|
1699
1728
|
throw_unexpected("method name");
|
1729
|
+
}
|
1700
1730
|
}
|
1701
1731
|
advance();
|
1702
1732
|
return name;
|
@@ -1766,15 +1796,15 @@ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
|
|
1766
1796
|
return new NthRefNode { token, token.get_fixnum() };
|
1767
1797
|
}
|
1768
1798
|
|
1769
|
-
void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
|
1799
|
+
void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, IterAllow iter_allow) {
|
1770
1800
|
if (current_token().is_semicolon()) {
|
1771
1801
|
parse_shadow_variables_in_args(args, locals);
|
1772
1802
|
return;
|
1773
1803
|
}
|
1774
|
-
parse_def_single_arg(args, locals, ArgsContext::Proc);
|
1804
|
+
parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
|
1775
1805
|
while (current_token().is_comma()) {
|
1776
1806
|
advance();
|
1777
|
-
parse_def_single_arg(args, locals, ArgsContext::Proc);
|
1807
|
+
parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
|
1778
1808
|
}
|
1779
1809
|
if (current_token().is_semicolon()) {
|
1780
1810
|
parse_shadow_variables_in_args(args, locals);
|
@@ -1887,13 +1917,13 @@ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
|
|
1887
1917
|
if (current_token().is_rparen()) {
|
1888
1918
|
advance(); // )
|
1889
1919
|
} else {
|
1890
|
-
parse_proc_args(args, locals);
|
1920
|
+
parse_proc_args(args, locals, IterAllow::CURLY_AND_BLOCK);
|
1891
1921
|
expect(Token::Type::RParen, "proc args closing paren");
|
1892
1922
|
advance(); // )
|
1893
1923
|
}
|
1894
|
-
} else if (current_token().
|
1924
|
+
} else if (current_token().can_be_first_arg_of_def()) {
|
1895
1925
|
has_args = true;
|
1896
|
-
parse_proc_args(args, locals);
|
1926
|
+
parse_proc_args(args, locals, IterAllow::NONE);
|
1897
1927
|
}
|
1898
1928
|
if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
|
1899
1929
|
throw_unexpected("block");
|
@@ -2065,36 +2095,23 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
|
|
2065
2095
|
SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
|
2066
2096
|
auto undef_token = current_token();
|
2067
2097
|
advance();
|
2068
|
-
auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
|
2069
|
-
switch (token.type()) {
|
2070
|
-
case Token::Type::BareName:
|
2071
|
-
case Token::Type::Constant:
|
2072
|
-
advance();
|
2073
|
-
return new SymbolNode { token, token.literal_string() };
|
2074
|
-
case Token::Type::Symbol:
|
2075
|
-
return parse_symbol(locals);
|
2076
|
-
case Token::Type::InterpolatedSymbolBegin: {
|
2077
|
-
return parse_interpolated_symbol(locals);
|
2078
|
-
}
|
2079
|
-
default:
|
2080
|
-
throw_unexpected("method name for undef");
|
2081
|
-
}
|
2082
|
-
};
|
2083
2098
|
SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
|
2084
|
-
auto
|
2085
|
-
undef_node->add_arg(
|
2099
|
+
auto arg = parse_alias_arg(locals, "method name for undef");
|
2100
|
+
undef_node->add_arg(arg.static_cast_as<Node>());
|
2086
2101
|
if (current_token().is_comma()) {
|
2087
2102
|
SharedPtr<BlockNode> block = new BlockNode { undef_token };
|
2088
2103
|
block->add_node(undef_node.static_cast_as<Node>());
|
2089
2104
|
while (current_token().is_comma()) {
|
2090
2105
|
advance();
|
2091
|
-
token = current_token();
|
2092
2106
|
SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
|
2093
|
-
|
2107
|
+
auto arg = parse_alias_arg(locals, "method name for undef");
|
2108
|
+
undef_node->add_arg(arg.static_cast_as<Node>());
|
2094
2109
|
block->add_node(undef_node.static_cast_as<Node>());
|
2095
2110
|
}
|
2111
|
+
reinsert_collapsed_newline();
|
2096
2112
|
return block.static_cast_as<Node>();
|
2097
2113
|
}
|
2114
|
+
reinsert_collapsed_newline();
|
2098
2115
|
return undef_node.static_cast_as<Node>();
|
2099
2116
|
};
|
2100
2117
|
|
@@ -2155,32 +2172,34 @@ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, Locals
|
|
2155
2172
|
if (left->type() == Node::Type::Splat) {
|
2156
2173
|
return parse_multiple_assignment_expression(left, locals);
|
2157
2174
|
}
|
2175
|
+
add_assignment_locals(left, locals);
|
2176
|
+
advance();
|
2177
|
+
bool to_array = left->type() == Node::Type::MultipleAssignment;
|
2178
|
+
auto value = parse_assignment_expression_value(to_array, locals, allow_multiple);
|
2179
|
+
return new AssignmentNode { token, left, value };
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
void Parser::add_assignment_locals(SharedPtr<Node> left, LocalsHashmap &locals) {
|
2158
2183
|
switch (left->type()) {
|
2159
2184
|
case Node::Type::Identifier: {
|
2160
2185
|
auto left_identifier = left.static_cast_as<IdentifierNode>();
|
2161
2186
|
left_identifier->add_to_locals(locals);
|
2162
|
-
|
2163
|
-
auto value = parse_assignment_expression_value(false, locals, allow_multiple);
|
2164
|
-
return new AssignmentNode { token, left, value };
|
2187
|
+
break;
|
2165
2188
|
}
|
2166
2189
|
case Node::Type::Call:
|
2167
2190
|
case Node::Type::Colon2:
|
2168
2191
|
case Node::Type::Colon3:
|
2169
2192
|
case Node::Type::SafeCall: {
|
2170
|
-
|
2171
|
-
auto value = parse_assignment_expression_value(false, locals, allow_multiple);
|
2172
|
-
return new AssignmentNode { token, left, value };
|
2193
|
+
break;
|
2173
2194
|
}
|
2174
2195
|
case Node::Type::MultipleAssignment: {
|
2175
2196
|
left.static_cast_as<MultipleAssignmentNode>()->add_locals(locals);
|
2176
|
-
|
2177
|
-
auto value = parse_assignment_expression_value(true, locals, allow_multiple);
|
2178
|
-
return new AssignmentNode { token, left, value };
|
2197
|
+
break;
|
2179
2198
|
}
|
2180
2199
|
default:
|
2181
|
-
throw_unexpected(left->token(), "
|
2200
|
+
throw_unexpected(left->token(), "assignment identifier");
|
2182
2201
|
}
|
2183
|
-
}
|
2202
|
+
}
|
2184
2203
|
|
2185
2204
|
SharedPtr<Node> Parser::parse_assignment_expression_value(bool to_array, LocalsHashmap &locals, bool allow_multiple) {
|
2186
2205
|
auto token = current_token();
|
@@ -2416,6 +2435,7 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
|
|
2416
2435
|
SharedPtr<Node> node;
|
2417
2436
|
switch (name_token.type()) {
|
2418
2437
|
case Token::Type::BareName:
|
2438
|
+
case Token::Type::OperatorName:
|
2419
2439
|
advance();
|
2420
2440
|
node = new CallNode { name_token, left, name_token.literal_string() };
|
2421
2441
|
break;
|
@@ -2434,7 +2454,12 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
|
|
2434
2454
|
break;
|
2435
2455
|
}
|
2436
2456
|
default:
|
2437
|
-
|
2457
|
+
if (name_token.is_operator() || name_token.is_keyword()) {
|
2458
|
+
advance();
|
2459
|
+
node = new CallNode { name_token, left, new String(name_token.type_value()) };
|
2460
|
+
} else {
|
2461
|
+
throw_unexpected(name_token, ":: identifier name");
|
2462
|
+
}
|
2438
2463
|
}
|
2439
2464
|
return node;
|
2440
2465
|
}
|
@@ -2677,6 +2702,7 @@ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsH
|
|
2677
2702
|
break;
|
2678
2703
|
case Token::Type::BareName:
|
2679
2704
|
case Token::Type::Constant:
|
2705
|
+
case Token::Type::OperatorName:
|
2680
2706
|
name = name_token.literal_string();
|
2681
2707
|
advance();
|
2682
2708
|
break;
|
@@ -2703,6 +2729,7 @@ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashma
|
|
2703
2729
|
switch (name_token.type()) {
|
2704
2730
|
case Token::Type::BareName:
|
2705
2731
|
case Token::Type::Constant:
|
2732
|
+
case Token::Type::OperatorName:
|
2706
2733
|
name = name_token.literal_string();
|
2707
2734
|
advance();
|
2708
2735
|
break;
|
@@ -2745,7 +2772,11 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
|
|
2745
2772
|
if (condition->type() == Node::Type::Regexp) {
|
2746
2773
|
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2747
2774
|
}
|
2748
|
-
|
2775
|
+
if (current_token().type() == Token::Type::ThenKeyword) {
|
2776
|
+
advance(); // then
|
2777
|
+
} else {
|
2778
|
+
next_expression();
|
2779
|
+
}
|
2749
2780
|
SharedPtr<Node> false_expr = parse_if_body(locals);
|
2750
2781
|
SharedPtr<Node> true_expr;
|
2751
2782
|
if (current_token().is_else_keyword()) {
|
@@ -2762,11 +2793,15 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
|
|
2762
2793
|
SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
|
2763
2794
|
auto token = current_token();
|
2764
2795
|
advance();
|
2765
|
-
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
2796
|
+
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
|
2766
2797
|
if (condition->type() == Node::Type::Regexp) {
|
2767
2798
|
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2768
2799
|
}
|
2769
|
-
|
2800
|
+
if (current_token().type() == Token::Type::DoKeyword) {
|
2801
|
+
advance();
|
2802
|
+
} else {
|
2803
|
+
next_expression();
|
2804
|
+
}
|
2770
2805
|
SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
|
2771
2806
|
expect(Token::Type::EndKeyword, "while end");
|
2772
2807
|
advance();
|
@@ -2824,6 +2859,8 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
|
|
2824
2859
|
return &Parser::parse_group;
|
2825
2860
|
case Type::LCurlyBrace:
|
2826
2861
|
return &Parser::parse_hash;
|
2862
|
+
case Type::LINEKeyword:
|
2863
|
+
return &Parser::parse_line_constant;
|
2827
2864
|
case Type::BareName:
|
2828
2865
|
case Type::ClassVariable:
|
2829
2866
|
case Type::Constant:
|
@@ -2907,7 +2944,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
|
|
2907
2944
|
using Type = Token::Type;
|
2908
2945
|
switch (token.type()) {
|
2909
2946
|
case Type::Equal:
|
2910
|
-
if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
|
2947
|
+
if (precedence == Precedence::ARRAY || precedence == Precedence::HASH || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
|
2911
2948
|
return &Parser::parse_assignment_expression_without_multiple_values;
|
2912
2949
|
else
|
2913
2950
|
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.
|
4
|
+
version: 2.3.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-
|
11
|
+
date: 2022-12-31 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.
|
@@ -151,6 +151,7 @@ files:
|
|
151
151
|
- src/node/begin_node.cpp
|
152
152
|
- src/node/begin_rescue_node.cpp
|
153
153
|
- src/node/class_node.cpp
|
154
|
+
- src/node/for_node.cpp
|
154
155
|
- src/node/interpolated_regexp_node.cpp
|
155
156
|
- src/node/interpolated_shell_node.cpp
|
156
157
|
- src/node/interpolated_string_node.cpp
|