natalie_parser 2.0.0 → 2.2.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 +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
|