natalie_parser 2.0.0 → 2.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 +43 -0
- data/ext/natalie_parser/natalie_parser.cpp +1 -0
- data/include/natalie_parser/lexer.hpp +17 -4
- data/include/natalie_parser/node/for_node.hpp +50 -0
- data/include/natalie_parser/node/match_node.hpp +6 -0
- data/include/natalie_parser/node/node.hpp +1 -0
- data/include/natalie_parser/node.hpp +1 -0
- data/include/natalie_parser/parser.hpp +16 -5
- data/include/natalie_parser/token.hpp +38 -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 +170 -248
- data/src/node/match_node.cpp +5 -0
- data/src/parser.cpp +195 -105
- 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
|
}
|
@@ -354,11 +357,17 @@ SharedPtr<Node> Parser::parse_array(LocalsHashmap &locals) {
|
|
354
357
|
return {};
|
355
358
|
};
|
356
359
|
auto ret = add_node();
|
357
|
-
if (ret)
|
360
|
+
if (ret) {
|
361
|
+
m_call_depth.pop();
|
362
|
+
return ret;
|
363
|
+
}
|
358
364
|
while (current_token().is_comma()) {
|
359
365
|
advance();
|
360
366
|
ret = add_node();
|
361
|
-
if (ret)
|
367
|
+
if (ret) {
|
368
|
+
m_call_depth.pop();
|
369
|
+
return ret;
|
370
|
+
}
|
362
371
|
}
|
363
372
|
expect(Token::Type::RBracket, "array closing bracket");
|
364
373
|
advance(); // ]
|
@@ -495,7 +504,7 @@ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
|
|
495
504
|
SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
|
496
505
|
auto token = current_token();
|
497
506
|
advance();
|
498
|
-
auto value = parse_expression(Precedence::
|
507
|
+
auto value = parse_expression(Precedence::LOWEST, locals);
|
499
508
|
return new BlockPassNode { token, value };
|
500
509
|
}
|
501
510
|
|
@@ -859,15 +868,19 @@ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
|
|
859
868
|
}
|
860
869
|
|
861
870
|
SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
871
|
+
auto name_token = current_token();
|
872
|
+
auto exp = parse_expression(Precedence::LESS_GREATER, locals);
|
873
|
+
switch (exp->type()) {
|
874
|
+
case Node::Type::Colon2:
|
875
|
+
case Node::Type::Colon3:
|
876
|
+
return exp;
|
877
|
+
case Node::Type::Identifier:
|
878
|
+
if (name_token.type() == Token::Type::Constant)
|
879
|
+
return exp;
|
880
|
+
[[fallthrough]];
|
881
|
+
default:
|
869
882
|
throw SyntaxError { "class/module name must be CONSTANT" };
|
870
|
-
|
883
|
+
}
|
871
884
|
}
|
872
885
|
|
873
886
|
SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
|
@@ -985,21 +998,24 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
985
998
|
auto token = current_token();
|
986
999
|
switch (token.type()) {
|
987
1000
|
case Token::Type::BareName:
|
988
|
-
if (peek_token().
|
1001
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
989
1002
|
self_node = parse_identifier(locals);
|
990
1003
|
advance(); // dot
|
991
1004
|
}
|
992
1005
|
name = parse_method_name(locals);
|
993
1006
|
break;
|
994
1007
|
case Token::Type::Constant:
|
995
|
-
if (peek_token().
|
1008
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
996
1009
|
self_node = parse_constant(locals);
|
997
1010
|
advance(); // dot
|
998
1011
|
}
|
999
1012
|
name = parse_method_name(locals);
|
1000
1013
|
break;
|
1014
|
+
case Token::Type::OperatorName:
|
1015
|
+
name = parse_method_name(locals);
|
1016
|
+
break;
|
1001
1017
|
case Token::Type::SelfKeyword:
|
1002
|
-
if (peek_token().
|
1018
|
+
if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
|
1003
1019
|
self_node = new SelfNode { current_token() };
|
1004
1020
|
advance(); // self
|
1005
1021
|
advance(); // dot
|
@@ -1017,10 +1033,6 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1017
1033
|
}
|
1018
1034
|
}
|
1019
1035
|
}
|
1020
|
-
if (current_token().is_equal() && !current_token().whitespace_precedes()) {
|
1021
|
-
advance();
|
1022
|
-
name->append_char('=');
|
1023
|
-
}
|
1024
1036
|
auto args = Vector<SharedPtr<Node>> {};
|
1025
1037
|
if (current_token().is_lparen()) {
|
1026
1038
|
advance();
|
@@ -1031,7 +1043,7 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1031
1043
|
expect(Token::Type::RParen, "args closing paren");
|
1032
1044
|
advance();
|
1033
1045
|
}
|
1034
|
-
} else if (current_token().
|
1046
|
+
} else if (current_token().can_be_first_arg_of_def()) {
|
1035
1047
|
parse_def_args(args, our_locals);
|
1036
1048
|
}
|
1037
1049
|
SharedPtr<BlockNode> body;
|
@@ -1058,7 +1070,16 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
|
|
1058
1070
|
SharedPtr<Node> Parser::parse_defined(LocalsHashmap &locals) {
|
1059
1071
|
auto token = current_token();
|
1060
1072
|
advance();
|
1061
|
-
|
1073
|
+
bool bare = true;
|
1074
|
+
if (current_token().is_lparen()) {
|
1075
|
+
advance();
|
1076
|
+
bare = false;
|
1077
|
+
}
|
1078
|
+
auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
1079
|
+
if (!bare) {
|
1080
|
+
expect(Token::Type::RParen, "defined? closing paren");
|
1081
|
+
advance();
|
1082
|
+
}
|
1062
1083
|
return new DefinedNode { token, arg };
|
1063
1084
|
}
|
1064
1085
|
|
@@ -1070,7 +1091,21 @@ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals
|
|
1070
1091
|
}
|
1071
1092
|
}
|
1072
1093
|
|
1073
|
-
|
1094
|
+
SharedPtr<Node> Parser::parse_arg_default_value(LocalsHashmap &locals, IterAllow iter_allow) {
|
1095
|
+
auto token = current_token();
|
1096
|
+
if (token.is_bare_name() && peek_token().is_equal()) {
|
1097
|
+
SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
|
1098
|
+
advance();
|
1099
|
+
advance(); // =
|
1100
|
+
arg->add_to_locals(locals);
|
1101
|
+
arg->set_value(parse_arg_default_value(locals, iter_allow));
|
1102
|
+
return arg.static_cast_as<Node>();
|
1103
|
+
} else {
|
1104
|
+
return parse_expression(Precedence::DEF_ARG, locals, iter_allow);
|
1105
|
+
}
|
1106
|
+
}
|
1107
|
+
|
1108
|
+
void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context, IterAllow iter_allow) {
|
1074
1109
|
auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
|
1075
1110
|
auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
|
1076
1111
|
|
@@ -1090,7 +1125,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
|
|
1090
1125
|
if (args_have_any_splat())
|
1091
1126
|
throw_error(token, "default value after splat");
|
1092
1127
|
advance(); // =
|
1093
|
-
arg->set_value(
|
1128
|
+
arg->set_value(parse_arg_default_value(locals, iter_allow));
|
1094
1129
|
}
|
1095
1130
|
args.push(arg.static_cast_as<Node>());
|
1096
1131
|
return;
|
@@ -1161,8 +1196,12 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
|
|
1161
1196
|
case Token::Type::RParen:
|
1162
1197
|
case Token::Type::Semicolon:
|
1163
1198
|
break;
|
1199
|
+
case Token::Type::LCurlyBrace:
|
1200
|
+
if (iter_allow < IterAllow::CURLY_ONLY)
|
1201
|
+
break;
|
1202
|
+
[[fallthrough]];
|
1164
1203
|
default:
|
1165
|
-
arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
|
1204
|
+
arg->set_value(parse_expression(Precedence::DEF_ARG, locals, iter_allow));
|
1166
1205
|
}
|
1167
1206
|
arg->add_to_locals(locals);
|
1168
1207
|
args.push(arg.static_cast_as<Node>());
|
@@ -1248,6 +1287,31 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
|
|
1248
1287
|
return new StringNode { token, token.file() };
|
1249
1288
|
}
|
1250
1289
|
|
1290
|
+
SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
|
1291
|
+
auto token = current_token();
|
1292
|
+
advance();
|
1293
|
+
return new FixnumNode { token, static_cast<long long>(token.line() + 1) };
|
1294
|
+
}
|
1295
|
+
|
1296
|
+
SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
|
1297
|
+
auto token = current_token();
|
1298
|
+
advance();
|
1299
|
+
auto vars = parse_assignment_identifier(false, locals);
|
1300
|
+
if (current_token().type() == Token::Type::Comma) {
|
1301
|
+
vars = parse_multiple_assignment_expression(vars, locals);
|
1302
|
+
}
|
1303
|
+
expect(Token::Type::InKeyword, "for in");
|
1304
|
+
advance();
|
1305
|
+
auto expr = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
|
1306
|
+
if (current_token().type() == Token::Type::DoKeyword) {
|
1307
|
+
advance();
|
1308
|
+
}
|
1309
|
+
auto body = parse_body(locals, Precedence::LOWEST);
|
1310
|
+
expect(Token::Type::EndKeyword, "for end");
|
1311
|
+
advance();
|
1312
|
+
return new ForNode { token, expr, vars, body };
|
1313
|
+
}
|
1314
|
+
|
1251
1315
|
SharedPtr<Node> Parser::parse_forward_args(LocalsHashmap &locals) {
|
1252
1316
|
auto token = current_token();
|
1253
1317
|
if (!locals.get("..."))
|
@@ -1310,28 +1374,35 @@ SharedPtr<Node> Parser::parse_hash(LocalsHashmap &locals) {
|
|
1310
1374
|
SharedPtr<Node> Parser::parse_hash_inner(LocalsHashmap &locals, Precedence precedence, Token::Type closing_token_type, bool bare, SharedPtr<Node> first_key) {
|
1311
1375
|
auto token = current_token();
|
1312
1376
|
SharedPtr<HashNode> hash = new HashNode { token, bare };
|
1377
|
+
|
1378
|
+
auto add_value = [&](SharedPtr<Node> key) {
|
1379
|
+
if (key->is_symbol_key()) {
|
1380
|
+
hash->add_node(parse_expression(precedence, locals));
|
1381
|
+
} else if (key->type() == Node::Type::KeywordSplat) {
|
1382
|
+
// kwsplat already added
|
1383
|
+
} else {
|
1384
|
+
expect(Token::Type::HashRocket, "hash rocket");
|
1385
|
+
advance(); // =>
|
1386
|
+
hash->add_node(parse_expression(precedence, locals));
|
1387
|
+
}
|
1388
|
+
};
|
1389
|
+
|
1313
1390
|
if (!first_key)
|
1314
1391
|
first_key = parse_expression(precedence, locals);
|
1315
1392
|
hash->add_node(first_key);
|
1316
|
-
|
1317
|
-
|
1318
|
-
advance();
|
1319
|
-
}
|
1320
|
-
hash->add_node(parse_expression(precedence, locals));
|
1393
|
+
add_value(first_key);
|
1394
|
+
|
1321
1395
|
while (current_token().is_comma()) {
|
1322
1396
|
advance();
|
1323
1397
|
if (current_token().type() == closing_token_type)
|
1324
1398
|
break;
|
1325
|
-
if (current_token().type() == Token::Type::
|
1399
|
+
if (current_token().type() == Token::Type::Ampersand) // &block
|
1326
1400
|
break;
|
1327
1401
|
auto key = parse_expression(precedence, locals);
|
1328
1402
|
hash->add_node(key);
|
1329
|
-
|
1330
|
-
expect(Token::Type::HashRocket, "hash rocket");
|
1331
|
-
advance();
|
1332
|
-
}
|
1333
|
-
hash->add_node(parse_expression(precedence, locals));
|
1403
|
+
add_value(key);
|
1334
1404
|
}
|
1405
|
+
|
1335
1406
|
return hash.static_cast_as<Node>();
|
1336
1407
|
}
|
1337
1408
|
|
@@ -1345,9 +1416,16 @@ SharedPtr<Node> Parser::parse_identifier(LocalsHashmap &locals) {
|
|
1345
1416
|
};
|
1346
1417
|
|
1347
1418
|
SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
|
1419
|
+
return parse_if_branch(locals, true);
|
1420
|
+
}
|
1421
|
+
|
1422
|
+
SharedPtr<Node> Parser::parse_if_branch(LocalsHashmap &locals, bool parse_match_condition) {
|
1348
1423
|
auto token = current_token();
|
1349
1424
|
advance();
|
1350
1425
|
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
1426
|
+
if (parse_match_condition && condition->type() == Node::Type::Regexp) {
|
1427
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
1428
|
+
}
|
1351
1429
|
if (current_token().type() == Token::Type::ThenKeyword) {
|
1352
1430
|
advance(); // then
|
1353
1431
|
} else {
|
@@ -1356,7 +1434,7 @@ SharedPtr<Node> Parser::parse_if(LocalsHashmap &locals) {
|
|
1356
1434
|
SharedPtr<Node> true_expr = parse_if_body(locals);
|
1357
1435
|
SharedPtr<Node> false_expr;
|
1358
1436
|
if (current_token().is_elsif_keyword()) {
|
1359
|
-
false_expr =
|
1437
|
+
false_expr = parse_if_branch(locals, false);
|
1360
1438
|
return new IfNode { current_token(), condition, true_expr, false_expr };
|
1361
1439
|
} else {
|
1362
1440
|
if (current_token().is_else_keyword()) {
|
@@ -1639,25 +1717,21 @@ SharedPtr<Node> Parser::parse_keyword_splat(LocalsHashmap &locals) {
|
|
1639
1717
|
return new KeywordSplatNode { token, parse_expression(Precedence::SPLAT, locals) };
|
1640
1718
|
}
|
1641
1719
|
|
1642
|
-
SharedPtr<Node> Parser::parse_keyword_splat_wrapped_in_hash(LocalsHashmap &locals) {
|
1643
|
-
SharedPtr<HashNode> hash = new HashNode { current_token(), true };
|
1644
|
-
hash->add_node(parse_keyword_splat(locals));
|
1645
|
-
return hash.static_cast_as<Node>();
|
1646
|
-
}
|
1647
|
-
|
1648
1720
|
SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
|
1649
1721
|
SharedPtr<String> name = new String("");
|
1650
1722
|
auto token = current_token();
|
1651
1723
|
switch (token.type()) {
|
1652
1724
|
case Token::Type::BareName:
|
1653
1725
|
case Token::Type::Constant:
|
1726
|
+
case Token::Type::OperatorName:
|
1654
1727
|
name = current_token().literal_string();
|
1655
1728
|
break;
|
1656
1729
|
default:
|
1657
|
-
if (token.is_operator() || token.is_keyword())
|
1730
|
+
if (token.is_operator() || token.is_keyword()) {
|
1658
1731
|
name = new String(current_token().type_value());
|
1659
|
-
else
|
1732
|
+
} else {
|
1660
1733
|
throw_unexpected("method name");
|
1734
|
+
}
|
1661
1735
|
}
|
1662
1736
|
advance();
|
1663
1737
|
return name;
|
@@ -1727,15 +1801,15 @@ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
|
|
1727
1801
|
return new NthRefNode { token, token.get_fixnum() };
|
1728
1802
|
}
|
1729
1803
|
|
1730
|
-
void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
|
1804
|
+
void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, IterAllow iter_allow) {
|
1731
1805
|
if (current_token().is_semicolon()) {
|
1732
1806
|
parse_shadow_variables_in_args(args, locals);
|
1733
1807
|
return;
|
1734
1808
|
}
|
1735
|
-
parse_def_single_arg(args, locals, ArgsContext::Proc);
|
1809
|
+
parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
|
1736
1810
|
while (current_token().is_comma()) {
|
1737
1811
|
advance();
|
1738
|
-
parse_def_single_arg(args, locals, ArgsContext::Proc);
|
1812
|
+
parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
|
1739
1813
|
}
|
1740
1814
|
if (current_token().is_semicolon()) {
|
1741
1815
|
parse_shadow_variables_in_args(args, locals);
|
@@ -1848,13 +1922,13 @@ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
|
|
1848
1922
|
if (current_token().is_rparen()) {
|
1849
1923
|
advance(); // )
|
1850
1924
|
} else {
|
1851
|
-
parse_proc_args(args, locals);
|
1925
|
+
parse_proc_args(args, locals, IterAllow::CURLY_AND_BLOCK);
|
1852
1926
|
expect(Token::Type::RParen, "proc args closing paren");
|
1853
1927
|
advance(); // )
|
1854
1928
|
}
|
1855
|
-
} else if (current_token().
|
1929
|
+
} else if (current_token().can_be_first_arg_of_def()) {
|
1856
1930
|
has_args = true;
|
1857
|
-
parse_proc_args(args, locals);
|
1931
|
+
parse_proc_args(args, locals, IterAllow::NONE);
|
1858
1932
|
}
|
1859
1933
|
if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
|
1860
1934
|
throw_unexpected("block");
|
@@ -2026,36 +2100,23 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
|
|
2026
2100
|
SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
|
2027
2101
|
auto undef_token = current_token();
|
2028
2102
|
advance();
|
2029
|
-
auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
|
2030
|
-
switch (token.type()) {
|
2031
|
-
case Token::Type::BareName:
|
2032
|
-
case Token::Type::Constant:
|
2033
|
-
advance();
|
2034
|
-
return new SymbolNode { token, token.literal_string() };
|
2035
|
-
case Token::Type::Symbol:
|
2036
|
-
return parse_symbol(locals);
|
2037
|
-
case Token::Type::InterpolatedSymbolBegin: {
|
2038
|
-
return parse_interpolated_symbol(locals);
|
2039
|
-
}
|
2040
|
-
default:
|
2041
|
-
throw_unexpected("method name for undef");
|
2042
|
-
}
|
2043
|
-
};
|
2044
2103
|
SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
|
2045
|
-
auto
|
2046
|
-
undef_node->add_arg(
|
2104
|
+
auto arg = parse_alias_arg(locals, "method name for undef");
|
2105
|
+
undef_node->add_arg(arg.static_cast_as<Node>());
|
2047
2106
|
if (current_token().is_comma()) {
|
2048
2107
|
SharedPtr<BlockNode> block = new BlockNode { undef_token };
|
2049
2108
|
block->add_node(undef_node.static_cast_as<Node>());
|
2050
2109
|
while (current_token().is_comma()) {
|
2051
2110
|
advance();
|
2052
|
-
token = current_token();
|
2053
2111
|
SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
|
2054
|
-
|
2112
|
+
auto arg = parse_alias_arg(locals, "method name for undef");
|
2113
|
+
undef_node->add_arg(arg.static_cast_as<Node>());
|
2055
2114
|
block->add_node(undef_node.static_cast_as<Node>());
|
2056
2115
|
}
|
2116
|
+
reinsert_collapsed_newline();
|
2057
2117
|
return block.static_cast_as<Node>();
|
2058
2118
|
}
|
2119
|
+
reinsert_collapsed_newline();
|
2059
2120
|
return undef_node.static_cast_as<Node>();
|
2060
2121
|
};
|
2061
2122
|
|
@@ -2313,7 +2374,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2313
2374
|
if (bare && node.can_accept_a_block())
|
2314
2375
|
m_call_depth.last()++;
|
2315
2376
|
auto arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
2316
|
-
if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
|
2377
|
+
if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
|
2317
2378
|
node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
|
2318
2379
|
} else {
|
2319
2380
|
node.add_arg(arg);
|
@@ -2325,7 +2386,7 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2325
2386
|
break;
|
2326
2387
|
}
|
2327
2388
|
arg = parse_expression(bare ? Precedence::BARE_CALL_ARG : Precedence::CALL_ARG, locals);
|
2328
|
-
if (current_token().is_hash_rocket() || arg->is_symbol_key()) {
|
2389
|
+
if (current_token().is_hash_rocket() || arg->is_symbol_key() || arg->type() == Node::Type::KeywordSplat) {
|
2329
2390
|
node.add_arg(parse_call_hash_args(locals, bare, closing_token_type, arg));
|
2330
2391
|
break;
|
2331
2392
|
} else {
|
@@ -2333,6 +2394,9 @@ void Parser::parse_call_args(NodeWithArgs &node, LocalsHashmap &locals, bool bar
|
|
2333
2394
|
}
|
2334
2395
|
}
|
2335
2396
|
}
|
2397
|
+
if (current_token().type() == Token::Type::Ampersand) {
|
2398
|
+
node.add_arg(parse_block_pass(locals));
|
2399
|
+
}
|
2336
2400
|
if (bare && node.can_accept_a_block())
|
2337
2401
|
m_call_depth.last()--;
|
2338
2402
|
}
|
@@ -2374,6 +2438,7 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
|
|
2374
2438
|
SharedPtr<Node> node;
|
2375
2439
|
switch (name_token.type()) {
|
2376
2440
|
case Token::Type::BareName:
|
2441
|
+
case Token::Type::OperatorName:
|
2377
2442
|
advance();
|
2378
2443
|
node = new CallNode { name_token, left, name_token.literal_string() };
|
2379
2444
|
break;
|
@@ -2392,7 +2457,12 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
|
|
2392
2457
|
break;
|
2393
2458
|
}
|
2394
2459
|
default:
|
2395
|
-
|
2460
|
+
if (name_token.is_operator() || name_token.is_keyword()) {
|
2461
|
+
advance();
|
2462
|
+
node = new CallNode { name_token, left, new String(name_token.type_value()) };
|
2463
|
+
} else {
|
2464
|
+
throw_unexpected(name_token, ":: identifier name");
|
2465
|
+
}
|
2396
2466
|
}
|
2397
2467
|
return node;
|
2398
2468
|
}
|
@@ -2635,6 +2705,7 @@ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsH
|
|
2635
2705
|
break;
|
2636
2706
|
case Token::Type::BareName:
|
2637
2707
|
case Token::Type::Constant:
|
2708
|
+
case Token::Type::OperatorName:
|
2638
2709
|
name = name_token.literal_string();
|
2639
2710
|
advance();
|
2640
2711
|
break;
|
@@ -2661,6 +2732,7 @@ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashma
|
|
2661
2732
|
switch (name_token.type()) {
|
2662
2733
|
case Token::Type::BareName:
|
2663
2734
|
case Token::Type::Constant:
|
2735
|
+
case Token::Type::OperatorName:
|
2664
2736
|
name = name_token.literal_string();
|
2665
2737
|
advance();
|
2666
2738
|
break;
|
@@ -2700,7 +2772,14 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
|
|
2700
2772
|
auto token = current_token();
|
2701
2773
|
advance();
|
2702
2774
|
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
2703
|
-
|
2775
|
+
if (condition->type() == Node::Type::Regexp) {
|
2776
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2777
|
+
}
|
2778
|
+
if (current_token().type() == Token::Type::ThenKeyword) {
|
2779
|
+
advance(); // then
|
2780
|
+
} else {
|
2781
|
+
next_expression();
|
2782
|
+
}
|
2704
2783
|
SharedPtr<Node> false_expr = parse_if_body(locals);
|
2705
2784
|
SharedPtr<Node> true_expr;
|
2706
2785
|
if (current_token().is_else_keyword()) {
|
@@ -2717,8 +2796,15 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
|
|
2717
2796
|
SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
|
2718
2797
|
auto token = current_token();
|
2719
2798
|
advance();
|
2720
|
-
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
|
2721
|
-
|
2799
|
+
SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
|
2800
|
+
if (condition->type() == Node::Type::Regexp) {
|
2801
|
+
condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
|
2802
|
+
}
|
2803
|
+
if (current_token().type() == Token::Type::DoKeyword) {
|
2804
|
+
advance();
|
2805
|
+
} else {
|
2806
|
+
next_expression();
|
2807
|
+
}
|
2722
2808
|
SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
|
2723
2809
|
expect(Token::Type::EndKeyword, "while end");
|
2724
2810
|
advance();
|
@@ -2770,10 +2856,14 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
|
|
2770
2856
|
return &Parser::parse_end_block;
|
2771
2857
|
case Type::FILEKeyword:
|
2772
2858
|
return &Parser::parse_file_constant;
|
2859
|
+
case Type::ForKeyword:
|
2860
|
+
return &Parser::parse_for;
|
2773
2861
|
case Type::LParen:
|
2774
2862
|
return &Parser::parse_group;
|
2775
2863
|
case Type::LCurlyBrace:
|
2776
2864
|
return &Parser::parse_hash;
|
2865
|
+
case Type::LINEKeyword:
|
2866
|
+
return &Parser::parse_line_constant;
|
2777
2867
|
case Type::BareName:
|
2778
2868
|
case Type::ClassVariable:
|
2779
2869
|
case Type::Constant:
|
@@ -2791,7 +2881,7 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
|
|
2791
2881
|
case Type::InterpolatedSymbolBegin:
|
2792
2882
|
return &Parser::parse_interpolated_symbol;
|
2793
2883
|
case Type::StarStar:
|
2794
|
-
return &Parser::
|
2884
|
+
return &Parser::parse_keyword_splat;
|
2795
2885
|
case Type::Bignum:
|
2796
2886
|
case Type::Fixnum:
|
2797
2887
|
case Type::Float:
|
@@ -2857,7 +2947,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
|
|
2857
2947
|
using Type = Token::Type;
|
2858
2948
|
switch (token.type()) {
|
2859
2949
|
case Type::Equal:
|
2860
|
-
if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
|
2950
|
+
if (precedence == Precedence::ARRAY || precedence == Precedence::HASH || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
|
2861
2951
|
return &Parser::parse_assignment_expression_without_multiple_values;
|
2862
2952
|
else
|
2863
2953
|
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.2.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-10-25 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.
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- include/natalie_parser/node/false_node.hpp
|
66
66
|
- include/natalie_parser/node/fixnum_node.hpp
|
67
67
|
- include/natalie_parser/node/float_node.hpp
|
68
|
+
- include/natalie_parser/node/for_node.hpp
|
68
69
|
- include/natalie_parser/node/forward_args_node.hpp
|
69
70
|
- include/natalie_parser/node/hash_node.hpp
|
70
71
|
- include/natalie_parser/node/hash_pattern_node.hpp
|