natalie_parser 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|