prism 1.1.0 → 1.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 +17 -1
- data/config.yml +2 -1
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +1 -1
- data/include/prism/diagnostic.h +1 -0
- data/include/prism/parser.h +25 -12
- data/include/prism/version.h +2 -2
- data/lib/prism/node.rb +1 -1
- data/lib/prism/parse_result.rb +140 -3
- data/lib/prism/serialize.rb +13 -1
- data/lib/prism/translation/parser.rb +3 -3
- data/lib/prism/translation/ripper.rb +1 -5
- data/lib/prism/translation/ruby_parser.rb +2 -2
- data/prism.gemspec +1 -1
- data/rbi/prism/node.rbi +5777 -1701
- data/rbi/prism/parse_result.rbi +29 -0
- data/rbi/prism.rbi +34 -34
- data/sig/prism/node.rbs +1 -90
- data/sig/prism/parse_result.rbs +20 -0
- data/src/diagnostic.c +3 -1
- data/src/prism.c +223 -115
- metadata +2 -2
data/src/prism.c
CHANGED
@@ -544,10 +544,7 @@ pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id
|
|
544
544
|
* token.
|
545
545
|
*/
|
546
546
|
static void
|
547
|
-
pm_parser_err_heredoc_term(pm_parser_t *parser,
|
548
|
-
const uint8_t *ident_start = lex_mode->as.heredoc.ident_start;
|
549
|
-
size_t ident_length = lex_mode->as.heredoc.ident_length;
|
550
|
-
|
547
|
+
pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
|
551
548
|
PM_PARSER_ERR_FORMAT(
|
552
549
|
parser,
|
553
550
|
ident_start,
|
@@ -964,7 +961,7 @@ pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals,
|
|
964
961
|
if (local->name != PM_CONSTANT_ID_UNSET) {
|
965
962
|
pm_constant_id_list_insert(list, (size_t) local->index, local->name);
|
966
963
|
|
967
|
-
if (warn_unused && local->reads == 0) {
|
964
|
+
if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_newline_list_line(&parser->newline_list, local->location.start, parser->start_line) >= 0))) {
|
968
965
|
pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
|
969
966
|
|
970
967
|
if (constant->length >= 1 && *constant->start != '_') {
|
@@ -2110,14 +2107,6 @@ pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
|
|
2110
2107
|
return node;
|
2111
2108
|
}
|
2112
2109
|
|
2113
|
-
/**
|
2114
|
-
* Return the size of the given array node.
|
2115
|
-
*/
|
2116
|
-
static inline size_t
|
2117
|
-
pm_array_node_size(pm_array_node_t *node) {
|
2118
|
-
return node->elements.size;
|
2119
|
-
}
|
2120
|
-
|
2121
2110
|
/**
|
2122
2111
|
* Append an argument to an array node.
|
2123
2112
|
*/
|
@@ -8573,6 +8562,7 @@ context_terminator(pm_context_t context, pm_token_t *token) {
|
|
8573
8562
|
case PM_CONTEXT_MAIN:
|
8574
8563
|
case PM_CONTEXT_DEF_PARAMS:
|
8575
8564
|
case PM_CONTEXT_DEFINED:
|
8565
|
+
case PM_CONTEXT_MULTI_TARGET:
|
8576
8566
|
case PM_CONTEXT_TERNARY:
|
8577
8567
|
case PM_CONTEXT_RESCUE_MODIFIER:
|
8578
8568
|
return token->type == PM_TOKEN_EOF;
|
@@ -8777,6 +8767,7 @@ context_human(pm_context_t context) {
|
|
8777
8767
|
case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
|
8778
8768
|
case PM_CONTEXT_MAIN: return "top level context";
|
8779
8769
|
case PM_CONTEXT_MODULE: return "module definition";
|
8770
|
+
case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
|
8780
8771
|
case PM_CONTEXT_PARENS: return "parentheses";
|
8781
8772
|
case PM_CONTEXT_POSTEXE: return "'END' block";
|
8782
8773
|
case PM_CONTEXT_PREDICATE: return "predicate";
|
@@ -9051,6 +9042,10 @@ lex_global_variable(pm_parser_t *parser) {
|
|
9051
9042
|
return PM_TOKEN_GLOBAL_VARIABLE;
|
9052
9043
|
}
|
9053
9044
|
|
9045
|
+
// True if multiple characters are allowed after the declaration of the
|
9046
|
+
// global variable. Not true when it starts with "$-".
|
9047
|
+
bool allow_multiple = true;
|
9048
|
+
|
9054
9049
|
switch (*parser->current.end) {
|
9055
9050
|
case '~': // $~: match-data
|
9056
9051
|
case '*': // $*: argv
|
@@ -9109,6 +9104,7 @@ lex_global_variable(pm_parser_t *parser) {
|
|
9109
9104
|
|
9110
9105
|
case '-':
|
9111
9106
|
parser->current.end++;
|
9107
|
+
allow_multiple = false;
|
9112
9108
|
/* fallthrough */
|
9113
9109
|
default: {
|
9114
9110
|
size_t width;
|
@@ -9116,7 +9112,7 @@ lex_global_variable(pm_parser_t *parser) {
|
|
9116
9112
|
if ((width = char_is_identifier(parser, parser->current.end)) > 0) {
|
9117
9113
|
do {
|
9118
9114
|
parser->current.end += width;
|
9119
|
-
} while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
|
9115
|
+
} while (allow_multiple && parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0);
|
9120
9116
|
} else if (pm_char_is_whitespace(peek(parser))) {
|
9121
9117
|
// If we get here, then we have a $ followed by whitespace,
|
9122
9118
|
// which is not allowed.
|
@@ -9881,6 +9877,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
9881
9877
|
}
|
9882
9878
|
case 'c': {
|
9883
9879
|
parser->current.end++;
|
9880
|
+
if (flags & PM_ESCAPE_FLAG_CONTROL) {
|
9881
|
+
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
9882
|
+
}
|
9883
|
+
|
9884
9884
|
if (parser->current.end == parser->end) {
|
9885
9885
|
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
|
9886
9886
|
return;
|
@@ -9894,10 +9894,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
9894
9894
|
return;
|
9895
9895
|
}
|
9896
9896
|
case '\\':
|
9897
|
-
if (flags & PM_ESCAPE_FLAG_CONTROL) {
|
9898
|
-
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
9899
|
-
return;
|
9900
|
-
}
|
9901
9897
|
parser->current.end++;
|
9902
9898
|
|
9903
9899
|
if (match(parser, 'u') || match(parser, 'U')) {
|
@@ -9931,6 +9927,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
9931
9927
|
}
|
9932
9928
|
case 'C': {
|
9933
9929
|
parser->current.end++;
|
9930
|
+
if (flags & PM_ESCAPE_FLAG_CONTROL) {
|
9931
|
+
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
9932
|
+
}
|
9933
|
+
|
9934
9934
|
if (peek(parser) != '-') {
|
9935
9935
|
size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
|
9936
9936
|
pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL);
|
@@ -9951,10 +9951,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
9951
9951
|
return;
|
9952
9952
|
}
|
9953
9953
|
case '\\':
|
9954
|
-
if (flags & PM_ESCAPE_FLAG_CONTROL) {
|
9955
|
-
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
9956
|
-
return;
|
9957
|
-
}
|
9958
9954
|
parser->current.end++;
|
9959
9955
|
|
9960
9956
|
if (match(parser, 'u') || match(parser, 'U')) {
|
@@ -9989,6 +9985,10 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
9989
9985
|
}
|
9990
9986
|
case 'M': {
|
9991
9987
|
parser->current.end++;
|
9988
|
+
if (flags & PM_ESCAPE_FLAG_META) {
|
9989
|
+
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
|
9990
|
+
}
|
9991
|
+
|
9992
9992
|
if (peek(parser) != '-') {
|
9993
9993
|
size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
|
9994
9994
|
pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META);
|
@@ -10004,10 +10004,6 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
10004
10004
|
uint8_t peeked = peek(parser);
|
10005
10005
|
switch (peeked) {
|
10006
10006
|
case '\\':
|
10007
|
-
if (flags & PM_ESCAPE_FLAG_META) {
|
10008
|
-
pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
|
10009
|
-
return;
|
10010
|
-
}
|
10011
10007
|
parser->current.end++;
|
10012
10008
|
|
10013
10009
|
if (match(parser, 'u') || match(parser, 'U')) {
|
@@ -10050,6 +10046,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre
|
|
10050
10046
|
default: {
|
10051
10047
|
if (parser->current.end < parser->end) {
|
10052
10048
|
escape_write_escape_encoded(parser, buffer);
|
10049
|
+
} else {
|
10050
|
+
pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
|
10053
10051
|
}
|
10054
10052
|
return;
|
10055
10053
|
}
|
@@ -11153,12 +11151,14 @@ parser_lex(pm_parser_t *parser) {
|
|
11153
11151
|
lex_mode_push(parser, (pm_lex_mode_t) {
|
11154
11152
|
.mode = PM_LEX_HEREDOC,
|
11155
11153
|
.as.heredoc = {
|
11156
|
-
.
|
11157
|
-
|
11154
|
+
.base = {
|
11155
|
+
.ident_start = ident_start,
|
11156
|
+
.ident_length = ident_length,
|
11157
|
+
.quote = quote,
|
11158
|
+
.indent = indent
|
11159
|
+
},
|
11158
11160
|
.next_start = parser->current.end,
|
11159
|
-
.
|
11160
|
-
.indent = indent,
|
11161
|
-
.common_whitespace = (size_t) -1,
|
11161
|
+
.common_whitespace = NULL,
|
11162
11162
|
.line_continuation = false
|
11163
11163
|
}
|
11164
11164
|
});
|
@@ -11171,7 +11171,7 @@ parser_lex(pm_parser_t *parser) {
|
|
11171
11171
|
// this is not a valid heredoc declaration. In this case we
|
11172
11172
|
// will add an error, but we will still return a heredoc
|
11173
11173
|
// start.
|
11174
|
-
if (!ident_error) pm_parser_err_heredoc_term(parser,
|
11174
|
+
if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
|
11175
11175
|
body_start = parser->end;
|
11176
11176
|
} else {
|
11177
11177
|
// Otherwise, we want to indicate that the body of the
|
@@ -12514,6 +12514,7 @@ parser_lex(pm_parser_t *parser) {
|
|
12514
12514
|
// Now let's grab the information about the identifier off of the
|
12515
12515
|
// current lex mode.
|
12516
12516
|
pm_lex_mode_t *lex_mode = parser->lex_modes.current;
|
12517
|
+
pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
|
12517
12518
|
|
12518
12519
|
bool line_continuation = lex_mode->as.heredoc.line_continuation;
|
12519
12520
|
lex_mode->as.heredoc.line_continuation = false;
|
@@ -12523,15 +12524,16 @@ parser_lex(pm_parser_t *parser) {
|
|
12523
12524
|
// terminator) but still continue parsing so that content after the
|
12524
12525
|
// declaration of the heredoc can be parsed.
|
12525
12526
|
if (parser->current.end >= parser->end) {
|
12526
|
-
pm_parser_err_heredoc_term(parser,
|
12527
|
+
pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
|
12527
12528
|
parser->next_start = lex_mode->as.heredoc.next_start;
|
12528
12529
|
parser->heredoc_end = parser->current.end;
|
12529
12530
|
lex_state_set(parser, PM_LEX_STATE_END);
|
12531
|
+
lex_mode_pop(parser);
|
12530
12532
|
LEX(PM_TOKEN_HEREDOC_END);
|
12531
12533
|
}
|
12532
12534
|
|
12533
|
-
const uint8_t *ident_start =
|
12534
|
-
size_t ident_length =
|
12535
|
+
const uint8_t *ident_start = heredoc_lex_mode->ident_start;
|
12536
|
+
size_t ident_length = heredoc_lex_mode->ident_length;
|
12535
12537
|
|
12536
12538
|
// If we are immediately following a newline and we have hit the
|
12537
12539
|
// terminator, then we need to return the ending of the heredoc.
|
@@ -12556,10 +12558,7 @@ parser_lex(pm_parser_t *parser) {
|
|
12556
12558
|
const uint8_t *terminator_start = ident_end - ident_length;
|
12557
12559
|
const uint8_t *cursor = start;
|
12558
12560
|
|
12559
|
-
if (
|
12560
|
-
lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_DASH ||
|
12561
|
-
lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE
|
12562
|
-
) {
|
12561
|
+
if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
|
12563
12562
|
while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
|
12564
12563
|
cursor++;
|
12565
12564
|
}
|
@@ -12582,17 +12581,19 @@ parser_lex(pm_parser_t *parser) {
|
|
12582
12581
|
}
|
12583
12582
|
|
12584
12583
|
lex_state_set(parser, PM_LEX_STATE_END);
|
12584
|
+
lex_mode_pop(parser);
|
12585
12585
|
LEX(PM_TOKEN_HEREDOC_END);
|
12586
12586
|
}
|
12587
12587
|
}
|
12588
12588
|
|
12589
|
-
size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start,
|
12589
|
+
size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
|
12590
12590
|
if (
|
12591
|
-
|
12592
|
-
|
12591
|
+
heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
|
12592
|
+
lex_mode->as.heredoc.common_whitespace != NULL &&
|
12593
|
+
(*lex_mode->as.heredoc.common_whitespace > whitespace) &&
|
12593
12594
|
peek_at(parser, start) != '\n'
|
12594
12595
|
) {
|
12595
|
-
lex_mode->as.heredoc.common_whitespace = whitespace;
|
12596
|
+
*lex_mode->as.heredoc.common_whitespace = whitespace;
|
12596
12597
|
}
|
12597
12598
|
}
|
12598
12599
|
|
@@ -12601,7 +12602,7 @@ parser_lex(pm_parser_t *parser) {
|
|
12601
12602
|
// strpbrk to find the first of these characters.
|
12602
12603
|
uint8_t breakpoints[] = "\r\n\\#";
|
12603
12604
|
|
12604
|
-
pm_heredoc_quote_t quote =
|
12605
|
+
pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
|
12605
12606
|
if (quote == PM_HEREDOC_QUOTE_SINGLE) {
|
12606
12607
|
breakpoints[3] = '\0';
|
12607
12608
|
}
|
@@ -12664,8 +12665,7 @@ parser_lex(pm_parser_t *parser) {
|
|
12664
12665
|
// leading whitespace if we have a - or ~ heredoc.
|
12665
12666
|
const uint8_t *cursor = start;
|
12666
12667
|
|
12667
|
-
if (
|
12668
|
-
lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
|
12668
|
+
if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
|
12669
12669
|
while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
|
12670
12670
|
cursor++;
|
12671
12671
|
}
|
@@ -12681,16 +12681,16 @@ parser_lex(pm_parser_t *parser) {
|
|
12681
12681
|
}
|
12682
12682
|
}
|
12683
12683
|
|
12684
|
-
size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.indent);
|
12684
|
+
size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
|
12685
12685
|
|
12686
12686
|
// If we have hit a newline that is followed by a valid
|
12687
12687
|
// terminator, then we need to return the content of the
|
12688
12688
|
// heredoc here as string content. Then, the next time a
|
12689
12689
|
// token is lexed, it will match again and return the
|
12690
12690
|
// end of the heredoc.
|
12691
|
-
if (lex_mode->as.heredoc.indent == PM_HEREDOC_INDENT_TILDE) {
|
12692
|
-
if ((lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
|
12693
|
-
lex_mode->as.heredoc.common_whitespace = whitespace;
|
12691
|
+
if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
|
12692
|
+
if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
|
12693
|
+
*lex_mode->as.heredoc.common_whitespace = whitespace;
|
12694
12694
|
}
|
12695
12695
|
|
12696
12696
|
parser->current.end = breakpoint + 1;
|
@@ -12757,7 +12757,7 @@ parser_lex(pm_parser_t *parser) {
|
|
12757
12757
|
// If we are in a tilde here, we should
|
12758
12758
|
// break out of the loop and return the
|
12759
12759
|
// string content.
|
12760
|
-
if (
|
12760
|
+
if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
|
12761
12761
|
const uint8_t *end = parser->current.end;
|
12762
12762
|
pm_newline_list_append(&parser->newline_list, end);
|
12763
12763
|
|
@@ -12983,7 +12983,7 @@ pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
|
|
12983
12983
|
[PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
|
12984
12984
|
[PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
|
12985
12985
|
[PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
|
12986
|
-
[PM_TOKEN_USTAR] =
|
12986
|
+
[PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
|
12987
12987
|
|
12988
12988
|
// -@
|
12989
12989
|
[PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
|
@@ -13165,13 +13165,11 @@ expect3(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_to
|
|
13165
13165
|
* lex mode accordingly.
|
13166
13166
|
*/
|
13167
13167
|
static void
|
13168
|
-
expect1_heredoc_term(pm_parser_t *parser,
|
13168
|
+
expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
|
13169
13169
|
if (match1(parser, PM_TOKEN_HEREDOC_END)) {
|
13170
|
-
lex_mode_pop(parser);
|
13171
13170
|
parser_lex(parser);
|
13172
13171
|
} else {
|
13173
|
-
pm_parser_err_heredoc_term(parser,
|
13174
|
-
lex_mode_pop(parser);
|
13172
|
+
pm_parser_err_heredoc_term(parser, ident_start, ident_length);
|
13175
13173
|
parser->previous.start = parser->previous.end;
|
13176
13174
|
parser->previous.type = PM_TOKEN_MISSING;
|
13177
13175
|
}
|
@@ -13797,6 +13795,13 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b
|
|
13797
13795
|
pm_node_t *splat = (pm_node_t *) pm_splat_node_create(parser, &star_operator, name);
|
13798
13796
|
pm_multi_target_node_targets_append(parser, result, splat);
|
13799
13797
|
has_rest = true;
|
13798
|
+
} else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
|
13799
|
+
context_push(parser, PM_CONTEXT_MULTI_TARGET);
|
13800
|
+
pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
|
13801
|
+
target = parse_target(parser, target, true, false);
|
13802
|
+
|
13803
|
+
pm_multi_target_node_targets_append(parser, result, target);
|
13804
|
+
context_pop(parser);
|
13800
13805
|
} else if (token_begins_expression_p(parser->current.type)) {
|
13801
13806
|
pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
|
13802
13807
|
target = parse_target(parser, target, true, false);
|
@@ -14108,8 +14113,8 @@ static void
|
|
14108
14113
|
parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint16_t depth) {
|
14109
14114
|
pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
|
14110
14115
|
|
14111
|
-
// First we need to check if the next token is one that could be the start
|
14112
|
-
// an argument. If it's not, then we can just return.
|
14116
|
+
// First we need to check if the next token is one that could be the start
|
14117
|
+
// of an argument. If it's not, then we can just return.
|
14113
14118
|
if (
|
14114
14119
|
match2(parser, terminator, PM_TOKEN_EOF) ||
|
14115
14120
|
(binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
|
@@ -14297,23 +14302,32 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
14297
14302
|
// If parsing the argument failed, we need to stop parsing arguments.
|
14298
14303
|
if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break;
|
14299
14304
|
|
14300
|
-
// If the terminator of these arguments is not EOF, then we have a
|
14301
|
-
// token we're looking for. In that case we can accept a
|
14302
|
-
// because it is not functioning as a statement terminator.
|
14303
|
-
|
14305
|
+
// If the terminator of these arguments is not EOF, then we have a
|
14306
|
+
// specific token we're looking for. In that case we can accept a
|
14307
|
+
// newline here because it is not functioning as a statement terminator.
|
14308
|
+
bool accepted_newline = false;
|
14309
|
+
if (terminator != PM_TOKEN_EOF) {
|
14310
|
+
accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
|
14311
|
+
}
|
14304
14312
|
|
14305
14313
|
if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
|
14306
|
-
// If we previously were on a comma and we just parsed a bare hash,
|
14307
|
-
// we want to continue parsing arguments. This is because the
|
14308
|
-
// grabbed up by the hash parser.
|
14314
|
+
// If we previously were on a comma and we just parsed a bare hash,
|
14315
|
+
// then we want to continue parsing arguments. This is because the
|
14316
|
+
// comma was grabbed up by the hash parser.
|
14317
|
+
} else if (accept1(parser, PM_TOKEN_COMMA)) {
|
14318
|
+
// If there was a comma, then we need to check if we also accepted a
|
14319
|
+
// newline. If we did, then this is a syntax error.
|
14320
|
+
if (accepted_newline) {
|
14321
|
+
pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
|
14322
|
+
}
|
14309
14323
|
} else {
|
14310
|
-
// If there is no comma at the end of the argument list then we're
|
14311
|
-
// parsing arguments and can break out of this loop.
|
14312
|
-
|
14324
|
+
// If there is no comma at the end of the argument list then we're
|
14325
|
+
// done parsing arguments and can break out of this loop.
|
14326
|
+
break;
|
14313
14327
|
}
|
14314
14328
|
|
14315
|
-
// If we hit the terminator, then that means we have a trailing comma so
|
14316
|
-
// can accept that output as well.
|
14329
|
+
// If we hit the terminator, then that means we have a trailing comma so
|
14330
|
+
// we can accept that output as well.
|
14317
14331
|
if (match1(parser, terminator)) break;
|
14318
14332
|
}
|
14319
14333
|
}
|
@@ -14470,13 +14484,14 @@ parse_parameters(
|
|
14470
14484
|
bool accepts_blocks_in_defaults,
|
14471
14485
|
uint16_t depth
|
14472
14486
|
) {
|
14473
|
-
pm_parameters_node_t *params = pm_parameters_node_create(parser);
|
14474
|
-
bool looping = true;
|
14475
|
-
|
14476
14487
|
pm_do_loop_stack_push(parser, false);
|
14488
|
+
|
14489
|
+
pm_parameters_node_t *params = pm_parameters_node_create(parser);
|
14477
14490
|
pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
|
14478
14491
|
|
14479
|
-
|
14492
|
+
while (true) {
|
14493
|
+
bool parsing = true;
|
14494
|
+
|
14480
14495
|
switch (parser->current.type) {
|
14481
14496
|
case PM_TOKEN_PARENTHESIS_LEFT: {
|
14482
14497
|
update_parameter_state(parser, &parser->current, &order);
|
@@ -14611,7 +14626,7 @@ parse_parameters(
|
|
14611
14626
|
// then we can put a missing node in its place and stop parsing the
|
14612
14627
|
// parameters entirely now.
|
14613
14628
|
if (parser->recovering) {
|
14614
|
-
|
14629
|
+
parsing = false;
|
14615
14630
|
break;
|
14616
14631
|
}
|
14617
14632
|
} else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
|
@@ -14669,7 +14684,7 @@ parse_parameters(
|
|
14669
14684
|
context_pop(parser);
|
14670
14685
|
|
14671
14686
|
if (uses_parentheses) {
|
14672
|
-
|
14687
|
+
parsing = false;
|
14673
14688
|
break;
|
14674
14689
|
}
|
14675
14690
|
|
@@ -14713,7 +14728,7 @@ parse_parameters(
|
|
14713
14728
|
// then we can put a missing node in its place and stop parsing the
|
14714
14729
|
// parameters entirely now.
|
14715
14730
|
if (parser->recovering) {
|
14716
|
-
|
14731
|
+
parsing = false;
|
14717
14732
|
break;
|
14718
14733
|
}
|
14719
14734
|
}
|
@@ -14815,14 +14830,31 @@ parse_parameters(
|
|
14815
14830
|
}
|
14816
14831
|
}
|
14817
14832
|
|
14818
|
-
|
14833
|
+
parsing = false;
|
14819
14834
|
break;
|
14820
14835
|
}
|
14821
14836
|
|
14822
|
-
|
14823
|
-
|
14837
|
+
// If we hit some kind of issue while parsing the parameter, this would
|
14838
|
+
// have been set to false. In that case, we need to break out of the
|
14839
|
+
// loop.
|
14840
|
+
if (!parsing) break;
|
14841
|
+
|
14842
|
+
bool accepted_newline = false;
|
14843
|
+
if (uses_parentheses) {
|
14844
|
+
accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
|
14824
14845
|
}
|
14825
|
-
|
14846
|
+
|
14847
|
+
if (accept1(parser, PM_TOKEN_COMMA)) {
|
14848
|
+
// If there was a comma, but we also accepted a newline, then this
|
14849
|
+
// is a syntax error.
|
14850
|
+
if (accepted_newline) {
|
14851
|
+
pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
|
14852
|
+
}
|
14853
|
+
} else {
|
14854
|
+
// If there was no comma, then we're done parsing parameters.
|
14855
|
+
break;
|
14856
|
+
}
|
14857
|
+
}
|
14826
14858
|
|
14827
14859
|
pm_do_loop_stack_pop(parser);
|
14828
14860
|
|
@@ -15500,6 +15532,7 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
|
|
15500
15532
|
case PM_CONTEXT_IF:
|
15501
15533
|
case PM_CONTEXT_LOOP_PREDICATE:
|
15502
15534
|
case PM_CONTEXT_MAIN:
|
15535
|
+
case PM_CONTEXT_MULTI_TARGET:
|
15503
15536
|
case PM_CONTEXT_PARENS:
|
15504
15537
|
case PM_CONTEXT_POSTEXE:
|
15505
15538
|
case PM_CONTEXT_PREDICATE:
|
@@ -15628,6 +15661,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
|
|
15628
15661
|
case PM_CONTEXT_MODULE_ENSURE:
|
15629
15662
|
case PM_CONTEXT_MODULE_RESCUE:
|
15630
15663
|
case PM_CONTEXT_MODULE:
|
15664
|
+
case PM_CONTEXT_MULTI_TARGET:
|
15631
15665
|
case PM_CONTEXT_PARENS:
|
15632
15666
|
case PM_CONTEXT_PREDICATE:
|
15633
15667
|
case PM_CONTEXT_RESCUE_MODIFIER:
|
@@ -17045,7 +17079,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
|
|
17045
17079
|
parse_pattern_hash_key(parser, &keys, first_node);
|
17046
17080
|
pm_node_t *value;
|
17047
17081
|
|
17048
|
-
if (
|
17082
|
+
if (match8(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
|
17049
17083
|
// Otherwise, we will create an implicit local variable
|
17050
17084
|
// target for the value.
|
17051
17085
|
value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
|
@@ -17082,7 +17116,12 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
|
|
17082
17116
|
// If there are any other assocs, then we'll parse them now.
|
17083
17117
|
while (accept1(parser, PM_TOKEN_COMMA)) {
|
17084
17118
|
// Here we need to break to support trailing commas.
|
17085
|
-
if (
|
17119
|
+
if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
|
17120
|
+
// Trailing commas are not allowed to follow a rest pattern.
|
17121
|
+
if (rest != NULL) {
|
17122
|
+
pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
|
17123
|
+
}
|
17124
|
+
|
17086
17125
|
break;
|
17087
17126
|
}
|
17088
17127
|
|
@@ -17575,9 +17614,10 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
|
|
17575
17614
|
// Gather up all of the patterns into the list.
|
17576
17615
|
while (accept1(parser, PM_TOKEN_COMMA)) {
|
17577
17616
|
// Break early here in case we have a trailing comma.
|
17578
|
-
if (
|
17617
|
+
if (match6(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
|
17579
17618
|
node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
|
17580
17619
|
pm_node_list_append(&nodes, node);
|
17620
|
+
trailing_rest = true;
|
17581
17621
|
break;
|
17582
17622
|
}
|
17583
17623
|
|
@@ -17779,6 +17819,7 @@ parse_retry(pm_parser_t *parser, const pm_node_t *node) {
|
|
17779
17819
|
case PM_CONTEXT_LAMBDA_BRACES:
|
17780
17820
|
case PM_CONTEXT_LAMBDA_DO_END:
|
17781
17821
|
case PM_CONTEXT_LOOP_PREDICATE:
|
17822
|
+
case PM_CONTEXT_MULTI_TARGET:
|
17782
17823
|
case PM_CONTEXT_PARENS:
|
17783
17824
|
case PM_CONTEXT_POSTEXE:
|
17784
17825
|
case PM_CONTEXT_PREDICATE:
|
@@ -17862,6 +17903,7 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) {
|
|
17862
17903
|
case PM_CONTEXT_LAMBDA_ENSURE:
|
17863
17904
|
case PM_CONTEXT_LAMBDA_RESCUE:
|
17864
17905
|
case PM_CONTEXT_LOOP_PREDICATE:
|
17906
|
+
case PM_CONTEXT_MULTI_TARGET:
|
17865
17907
|
case PM_CONTEXT_PARENS:
|
17866
17908
|
case PM_CONTEXT_POSTEXE:
|
17867
17909
|
case PM_CONTEXT_PREDICATE:
|
@@ -17951,19 +17993,31 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
17951
17993
|
bool parsed_bare_hash = false;
|
17952
17994
|
|
17953
17995
|
while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
|
17996
|
+
bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
|
17997
|
+
|
17954
17998
|
// Handle the case where we don't have a comma and we have a
|
17955
17999
|
// newline followed by a right bracket.
|
17956
|
-
if (
|
18000
|
+
if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
|
17957
18001
|
break;
|
17958
18002
|
}
|
17959
18003
|
|
17960
18004
|
// Ensure that we have a comma between elements in the array.
|
17961
|
-
if (
|
17962
|
-
|
17963
|
-
|
18005
|
+
if (array->elements.size > 0) {
|
18006
|
+
if (accept1(parser, PM_TOKEN_COMMA)) {
|
18007
|
+
// If there was a comma but we also accepts a newline,
|
18008
|
+
// then this is a syntax error.
|
18009
|
+
if (accepted_newline) {
|
18010
|
+
pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
|
18011
|
+
}
|
18012
|
+
} else {
|
18013
|
+
// If there was no comma, then we need to add a syntax
|
18014
|
+
// error.
|
18015
|
+
const uint8_t *location = parser->previous.end;
|
18016
|
+
PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type));
|
17964
18017
|
|
17965
|
-
|
17966
|
-
|
18018
|
+
parser->previous.start = location;
|
18019
|
+
parser->previous.type = PM_TOKEN_MISSING;
|
18020
|
+
}
|
17967
18021
|
}
|
17968
18022
|
|
17969
18023
|
// If we have a right bracket immediately following a comma,
|
@@ -18119,14 +18173,32 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18119
18173
|
multi_target->base.location.start = lparen_loc.start;
|
18120
18174
|
multi_target->base.location.end = rparen_loc.end;
|
18121
18175
|
|
18122
|
-
|
18123
|
-
|
18124
|
-
|
18125
|
-
|
18126
|
-
|
18176
|
+
pm_node_t *result;
|
18177
|
+
if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
|
18178
|
+
result = parse_targets(parser, (pm_node_t *) multi_target, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
|
18179
|
+
accept1(parser, PM_TOKEN_NEWLINE);
|
18180
|
+
} else {
|
18181
|
+
result = (pm_node_t *) multi_target;
|
18127
18182
|
}
|
18128
18183
|
|
18129
|
-
|
18184
|
+
if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
|
18185
|
+
// All set, this is explicitly allowed by the parent
|
18186
|
+
// context.
|
18187
|
+
} else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) {
|
18188
|
+
// All set, we're inside a for loop and we're parsing
|
18189
|
+
// multiple targets.
|
18190
|
+
} else if (binding_power != PM_BINDING_POWER_STATEMENT) {
|
18191
|
+
// Multi targets are not allowed when it's not a
|
18192
|
+
// statement level.
|
18193
|
+
pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
|
18194
|
+
} else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
|
18195
|
+
// Multi targets must be followed by an equal sign in
|
18196
|
+
// order to be valid (or a right parenthesis if they are
|
18197
|
+
// nested).
|
18198
|
+
pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
|
18199
|
+
}
|
18200
|
+
|
18201
|
+
return result;
|
18130
18202
|
}
|
18131
18203
|
|
18132
18204
|
// If we have a single statement and are ending on a right parenthesis
|
@@ -18187,6 +18259,33 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18187
18259
|
pm_accepts_block_stack_pop(parser);
|
18188
18260
|
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
|
18189
18261
|
|
18262
|
+
// When we're parsing multi targets, we allow them to be followed by
|
18263
|
+
// a right parenthesis if they are at the statement level. This is
|
18264
|
+
// only possible if they are the final statement in a parentheses.
|
18265
|
+
// We need to explicitly reject that here.
|
18266
|
+
{
|
18267
|
+
pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
|
18268
|
+
|
18269
|
+
if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
|
18270
|
+
pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
|
18271
|
+
pm_multi_target_node_targets_append(parser, multi_target, statement);
|
18272
|
+
|
18273
|
+
statement = (pm_node_t *) multi_target;
|
18274
|
+
statements->body.nodes[statements->body.size - 1] = statement;
|
18275
|
+
}
|
18276
|
+
|
18277
|
+
if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
|
18278
|
+
const uint8_t *offset = statement->location.end;
|
18279
|
+
pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
|
18280
|
+
pm_node_t *value = (pm_node_t *) pm_missing_node_create(parser, offset, offset);
|
18281
|
+
|
18282
|
+
statement = (pm_node_t *) pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value);
|
18283
|
+
statements->body.nodes[statements->body.size - 1] = statement;
|
18284
|
+
|
18285
|
+
pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
|
18286
|
+
}
|
18287
|
+
}
|
18288
|
+
|
18190
18289
|
pop_block_exits(parser, previous_block_exits);
|
18191
18290
|
pm_node_list_free(¤t_block_exits);
|
18192
18291
|
|
@@ -18442,10 +18541,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18442
18541
|
case PM_TOKEN_HEREDOC_START: {
|
18443
18542
|
// Here we have found a heredoc. We'll parse it and add it to the
|
18444
18543
|
// list of strings.
|
18445
|
-
|
18446
|
-
|
18447
|
-
|
18448
|
-
|
18544
|
+
assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
|
18545
|
+
pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
|
18546
|
+
|
18547
|
+
size_t common_whitespace = (size_t) -1;
|
18548
|
+
parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
|
18449
18549
|
|
18450
18550
|
parser_lex(parser);
|
18451
18551
|
pm_token_t opening = parser->previous;
|
@@ -18456,10 +18556,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18456
18556
|
if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
|
18457
18557
|
// If we get here, then we have an empty heredoc. We'll create
|
18458
18558
|
// an empty content token and return an empty string node.
|
18459
|
-
expect1_heredoc_term(parser, lex_mode);
|
18559
|
+
expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
|
18460
18560
|
pm_token_t content = parse_strings_empty_content(parser->previous.start);
|
18461
18561
|
|
18462
|
-
if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18562
|
+
if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18463
18563
|
node = (pm_node_t *) pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
|
18464
18564
|
} else {
|
18465
18565
|
node = (pm_node_t *) pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY);
|
@@ -18486,18 +18586,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18486
18586
|
cast->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->current);
|
18487
18587
|
cast->base.location = cast->opening_loc;
|
18488
18588
|
|
18489
|
-
if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18589
|
+
if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18490
18590
|
assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
|
18491
18591
|
cast->base.type = PM_X_STRING_NODE;
|
18492
18592
|
}
|
18493
18593
|
|
18494
|
-
size_t common_whitespace
|
18495
|
-
if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
|
18594
|
+
if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
|
18496
18595
|
parse_heredoc_dedent_string(&cast->unescaped, common_whitespace);
|
18497
18596
|
}
|
18498
18597
|
|
18499
18598
|
node = (pm_node_t *) cast;
|
18500
|
-
expect1_heredoc_term(parser, lex_mode);
|
18599
|
+
expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
|
18501
18600
|
} else {
|
18502
18601
|
// If we get here, then we have multiple parts in the heredoc,
|
18503
18602
|
// so we'll need to create an interpolated string node to hold
|
@@ -18511,15 +18610,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18511
18610
|
}
|
18512
18611
|
}
|
18513
18612
|
|
18514
|
-
size_t common_whitespace = lex_mode->as.heredoc.common_whitespace;
|
18515
|
-
|
18516
18613
|
// Now that we have all of the parts, create the correct type of
|
18517
18614
|
// interpolated node.
|
18518
|
-
if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18615
|
+
if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18519
18616
|
pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
|
18520
18617
|
cast->parts = parts;
|
18521
18618
|
|
18522
|
-
expect1_heredoc_term(parser, lex_mode);
|
18619
|
+
expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
|
18523
18620
|
pm_interpolated_xstring_node_closing_set(cast, &parser->previous);
|
18524
18621
|
|
18525
18622
|
cast->base.location = cast->opening_loc;
|
@@ -18528,7 +18625,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18528
18625
|
pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
|
18529
18626
|
pm_node_list_free(&parts);
|
18530
18627
|
|
18531
|
-
expect1_heredoc_term(parser, lex_mode);
|
18628
|
+
expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
|
18532
18629
|
pm_interpolated_string_node_closing_set(cast, &parser->previous);
|
18533
18630
|
|
18534
18631
|
cast->base.location = cast->opening_loc;
|
@@ -18537,9 +18634,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
|
|
18537
18634
|
|
18538
18635
|
// If this is a heredoc that is indented with a ~, then we need
|
18539
18636
|
// to dedent each line by the common leading whitespace.
|
18540
|
-
if (indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
|
18637
|
+
if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
|
18541
18638
|
pm_node_list_t *nodes;
|
18542
|
-
if (quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18639
|
+
if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
|
18543
18640
|
nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
|
18544
18641
|
} else {
|
18545
18642
|
nodes = &((pm_interpolated_string_node_t *) node)->parts;
|
@@ -21684,8 +21781,11 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
|
|
21684
21781
|
// Otherwise we'll look and see if the next token can be parsed as an infix
|
21685
21782
|
// operator. If it can, then we'll parse it using parse_expression_infix.
|
21686
21783
|
pm_binding_powers_t current_binding_powers;
|
21784
|
+
pm_token_type_t current_token_type;
|
21785
|
+
|
21687
21786
|
while (
|
21688
|
-
|
21787
|
+
current_token_type = parser->current.type,
|
21788
|
+
current_binding_powers = pm_binding_powers[current_token_type],
|
21689
21789
|
binding_power <= current_binding_powers.left &&
|
21690
21790
|
current_binding_powers.binary
|
21691
21791
|
) {
|
@@ -21726,6 +21826,13 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
|
|
21726
21826
|
// If the operator is nonassoc and we should not be able to parse the
|
21727
21827
|
// upcoming infix operator, break.
|
21728
21828
|
if (current_binding_powers.nonassoc) {
|
21829
|
+
// If this is a non-assoc operator and we are about to parse the
|
21830
|
+
// exact same operator, then we need to add an error.
|
21831
|
+
if (match1(parser, current_token_type)) {
|
21832
|
+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
|
21833
|
+
break;
|
21834
|
+
}
|
21835
|
+
|
21729
21836
|
// If this is an endless range, then we need to reject a couple of
|
21730
21837
|
// additional operators because it violates the normal operator
|
21731
21838
|
// precedence rules. Those patterns are:
|
@@ -21735,7 +21842,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
|
|
21735
21842
|
//
|
21736
21843
|
if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
|
21737
21844
|
if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
|
21738
|
-
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(
|
21845
|
+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
|
21739
21846
|
break;
|
21740
21847
|
}
|
21741
21848
|
|
@@ -21857,6 +21964,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
|
|
21857
21964
|
));
|
21858
21965
|
|
21859
21966
|
pm_arguments_node_arguments_append(arguments, (pm_node_t *) keywords);
|
21967
|
+
pm_node_flag_set((pm_node_t *) arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
|
21860
21968
|
}
|
21861
21969
|
|
21862
21970
|
pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
|