prism 0.25.0 → 0.26.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 +20 -1
- data/Makefile +17 -14
- data/config.yml +12 -1
- data/docs/configuration.md +1 -0
- data/docs/releasing.md +7 -9
- data/ext/prism/extconf.rb +8 -3
- data/ext/prism/extension.c +28 -2
- data/ext/prism/extension.h +1 -1
- data/include/prism/diagnostic.h +12 -1
- data/include/prism/parser.h +5 -1
- data/include/prism/version.h +2 -2
- data/lib/prism/desugar_compiler.rb +4 -2
- data/lib/prism/ffi.rb +10 -0
- data/lib/prism/node.rb +16 -0
- data/lib/prism/parse_result.rb +5 -0
- data/lib/prism/reflection.rb +421 -0
- data/lib/prism/serialize.rb +13 -2
- data/lib/prism/translation/parser/compiler.rb +48 -11
- data/lib/prism.rb +1 -16
- data/prism.gemspec +7 -3
- data/rbi/prism/node.rbi +453 -0
- data/rbi/prism/parse_result.rbi +3 -0
- data/rbi/prism/reflection.rbi +64 -0
- data/rbi/prism/translation/parser.rbi +11 -0
- data/rbi/prism/translation/parser33.rbi +6 -0
- data/rbi/prism/translation/parser34.rbi +6 -0
- data/rbi/prism.rbi +33 -33
- data/sig/prism/node.rbs +10 -1
- data/sig/prism/parse_result.rbs +1 -0
- data/sig/prism/reflection.rbs +56 -0
- data/sig/prism.rbs +2 -2
- data/src/diagnostic.c +30 -8
- data/src/options.c +30 -19
- data/src/prism.c +404 -67
- data/src/token_type.c +3 -3
- data/src/util/pm_integer.c +14 -8
- metadata +8 -4
- data/include/prism/util/pm_state_stack.h +0 -42
- data/src/util/pm_state_stack.c +0 -25
data/src/prism.c
CHANGED
@@ -1005,7 +1005,7 @@ pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
|
|
1005
1005
|
* written but not read in certain contexts.
|
1006
1006
|
*/
|
1007
1007
|
static void
|
1008
|
-
pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool
|
1008
|
+
pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
|
1009
1009
|
pm_constant_id_list_init_capacity(list, locals->size);
|
1010
1010
|
|
1011
1011
|
// If we're still below the threshold for switching to a hash, then we only
|
@@ -1013,6 +1013,10 @@ pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals,
|
|
1013
1013
|
// stored in a list.
|
1014
1014
|
uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
|
1015
1015
|
|
1016
|
+
// We will only warn for unused variables if we're not at the top level, or
|
1017
|
+
// if we're parsing a file outside of eval or -e.
|
1018
|
+
bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
|
1019
|
+
|
1016
1020
|
for (uint32_t index = 0; index < capacity; index++) {
|
1017
1021
|
pm_local_t *local = &locals->locals[index];
|
1018
1022
|
|
@@ -1178,6 +1182,156 @@ pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
|
|
1178
1182
|
}
|
1179
1183
|
}
|
1180
1184
|
|
1185
|
+
/**
|
1186
|
+
* Warn if the given node is a "void" statement.
|
1187
|
+
*/
|
1188
|
+
static void
|
1189
|
+
pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
|
1190
|
+
const char *type = NULL;
|
1191
|
+
int length = 0;
|
1192
|
+
|
1193
|
+
switch (PM_NODE_TYPE(node)) {
|
1194
|
+
case PM_BACK_REFERENCE_READ_NODE:
|
1195
|
+
case PM_CLASS_VARIABLE_READ_NODE:
|
1196
|
+
case PM_GLOBAL_VARIABLE_READ_NODE:
|
1197
|
+
case PM_INSTANCE_VARIABLE_READ_NODE:
|
1198
|
+
case PM_LOCAL_VARIABLE_READ_NODE:
|
1199
|
+
case PM_NUMBERED_REFERENCE_READ_NODE:
|
1200
|
+
type = "a variable";
|
1201
|
+
length = 10;
|
1202
|
+
break;
|
1203
|
+
case PM_CALL_NODE: {
|
1204
|
+
const pm_call_node_t *cast = (const pm_call_node_t *) node;
|
1205
|
+
if (cast->call_operator_loc.start != NULL || cast->message_loc.start == NULL) break;
|
1206
|
+
|
1207
|
+
const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
|
1208
|
+
switch (message->length) {
|
1209
|
+
case 1:
|
1210
|
+
switch (message->start[0]) {
|
1211
|
+
case '+':
|
1212
|
+
case '-':
|
1213
|
+
case '*':
|
1214
|
+
case '/':
|
1215
|
+
case '%':
|
1216
|
+
case '|':
|
1217
|
+
case '^':
|
1218
|
+
case '&':
|
1219
|
+
case '>':
|
1220
|
+
case '<':
|
1221
|
+
type = (const char *) message->start;
|
1222
|
+
length = 1;
|
1223
|
+
break;
|
1224
|
+
}
|
1225
|
+
break;
|
1226
|
+
case 2:
|
1227
|
+
switch (message->start[1]) {
|
1228
|
+
case '=':
|
1229
|
+
if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
|
1230
|
+
type = (const char *) message->start;
|
1231
|
+
length = 2;
|
1232
|
+
}
|
1233
|
+
break;
|
1234
|
+
case '@':
|
1235
|
+
if (message->start[0] == '+' || message->start[0] == '-') {
|
1236
|
+
type = (const char *) message->start;
|
1237
|
+
length = 2;
|
1238
|
+
}
|
1239
|
+
break;
|
1240
|
+
case '*':
|
1241
|
+
if (message->start[0] == '*') {
|
1242
|
+
type = (const char *) message->start;
|
1243
|
+
length = 2;
|
1244
|
+
}
|
1245
|
+
break;
|
1246
|
+
}
|
1247
|
+
break;
|
1248
|
+
case 3:
|
1249
|
+
if (memcmp(message->start, "<=>", 3) == 0) {
|
1250
|
+
type = "<=>";
|
1251
|
+
length = 3;
|
1252
|
+
}
|
1253
|
+
break;
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
break;
|
1257
|
+
}
|
1258
|
+
case PM_CONSTANT_PATH_NODE:
|
1259
|
+
type = "::";
|
1260
|
+
length = 2;
|
1261
|
+
break;
|
1262
|
+
case PM_CONSTANT_READ_NODE:
|
1263
|
+
type = "a constant";
|
1264
|
+
length = 10;
|
1265
|
+
break;
|
1266
|
+
case PM_DEFINED_NODE:
|
1267
|
+
type = "defined?";
|
1268
|
+
length = 8;
|
1269
|
+
break;
|
1270
|
+
case PM_FALSE_NODE:
|
1271
|
+
type = "false";
|
1272
|
+
length = 5;
|
1273
|
+
break;
|
1274
|
+
case PM_FLOAT_NODE:
|
1275
|
+
case PM_IMAGINARY_NODE:
|
1276
|
+
case PM_INTEGER_NODE:
|
1277
|
+
case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
|
1278
|
+
case PM_INTERPOLATED_STRING_NODE:
|
1279
|
+
case PM_RATIONAL_NODE:
|
1280
|
+
case PM_REGULAR_EXPRESSION_NODE:
|
1281
|
+
case PM_SOURCE_ENCODING_NODE:
|
1282
|
+
case PM_SOURCE_FILE_NODE:
|
1283
|
+
case PM_SOURCE_LINE_NODE:
|
1284
|
+
case PM_STRING_NODE:
|
1285
|
+
case PM_SYMBOL_NODE:
|
1286
|
+
type = "a literal";
|
1287
|
+
length = 9;
|
1288
|
+
break;
|
1289
|
+
case PM_NIL_NODE:
|
1290
|
+
type = "nil";
|
1291
|
+
length = 3;
|
1292
|
+
break;
|
1293
|
+
case PM_RANGE_NODE: {
|
1294
|
+
const pm_range_node_t *cast = (const pm_range_node_t *) node;
|
1295
|
+
|
1296
|
+
if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
|
1297
|
+
type = "...";
|
1298
|
+
length = 3;
|
1299
|
+
} else {
|
1300
|
+
type = "..";
|
1301
|
+
length = 2;
|
1302
|
+
}
|
1303
|
+
|
1304
|
+
break;
|
1305
|
+
}
|
1306
|
+
case PM_SELF_NODE:
|
1307
|
+
type = "self";
|
1308
|
+
length = 4;
|
1309
|
+
break;
|
1310
|
+
case PM_TRUE_NODE:
|
1311
|
+
type = "true";
|
1312
|
+
length = 4;
|
1313
|
+
break;
|
1314
|
+
default:
|
1315
|
+
break;
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
if (type != NULL) {
|
1319
|
+
PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
|
1320
|
+
}
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
/**
|
1324
|
+
* Warn if any of the statements that are not the last statement in the list are
|
1325
|
+
* a "void" statement.
|
1326
|
+
*/
|
1327
|
+
static void
|
1328
|
+
pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node) {
|
1329
|
+
assert(node->body.size > 0);
|
1330
|
+
for (size_t index = 0; index < node->body.size - 1; index++) {
|
1331
|
+
pm_void_statement_check(parser, node->body.nodes[index]);
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
|
1181
1335
|
/**
|
1182
1336
|
* When we're handling the predicate of a conditional, we need to know our
|
1183
1337
|
* context in order to determine the kind of warning we should deliver to the
|
@@ -1741,7 +1895,7 @@ static pm_statements_node_t *
|
|
1741
1895
|
pm_statements_node_create(pm_parser_t *parser);
|
1742
1896
|
|
1743
1897
|
static void
|
1744
|
-
pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement);
|
1898
|
+
pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement);
|
1745
1899
|
|
1746
1900
|
static size_t
|
1747
1901
|
pm_statements_node_body_length(pm_statements_node_t *node);
|
@@ -2620,6 +2774,7 @@ pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *me
|
|
2620
2774
|
if (arguments->closing_loc.start != NULL) {
|
2621
2775
|
node->base.location.end = arguments->closing_loc.end;
|
2622
2776
|
} else {
|
2777
|
+
assert(receiver != NULL);
|
2623
2778
|
node->base.location.end = receiver->location.end;
|
2624
2779
|
}
|
2625
2780
|
|
@@ -4400,7 +4555,7 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t
|
|
4400
4555
|
pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t);
|
4401
4556
|
|
4402
4557
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
4403
|
-
pm_statements_node_body_append(statements, statement);
|
4558
|
+
pm_statements_node_body_append(parser, statements, statement);
|
4404
4559
|
|
4405
4560
|
*node = (pm_if_node_t) {
|
4406
4561
|
{
|
@@ -4431,10 +4586,10 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to
|
|
4431
4586
|
pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
|
4432
4587
|
|
4433
4588
|
pm_statements_node_t *if_statements = pm_statements_node_create(parser);
|
4434
|
-
pm_statements_node_body_append(if_statements, true_expression);
|
4589
|
+
pm_statements_node_body_append(parser, if_statements, true_expression);
|
4435
4590
|
|
4436
4591
|
pm_statements_node_t *else_statements = pm_statements_node_create(parser);
|
4437
|
-
pm_statements_node_body_append(else_statements, false_expression);
|
4592
|
+
pm_statements_node_body_append(parser, else_statements, false_expression);
|
4438
4593
|
|
4439
4594
|
pm_token_t end_keyword = not_provided(parser);
|
4440
4595
|
pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword);
|
@@ -6455,8 +6610,25 @@ pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement)
|
|
6455
6610
|
* Append a new node to the given StatementsNode node's body.
|
6456
6611
|
*/
|
6457
6612
|
static void
|
6458
|
-
pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement) {
|
6613
|
+
pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement) {
|
6459
6614
|
pm_statements_node_body_update(node, statement);
|
6615
|
+
|
6616
|
+
if (node->body.size > 0) {
|
6617
|
+
const pm_node_t *previous = node->body.nodes[node->body.size - 1];
|
6618
|
+
|
6619
|
+
switch (PM_NODE_TYPE(previous)) {
|
6620
|
+
case PM_BREAK_NODE:
|
6621
|
+
case PM_NEXT_NODE:
|
6622
|
+
case PM_REDO_NODE:
|
6623
|
+
case PM_RETRY_NODE:
|
6624
|
+
case PM_RETURN_NODE:
|
6625
|
+
pm_parser_warn_node(parser, previous, PM_WARN_UNREACHABLE_STATEMENT);
|
6626
|
+
break;
|
6627
|
+
default:
|
6628
|
+
break;
|
6629
|
+
}
|
6630
|
+
}
|
6631
|
+
|
6460
6632
|
pm_node_list_append(&node->body, statement);
|
6461
6633
|
pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
|
6462
6634
|
}
|
@@ -7019,7 +7191,7 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const
|
|
7019
7191
|
pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t);
|
7020
7192
|
|
7021
7193
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
7022
|
-
pm_statements_node_body_append(statements, statement);
|
7194
|
+
pm_statements_node_body_append(parser, statements, statement);
|
7023
7195
|
|
7024
7196
|
*node = (pm_unless_node_t) {
|
7025
7197
|
{
|
@@ -7460,6 +7632,30 @@ pm_parser_scope_pop(pm_parser_t *parser) {
|
|
7460
7632
|
/* Stack helpers */
|
7461
7633
|
/******************************************************************************/
|
7462
7634
|
|
7635
|
+
/**
|
7636
|
+
* Pushes a value onto the stack.
|
7637
|
+
*/
|
7638
|
+
static inline void
|
7639
|
+
pm_state_stack_push(pm_state_stack_t *stack, bool value) {
|
7640
|
+
*stack = (*stack << 1) | (value & 1);
|
7641
|
+
}
|
7642
|
+
|
7643
|
+
/**
|
7644
|
+
* Pops a value off the stack.
|
7645
|
+
*/
|
7646
|
+
static inline void
|
7647
|
+
pm_state_stack_pop(pm_state_stack_t *stack) {
|
7648
|
+
*stack >>= 1;
|
7649
|
+
}
|
7650
|
+
|
7651
|
+
/**
|
7652
|
+
* Returns the value at the top of the stack.
|
7653
|
+
*/
|
7654
|
+
static inline bool
|
7655
|
+
pm_state_stack_p(const pm_state_stack_t *stack) {
|
7656
|
+
return *stack & 1;
|
7657
|
+
}
|
7658
|
+
|
7463
7659
|
static inline void
|
7464
7660
|
pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
|
7465
7661
|
// Use the negation of the value to prevent stack overflow.
|
@@ -8400,6 +8596,10 @@ lex_global_variable(pm_parser_t *parser) {
|
|
8400
8596
|
do {
|
8401
8597
|
parser->current.end += width;
|
8402
8598
|
} while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
|
8599
|
+
} else if (pm_char_is_whitespace(peek(parser))) {
|
8600
|
+
// If we get here, then we have a $ followed by whitespace,
|
8601
|
+
// which is not allowed.
|
8602
|
+
pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
|
8403
8603
|
} else {
|
8404
8604
|
// If we get here, then we have a $ followed by something that
|
8405
8605
|
// isn't recognized as a global variable.
|
@@ -9430,15 +9630,23 @@ lex_embdoc(pm_parser_t *parser) {
|
|
9430
9630
|
pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
|
9431
9631
|
if (comment == NULL) return PM_TOKEN_EOF;
|
9432
9632
|
|
9433
|
-
// Now, loop until we find the end of the embedded documentation or the end
|
9434
|
-
// the file.
|
9633
|
+
// Now, loop until we find the end of the embedded documentation or the end
|
9634
|
+
// of the file.
|
9435
9635
|
while (parser->current.end + 4 <= parser->end) {
|
9436
9636
|
parser->current.start = parser->current.end;
|
9437
9637
|
|
9438
|
-
// If we've hit the end of the embedded documentation then we'll return
|
9439
|
-
// token here.
|
9440
|
-
if (
|
9441
|
-
|
9638
|
+
// If we've hit the end of the embedded documentation then we'll return
|
9639
|
+
// that token here.
|
9640
|
+
if (
|
9641
|
+
(memcmp(parser->current.end, "=end", 4) == 0) &&
|
9642
|
+
(
|
9643
|
+
(parser->current.end + 4 == parser->end) || // end of file
|
9644
|
+
pm_char_is_whitespace(parser->current.end[4]) || // whitespace
|
9645
|
+
(parser->current.end[4] == '\0') || // NUL or end of script
|
9646
|
+
(parser->current.end[4] == '\004') || // ^D
|
9647
|
+
(parser->current.end[4] == '\032') // ^Z
|
9648
|
+
)
|
9649
|
+
) {
|
9442
9650
|
const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
|
9443
9651
|
|
9444
9652
|
if (newline == NULL) {
|
@@ -10250,9 +10458,13 @@ parser_lex(pm_parser_t *parser) {
|
|
10250
10458
|
|
10251
10459
|
// = => =~ == === =begin
|
10252
10460
|
case '=':
|
10253
|
-
if (
|
10461
|
+
if (
|
10462
|
+
current_token_starts_line(parser) &&
|
10463
|
+
(parser->current.end + 5 <= parser->end) &&
|
10464
|
+
memcmp(parser->current.end, "begin", 5) == 0 &&
|
10465
|
+
(pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
|
10466
|
+
) {
|
10254
10467
|
pm_token_type_t type = lex_embdoc(parser);
|
10255
|
-
|
10256
10468
|
if (type == PM_TOKEN_EOF) {
|
10257
10469
|
LEX(type);
|
10258
10470
|
}
|
@@ -12329,7 +12541,8 @@ static pm_node_t *
|
|
12329
12541
|
parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id);
|
12330
12542
|
|
12331
12543
|
/**
|
12332
|
-
* This is a wrapper of parse_expression, which also checks whether the
|
12544
|
+
* This is a wrapper of parse_expression, which also checks whether the
|
12545
|
+
* resulting node is a value expression.
|
12333
12546
|
*/
|
12334
12547
|
static pm_node_t *
|
12335
12548
|
parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
|
@@ -12757,6 +12970,32 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
|
|
12757
12970
|
}
|
12758
12971
|
}
|
12759
12972
|
|
12973
|
+
/**
|
12974
|
+
* Certain expressions are not writable, but in order to provide a better
|
12975
|
+
* experience we give a specific error message. In order to maintain as much
|
12976
|
+
* information in the tree as possible, we replace them with local variable
|
12977
|
+
* writes.
|
12978
|
+
*/
|
12979
|
+
static pm_node_t *
|
12980
|
+
parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
|
12981
|
+
switch (PM_NODE_TYPE(target)) {
|
12982
|
+
case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
|
12983
|
+
case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
|
12984
|
+
case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
|
12985
|
+
case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
|
12986
|
+
case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
|
12987
|
+
case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
|
12988
|
+
case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
|
12989
|
+
default: break;
|
12990
|
+
}
|
12991
|
+
|
12992
|
+
pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end);
|
12993
|
+
pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
|
12994
|
+
|
12995
|
+
pm_node_destroy(parser, target);
|
12996
|
+
return (pm_node_t *) result;
|
12997
|
+
}
|
12998
|
+
|
12760
12999
|
/**
|
12761
13000
|
* Parse a list of targets for assignment. This is used in the case of a for
|
12762
13001
|
* loop or a multi-assignment. For example, in the following code:
|
@@ -12848,7 +13087,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context) {
|
|
12848
13087
|
|
12849
13088
|
while (true) {
|
12850
13089
|
pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION);
|
12851
|
-
pm_statements_node_body_append(statements, node);
|
13090
|
+
pm_statements_node_body_append(parser, statements, node);
|
12852
13091
|
|
12853
13092
|
// If we're recovering from a syntax error, then we need to stop parsing the
|
12854
13093
|
// statements now.
|
@@ -12902,6 +13141,8 @@ parse_statements(pm_parser_t *parser, pm_context_t context) {
|
|
12902
13141
|
}
|
12903
13142
|
|
12904
13143
|
context_pop(parser);
|
13144
|
+
pm_void_statements_check(parser, statements);
|
13145
|
+
|
12905
13146
|
return statements;
|
12906
13147
|
}
|
12907
13148
|
|
@@ -13134,7 +13375,6 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
13134
13375
|
if (token_begins_expression_p(parser->current.type)) {
|
13135
13376
|
expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_ARGUMENT);
|
13136
13377
|
} else {
|
13137
|
-
// A block forwarding in a method having `...` parameter (e.g. `def foo(...); bar(&); end`) is available.
|
13138
13378
|
pm_parser_scope_forwarding_block_check(parser, &operator);
|
13139
13379
|
}
|
13140
13380
|
|
@@ -13217,7 +13457,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
13217
13457
|
pm_static_literals_t literals = { 0 };
|
13218
13458
|
pm_hash_key_static_literals_add(parser, &literals, argument);
|
13219
13459
|
|
13220
|
-
// Finish parsing the one we are part way through
|
13460
|
+
// Finish parsing the one we are part way through.
|
13221
13461
|
pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE);
|
13222
13462
|
argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value);
|
13223
13463
|
|
@@ -13477,7 +13717,6 @@ parse_parameters(
|
|
13477
13717
|
update_parameter_state(parser, &parser->current, &order);
|
13478
13718
|
parser_lex(parser);
|
13479
13719
|
|
13480
|
-
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
|
13481
13720
|
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
|
13482
13721
|
|
13483
13722
|
pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
|
@@ -13850,6 +14089,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
|
|
13850
14089
|
case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
|
13851
14090
|
case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
|
13852
14091
|
case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
|
14092
|
+
default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
|
13853
14093
|
}
|
13854
14094
|
|
13855
14095
|
pm_statements_node_t *statements = parse_statements(parser, context);
|
@@ -13897,6 +14137,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
|
|
13897
14137
|
case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
|
13898
14138
|
case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
|
13899
14139
|
case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
|
14140
|
+
default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
|
13900
14141
|
}
|
13901
14142
|
|
13902
14143
|
else_statements = parse_statements(parser, context);
|
@@ -13926,6 +14167,7 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type
|
|
13926
14167
|
case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
|
13927
14168
|
case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
|
13928
14169
|
case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
|
14170
|
+
default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
|
13929
14171
|
}
|
13930
14172
|
|
13931
14173
|
ensure_statements = parse_statements(parser, context);
|
@@ -14014,9 +14256,8 @@ parse_block_parameters(
|
|
14014
14256
|
pm_parser_local_add_token(parser, &parser->previous, 1);
|
14015
14257
|
|
14016
14258
|
pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
|
14017
|
-
if (repeated)
|
14018
|
-
|
14019
|
-
}
|
14259
|
+
if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local);
|
14260
|
+
|
14020
14261
|
pm_block_parameters_node_append_local(block_parameters, local);
|
14021
14262
|
} while (accept1(parser, PM_TOKEN_COMMA));
|
14022
14263
|
}
|
@@ -14118,7 +14359,7 @@ parse_block(pm_parser_t *parser) {
|
|
14118
14359
|
}
|
14119
14360
|
|
14120
14361
|
pm_constant_id_list_t locals;
|
14121
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
14362
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
|
14122
14363
|
pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &opening, &parser->previous);
|
14123
14364
|
|
14124
14365
|
pm_parser_scope_pop(parser);
|
@@ -14145,9 +14386,14 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
|
|
14145
14386
|
} else {
|
14146
14387
|
pm_accepts_block_stack_push(parser, true);
|
14147
14388
|
parse_arguments(parser, arguments, true, PM_TOKEN_PARENTHESIS_RIGHT);
|
14148
|
-
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_ARGUMENT_TERM_PAREN);
|
14149
|
-
pm_accepts_block_stack_pop(parser);
|
14150
14389
|
|
14390
|
+
if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
|
14391
|
+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type));
|
14392
|
+
parser->previous.start = parser->previous.end;
|
14393
|
+
parser->previous.type = PM_TOKEN_MISSING;
|
14394
|
+
}
|
14395
|
+
|
14396
|
+
pm_accepts_block_stack_pop(parser);
|
14151
14397
|
arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
|
14152
14398
|
}
|
14153
14399
|
} else if (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
|
@@ -15091,6 +15337,7 @@ parse_method_definition_name(pm_parser_t *parser) {
|
|
15091
15337
|
parser_lex(parser);
|
15092
15338
|
return parser->previous;
|
15093
15339
|
default:
|
15340
|
+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_NAME, pm_token_type_human(parser->current.type));
|
15094
15341
|
return (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end };
|
15095
15342
|
}
|
15096
15343
|
}
|
@@ -16601,7 +16848,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
16601
16848
|
// and we didn't return a multiple assignment node, then we can return a
|
16602
16849
|
// regular parentheses node now.
|
16603
16850
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
16604
|
-
pm_statements_node_body_append(statements, statement);
|
16851
|
+
pm_statements_node_body_append(parser, statements, statement);
|
16605
16852
|
|
16606
16853
|
return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous);
|
16607
16854
|
}
|
@@ -16611,7 +16858,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
16611
16858
|
// We'll do that here.
|
16612
16859
|
context_push(parser, PM_CONTEXT_PARENS);
|
16613
16860
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
16614
|
-
pm_statements_node_body_append(statements, statement);
|
16861
|
+
pm_statements_node_body_append(parser, statements, statement);
|
16615
16862
|
|
16616
16863
|
// If we didn't find a terminator and we didn't find a right
|
16617
16864
|
// parenthesis, then this is a syntax error.
|
@@ -16622,7 +16869,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
16622
16869
|
// Parse each statement within the parentheses.
|
16623
16870
|
while (true) {
|
16624
16871
|
pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION);
|
16625
|
-
pm_statements_node_body_append(statements, node);
|
16872
|
+
pm_statements_node_body_append(parser, statements, node);
|
16626
16873
|
|
16627
16874
|
// If we're recovering from a syntax error, then we need to stop
|
16628
16875
|
// parsing the statements now.
|
@@ -16656,6 +16903,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
16656
16903
|
pop_block_exits(parser, previous_block_exits);
|
16657
16904
|
pm_node_list_free(¤t_block_exits);
|
16658
16905
|
|
16906
|
+
pm_void_statements_check(parser, statements);
|
16659
16907
|
return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous);
|
16660
16908
|
}
|
16661
16909
|
case PM_TOKEN_BRACE_LEFT: {
|
@@ -17410,6 +17658,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17410
17658
|
pm_arguments_t arguments = { 0 };
|
17411
17659
|
parse_arguments_list(parser, &arguments, false, accepts_command_call);
|
17412
17660
|
|
17661
|
+
// It's possible that we've parsed a block argument through our
|
17662
|
+
// call to parse_arguments_list. If we found one, we should mark it
|
17663
|
+
// as invalid and destroy it, as we don't have a place for it on the
|
17664
|
+
// yield node.
|
17665
|
+
if (arguments.block != NULL) {
|
17666
|
+
pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
|
17667
|
+
pm_node_destroy(parser, arguments.block);
|
17668
|
+
arguments.block = NULL;
|
17669
|
+
}
|
17670
|
+
|
17413
17671
|
pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
|
17414
17672
|
if (!parser->parsing_eval) parse_yield(parser, node);
|
17415
17673
|
|
@@ -17442,7 +17700,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17442
17700
|
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
|
17443
17701
|
|
17444
17702
|
pm_constant_id_list_t locals;
|
17445
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
17703
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
|
17446
17704
|
|
17447
17705
|
pm_parser_scope_pop(parser);
|
17448
17706
|
pm_do_loop_stack_pop(parser);
|
@@ -17502,7 +17760,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17502
17760
|
}
|
17503
17761
|
|
17504
17762
|
pm_constant_id_list_t locals;
|
17505
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
17763
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
|
17506
17764
|
|
17507
17765
|
pm_parser_scope_pop(parser);
|
17508
17766
|
pm_do_loop_stack_pop(parser);
|
@@ -17521,7 +17779,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17521
17779
|
|
17522
17780
|
pm_node_t *receiver = NULL;
|
17523
17781
|
pm_token_t operator = not_provided(parser);
|
17524
|
-
pm_token_t name
|
17782
|
+
pm_token_t name;
|
17525
17783
|
|
17526
17784
|
// This context is necessary for lexing `...` in a bare params
|
17527
17785
|
// correctly. It must be pushed before lexing the first param, so it
|
@@ -17603,7 +17861,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17603
17861
|
receiver = (pm_node_t *) pm_true_node_create(parser, &identifier);
|
17604
17862
|
break;
|
17605
17863
|
case PM_TOKEN_KEYWORD_FALSE:
|
17606
|
-
receiver = (pm_node_t *)pm_false_node_create(parser, &identifier);
|
17864
|
+
receiver = (pm_node_t *) pm_false_node_create(parser, &identifier);
|
17607
17865
|
break;
|
17608
17866
|
case PM_TOKEN_KEYWORD___FILE__:
|
17609
17867
|
receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier);
|
@@ -17625,9 +17883,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17625
17883
|
break;
|
17626
17884
|
}
|
17627
17885
|
case PM_TOKEN_PARENTHESIS_LEFT: {
|
17628
|
-
// The current context is `PM_CONTEXT_DEF_PARAMS`, however
|
17629
|
-
// of this parenthesis should not be
|
17630
|
-
// Thus, the context is popped
|
17886
|
+
// The current context is `PM_CONTEXT_DEF_PARAMS`, however
|
17887
|
+
// the inner expression of this parenthesis should not be
|
17888
|
+
// processed under this context. Thus, the context is popped
|
17889
|
+
// here.
|
17631
17890
|
context_pop(parser);
|
17632
17891
|
parser_lex(parser);
|
17633
17892
|
|
@@ -17644,7 +17903,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17644
17903
|
operator = parser->previous;
|
17645
17904
|
receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, expression, &rparen);
|
17646
17905
|
|
17647
|
-
// To push `PM_CONTEXT_DEF_PARAMS` again is for the same
|
17906
|
+
// To push `PM_CONTEXT_DEF_PARAMS` again is for the same
|
17907
|
+
// reason as described the above.
|
17648
17908
|
pm_parser_scope_push(parser, true);
|
17649
17909
|
context_push(parser, PM_CONTEXT_DEF_PARAMS);
|
17650
17910
|
name = parse_method_definition_name(parser);
|
@@ -17656,12 +17916,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17656
17916
|
break;
|
17657
17917
|
}
|
17658
17918
|
|
17659
|
-
// If, after all that, we were unable to find a method name, add an
|
17660
|
-
// error to the error list.
|
17661
|
-
if (name.type == PM_TOKEN_MISSING) {
|
17662
|
-
pm_parser_err_previous(parser, PM_ERR_DEF_NAME);
|
17663
|
-
}
|
17664
|
-
|
17665
17919
|
pm_token_t lparen;
|
17666
17920
|
pm_token_t rparen;
|
17667
17921
|
pm_parameters_node_t *params;
|
@@ -17731,7 +17985,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17731
17985
|
statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value);
|
17732
17986
|
}
|
17733
17987
|
|
17734
|
-
pm_statements_node_body_append((pm_statements_node_t *) statements, statement);
|
17988
|
+
pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement);
|
17735
17989
|
pm_do_loop_stack_pop(parser);
|
17736
17990
|
context_pop(parser);
|
17737
17991
|
end_keyword = not_provided(parser);
|
@@ -17767,7 +18021,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17767
18021
|
}
|
17768
18022
|
|
17769
18023
|
pm_constant_id_list_t locals;
|
17770
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
18024
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
|
17771
18025
|
pm_parser_scope_pop(parser);
|
17772
18026
|
|
17773
18027
|
/**
|
@@ -18029,7 +18283,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18029
18283
|
}
|
18030
18284
|
|
18031
18285
|
pm_constant_id_list_t locals;
|
18032
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
18286
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
|
18033
18287
|
|
18034
18288
|
pm_parser_scope_pop(parser);
|
18035
18289
|
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM);
|
@@ -18795,7 +19049,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18795
19049
|
}
|
18796
19050
|
|
18797
19051
|
pm_constant_id_list_t locals;
|
18798
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
19052
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
|
18799
19053
|
pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &operator, &parser->previous);
|
18800
19054
|
|
18801
19055
|
pm_parser_scope_pop(parser);
|
@@ -18851,11 +19105,21 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18851
19105
|
}
|
18852
19106
|
}
|
18853
19107
|
|
18854
|
-
|
19108
|
+
/**
|
19109
|
+
* Parse a value that is going to be written to some kind of variable or method
|
19110
|
+
* call. We need to handle this separately because the rescue modifier is
|
19111
|
+
* permitted on the end of the these expressions, which is a deviation from its
|
19112
|
+
* normal binding power.
|
19113
|
+
*
|
19114
|
+
* Note that this will only be called after an operator write, as in &&=, ||=,
|
19115
|
+
* or any of the binary operators that can be written to a variable.
|
19116
|
+
*/
|
19117
|
+
static pm_node_t *
|
18855
19118
|
parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
|
18856
19119
|
pm_node_t *value = parse_value_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id);
|
18857
19120
|
|
18858
|
-
// Contradicting binding powers, the right-hand-side value of
|
19121
|
+
// Contradicting binding powers, the right-hand-side value of the assignment
|
19122
|
+
// allows the `rescue` modifier.
|
18859
19123
|
if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
|
18860
19124
|
context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
|
18861
19125
|
|
@@ -18871,14 +19135,63 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_
|
|
18871
19135
|
return value;
|
18872
19136
|
}
|
18873
19137
|
|
19138
|
+
/**
|
19139
|
+
* When a local variable write node is the value being written in a different
|
19140
|
+
* write, the local variable is considered "used".
|
19141
|
+
*/
|
19142
|
+
static void
|
19143
|
+
parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
|
19144
|
+
switch (PM_NODE_TYPE(node)) {
|
19145
|
+
case PM_BEGIN_NODE: {
|
19146
|
+
const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
|
19147
|
+
if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
|
19148
|
+
break;
|
19149
|
+
}
|
19150
|
+
case PM_LOCAL_VARIABLE_WRITE_NODE: {
|
19151
|
+
const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
|
19152
|
+
pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
|
19153
|
+
break;
|
19154
|
+
}
|
19155
|
+
case PM_PARENTHESES_NODE: {
|
19156
|
+
const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
|
19157
|
+
if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
|
19158
|
+
break;
|
19159
|
+
}
|
19160
|
+
case PM_STATEMENTS_NODE: {
|
19161
|
+
const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
|
19162
|
+
const pm_node_t *statement;
|
18874
19163
|
|
18875
|
-
|
19164
|
+
PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
|
19165
|
+
parse_assignment_value_local(parser, statement);
|
19166
|
+
}
|
19167
|
+
break;
|
19168
|
+
}
|
19169
|
+
default:
|
19170
|
+
break;
|
19171
|
+
}
|
19172
|
+
}
|
19173
|
+
|
19174
|
+
/**
|
19175
|
+
* Parse the value (or values, through an implicit array) that is going to be
|
19176
|
+
* written to some kind of variable or method call. We need to handle this
|
19177
|
+
* separately because the rescue modifier is permitted on the end of the these
|
19178
|
+
* expressions, which is a deviation from its normal binding power.
|
19179
|
+
*
|
19180
|
+
* Additionally, if the value is a local variable write node (e.g., a = a = 1),
|
19181
|
+
* the "a" is marked as being used so the parser should not warn on it.
|
19182
|
+
*
|
19183
|
+
* Note that this will only be called after an = operator, as that is the only
|
19184
|
+
* operator that allows multiple values after it.
|
19185
|
+
*/
|
19186
|
+
static pm_node_t *
|
18876
19187
|
parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
|
18877
19188
|
pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id);
|
18878
|
-
|
19189
|
+
parse_assignment_value_local(parser, value);
|
18879
19190
|
|
19191
|
+
bool single_value = true;
|
18880
19192
|
if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
|
18881
19193
|
single_value = false;
|
19194
|
+
|
18882
19195
|
pm_token_t opening = not_provided(parser);
|
18883
19196
|
pm_array_node_t *array = pm_array_node_create(parser, &opening);
|
18884
19197
|
|
@@ -18887,8 +19200,11 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
|
|
18887
19200
|
|
18888
19201
|
while (accept1(parser, PM_TOKEN_COMMA)) {
|
18889
19202
|
pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT);
|
19203
|
+
|
18890
19204
|
pm_array_node_elements_append(array, element);
|
18891
19205
|
if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break;
|
19206
|
+
|
19207
|
+
parse_assignment_value_local(parser, element);
|
18892
19208
|
}
|
18893
19209
|
}
|
18894
19210
|
|
@@ -19092,13 +19408,25 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19092
19408
|
pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
|
19093
19409
|
return parse_write(parser, (pm_node_t *) multi_target, &token, value);
|
19094
19410
|
}
|
19411
|
+
case PM_SOURCE_ENCODING_NODE:
|
19412
|
+
case PM_FALSE_NODE:
|
19413
|
+
case PM_SOURCE_FILE_NODE:
|
19414
|
+
case PM_SOURCE_LINE_NODE:
|
19415
|
+
case PM_NIL_NODE:
|
19416
|
+
case PM_SELF_NODE:
|
19417
|
+
case PM_TRUE_NODE: {
|
19418
|
+
// In these special cases, we have specific error messages
|
19419
|
+
// and we will replace them with local variable writes.
|
19420
|
+
parser_lex(parser);
|
19421
|
+
pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
|
19422
|
+
return parse_unwriteable_write(parser, node, &token, value);
|
19423
|
+
}
|
19095
19424
|
default:
|
19425
|
+
// In this case we have an = sign, but we don't know what
|
19426
|
+
// it's for. We need to treat it as an error. We'll mark it
|
19427
|
+
// as an error and skip past it.
|
19096
19428
|
parser_lex(parser);
|
19097
|
-
|
19098
|
-
// In this case we have an = sign, but we don't know what it's for. We
|
19099
|
-
// need to treat it as an error. For now, we'll mark it as an error
|
19100
|
-
// and just skip right past it.
|
19101
|
-
pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL);
|
19429
|
+
pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
|
19102
19430
|
return node;
|
19103
19431
|
}
|
19104
19432
|
}
|
@@ -19173,7 +19501,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19173
19501
|
pm_location_t *message_loc = &cast->message_loc;
|
19174
19502
|
pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
|
19175
19503
|
|
19176
|
-
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end,
|
19504
|
+
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
|
19177
19505
|
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
|
19178
19506
|
pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
|
19179
19507
|
|
@@ -19286,7 +19614,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19286
19614
|
pm_location_t *message_loc = &cast->message_loc;
|
19287
19615
|
pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
|
19288
19616
|
|
19289
|
-
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end,
|
19617
|
+
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
|
19290
19618
|
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
|
19291
19619
|
pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
|
19292
19620
|
|
@@ -19409,7 +19737,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19409
19737
|
pm_location_t *message_loc = &cast->message_loc;
|
19410
19738
|
pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
|
19411
19739
|
|
19412
|
-
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end,
|
19740
|
+
pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
|
19413
19741
|
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
|
19414
19742
|
pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0);
|
19415
19743
|
|
@@ -19593,7 +19921,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19593
19921
|
break;
|
19594
19922
|
}
|
19595
19923
|
default: {
|
19596
|
-
|
19924
|
+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type));
|
19597
19925
|
message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
|
19598
19926
|
}
|
19599
19927
|
}
|
@@ -19640,7 +19968,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19640
19968
|
case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
|
19641
19969
|
parser_lex(parser);
|
19642
19970
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
19643
|
-
pm_statements_node_body_append(statements, node);
|
19971
|
+
pm_statements_node_body_append(parser, statements, node);
|
19644
19972
|
|
19645
19973
|
pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
|
19646
19974
|
return (pm_node_t *) pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0);
|
@@ -19648,7 +19976,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
|
|
19648
19976
|
case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
|
19649
19977
|
parser_lex(parser);
|
19650
19978
|
pm_statements_node_t *statements = pm_statements_node_create(parser);
|
19651
|
-
pm_statements_node_body_append(statements, node);
|
19979
|
+
pm_statements_node_body_append(parser, statements, node);
|
19652
19980
|
|
19653
19981
|
pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
|
19654
19982
|
return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0);
|
@@ -19993,7 +20321,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
|
|
19993
20321
|
(pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2))
|
19994
20322
|
);
|
19995
20323
|
|
19996
|
-
pm_statements_node_body_append(statements, (pm_node_t *) pm_call_node_fcall_synthesized_create(
|
20324
|
+
pm_statements_node_body_append(parser, statements, (pm_node_t *) pm_call_node_fcall_synthesized_create(
|
19997
20325
|
parser,
|
19998
20326
|
arguments,
|
19999
20327
|
pm_parser_constant_id_constant(parser, "print", 5)
|
@@ -20039,7 +20367,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
|
|
20039
20367
|
}
|
20040
20368
|
|
20041
20369
|
pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
|
20042
|
-
pm_statements_node_body_append(wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create(
|
20370
|
+
pm_statements_node_body_append(parser, wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create(
|
20043
20371
|
parser,
|
20044
20372
|
(pm_node_t *) pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4)),
|
20045
20373
|
statements
|
@@ -20065,12 +20393,19 @@ parse_program(pm_parser_t *parser) {
|
|
20065
20393
|
|
20066
20394
|
parser_lex(parser);
|
20067
20395
|
pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN);
|
20068
|
-
|
20396
|
+
|
20397
|
+
if (statements == NULL) {
|
20069
20398
|
statements = pm_statements_node_create(parser);
|
20399
|
+
} else if (!parser->parsing_eval) {
|
20400
|
+
// If we have statements, then the top-level statement should be
|
20401
|
+
// explicitly checked as well. We have to do this here because
|
20402
|
+
// everywhere else we check all but the last statement.
|
20403
|
+
assert(statements->body.size > 0);
|
20404
|
+
pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
|
20070
20405
|
}
|
20071
20406
|
|
20072
20407
|
pm_constant_id_list_t locals;
|
20073
|
-
pm_locals_order(parser, &parser->current_scope->locals, &locals,
|
20408
|
+
pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
|
20074
20409
|
pm_parser_scope_pop(parser);
|
20075
20410
|
|
20076
20411
|
// If this is an empty file, then we're still going to parse all of the
|
@@ -20517,7 +20852,7 @@ pm_parse_success_p(const uint8_t *source, size_t size, const char *data) {
|
|
20517
20852
|
pm_node_t *node = pm_parse(&parser);
|
20518
20853
|
pm_node_destroy(&parser, node);
|
20519
20854
|
|
20520
|
-
bool result = parser.error_list.size == 0
|
20855
|
+
bool result = parser.error_list.size == 0;
|
20521
20856
|
pm_parser_free(&parser);
|
20522
20857
|
pm_options_free(&options);
|
20523
20858
|
|
@@ -20919,6 +21254,8 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list,
|
|
20919
21254
|
|
20920
21255
|
if (inline_messages) {
|
20921
21256
|
pm_buffer_append_byte(buffer, ' ');
|
21257
|
+
assert(error->error != NULL);
|
21258
|
+
|
20922
21259
|
const char *message = error->error->message;
|
20923
21260
|
pm_buffer_append_string(buffer, message, strlen(message));
|
20924
21261
|
}
|