aboutsummaryrefslogtreecommitdiffstats
path: root/yarp
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2023-09-11 12:32:25 -0400
committergit <svn-admin@ruby-lang.org>2023-09-11 21:42:03 +0000
commitb7ffa74d583100bb41b43bfdc8d883aea2e1fcad (patch)
treed06cdfa75b81a48efd3326ee1b8f68222f90c42d /yarp
parentaa69bd841aa5c35429e0dc08f5955a0dbd9176e5 (diff)
downloadruby-b7ffa74d583100bb41b43bfdc8d883aea2e1fcad.tar.gz
[ruby/yarp] Introduce MatchLastLineNode and InterpolatedMatchLastLineNode
These are replacements for regular expressions when they are used alone as the predicate of a conditional. That's because they are significantly different from a regular expression because they are not evaluated for truthyness, but instead evaluated as a match against the last line read by an IO object. https://github.com/ruby/yarp/commit/0f1c7780e8
Diffstat (limited to 'yarp')
-rw-r--r--yarp/config.yml39
-rw-r--r--yarp/yarp.c58
2 files changed, 80 insertions, 17 deletions
diff --git a/yarp/config.yml b/yarp/config.yml
index 1d96410fd5..ae63472bd7 100644
--- a/yarp/config.yml
+++ b/yarp/config.yml
@@ -1488,6 +1488,25 @@ nodes:
1
^
+ - name: InterpolatedMatchLastLineNode
+ fields:
+ - name: opening_loc
+ type: location
+ - name: parts
+ type: node[]
+ - name: closing_loc
+ type: location
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ newline: parts
+ comment: |
+ Represents a regular expression literal that contains interpolation that
+ is being used in the predicate of a conditional to implicitly match
+ against the last line read by an IO object.
+
+ if /foo #{bar} baz/ then end
+ ^^^^^^^^^^^^^^^^
- name: InterpolatedRegularExpressionNode
fields:
- name: opening_loc
@@ -1702,6 +1721,26 @@ nodes:
foo = 1
^^^^^^^
+ - name: MatchLastLineNode
+ fields:
+ - name: opening_loc
+ type: location
+ - name: content_loc
+ type: location
+ - name: closing_loc
+ type: location
+ - name: unescaped
+ type: string
+ - name: flags
+ type: flags
+ kind: RegularExpressionFlags
+ comment: |
+ Represents a regular expression literal used in the predicate of a
+ conditional to implicitly match against the last line read by an IO
+ object.
+
+ if /foo/i then end
+ ^^^^^^
- name: MatchPredicateNode
fields:
- name: value
diff --git a/yarp/yarp.c b/yarp/yarp.c
index 820361e25a..b2fecf9c0a 100644
--- a/yarp/yarp.c
+++ b/yarp/yarp.c
@@ -450,20 +450,27 @@ yp_parser_optional_constant_id_token(yp_parser_t *parser, const yp_token_t *toke
return token->type == YP_TOKEN_NOT_PROVIDED ? 0 : yp_parser_constant_id_token(parser, token);
}
-// Mark any range nodes in this subtree as flipflops.
+// The predicate of conditional nodes can change what would otherwise be regular
+// nodes into specialized nodes. For example:
+//
+// if foo .. bar => RangeNode becomes FlipFlopNode
+// if foo and bar .. baz => RangeNode becomes FlipFlopNode
+// if /foo/ => RegularExpressionNode becomes MatchLastLineNode
+// if /foo #{bar}/ => InterpolatedRegularExpressionNode becomes InterpolatedMatchLastLineNode
+//
static void
-yp_flip_flop(yp_node_t *node) {
+yp_conditional_predicate(yp_node_t *node) {
switch (YP_NODE_TYPE(node)) {
case YP_AND_NODE: {
yp_and_node_t *cast = (yp_and_node_t *) node;
- yp_flip_flop(cast->left);
- yp_flip_flop(cast->right);
+ yp_conditional_predicate(cast->left);
+ yp_conditional_predicate(cast->right);
break;
}
case YP_OR_NODE: {
yp_or_node_t *cast = (yp_or_node_t *) node;
- yp_flip_flop(cast->left);
- yp_flip_flop(cast->right);
+ yp_conditional_predicate(cast->left);
+ yp_conditional_predicate(cast->right);
break;
}
case YP_PARENTHESES_NODE: {
@@ -471,7 +478,7 @@ yp_flip_flop(yp_node_t *node) {
if ((cast->body != NULL) && YP_NODE_TYPE_P(cast->body, YP_STATEMENTS_NODE)) {
yp_statements_node_t *statements = (yp_statements_node_t *) cast->body;
- if (statements->body.size == 1) yp_flip_flop(statements->body.nodes[0]);
+ if (statements->body.size == 1) yp_conditional_predicate(statements->body.nodes[0]);
}
break;
@@ -479,19 +486,36 @@ yp_flip_flop(yp_node_t *node) {
case YP_RANGE_NODE: {
yp_range_node_t *cast = (yp_range_node_t *) node;
if (cast->left) {
- yp_flip_flop(cast->left);
+ yp_conditional_predicate(cast->left);
}
if (cast->right) {
- yp_flip_flop(cast->right);
+ yp_conditional_predicate(cast->right);
}
// Here we change the range node into a flip flop node. We can do
// this since the nodes are exactly the same except for the type.
+ // We're only asserting against the size when we should probably
+ // assert against the entire layout, but we'll assume tests will
+ // catch this.
assert(sizeof(yp_range_node_t) == sizeof(yp_flip_flop_node_t));
node->type = YP_FLIP_FLOP_NODE;
break;
}
+ case YP_REGULAR_EXPRESSION_NODE:
+ // Here we change the regular expression node into a match last line
+ // node. We can do this since the nodes are exactly the same except
+ // for the type.
+ assert(sizeof(yp_regular_expression_node_t) == sizeof(yp_match_last_line_node_t));
+ node->type = YP_MATCH_LAST_LINE_NODE;
+ break;
+ case YP_INTERPOLATED_REGULAR_EXPRESSION_NODE:
+ // Here we change the interpolated regular expression node into an
+ // interpolated match last line node. We can do this since the nodes
+ // are exactly the same except for the type.
+ assert(sizeof(yp_interpolated_regular_expression_node_t) == sizeof(yp_interpolated_match_last_line_node_t));
+ node->type = YP_INTERPOLATED_MATCH_LAST_LINE_NODE;
+ break;
default:
break;
}
@@ -2562,7 +2586,7 @@ yp_if_node_create(yp_parser_t *parser,
yp_node_t *consequent,
const yp_token_t *end_keyword
) {
- yp_flip_flop(predicate);
+ yp_conditional_predicate(predicate);
yp_if_node_t *node = YP_ALLOC_NODE(parser, yp_if_node_t);
const uint8_t *end;
@@ -2598,7 +2622,7 @@ yp_if_node_create(yp_parser_t *parser,
// Allocate and initialize new IfNode node in the modifier form.
static yp_if_node_t *
yp_if_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *if_keyword, yp_node_t *predicate) {
- yp_flip_flop(predicate);
+ yp_conditional_predicate(predicate);
yp_if_node_t *node = YP_ALLOC_NODE(parser, yp_if_node_t);
yp_statements_node_t *statements = yp_statements_node_create(parser);
@@ -2626,7 +2650,7 @@ yp_if_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_t
// Allocate and initialize an if node from a ternary expression.
static yp_if_node_t *
yp_if_node_ternary_create(yp_parser_t *parser, yp_node_t *predicate, yp_node_t *true_expression, const yp_token_t *colon, yp_node_t *false_expression) {
- yp_flip_flop(predicate);
+ yp_conditional_predicate(predicate);
yp_statements_node_t *if_statements = yp_statements_node_create(parser);
yp_statements_node_body_append(if_statements, true_expression);
@@ -4326,7 +4350,7 @@ yp_undef_node_append(yp_undef_node_t *node, yp_node_t *name) {
// Allocate a new UnlessNode node.
static yp_unless_node_t *
yp_unless_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) {
- yp_flip_flop(predicate);
+ yp_conditional_predicate(predicate);
yp_unless_node_t *node = YP_ALLOC_NODE(parser, yp_unless_node_t);
const uint8_t *end;
@@ -4358,7 +4382,7 @@ yp_unless_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t
// Allocate and initialize new UnlessNode node in the modifier form.
static yp_unless_node_t *
yp_unless_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *unless_keyword, yp_node_t *predicate) {
- yp_flip_flop(predicate);
+ yp_conditional_predicate(predicate);
yp_unless_node_t *node = YP_ALLOC_NODE(parser, yp_unless_node_t);
yp_statements_node_t *statements = yp_statements_node_create(parser);
@@ -12027,7 +12051,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
arguments.closing_loc = YP_LOCATION_TOKEN_VALUE(&parser->previous);
} else {
receiver = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, YP_ERR_NOT_EXPRESSION);
- yp_flip_flop(receiver);
+ yp_conditional_predicate(receiver);
if (!parser->recovering) {
accept(parser, YP_TOKEN_NEWLINE);
@@ -12037,7 +12061,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
}
} else {
receiver = parse_expression(parser, YP_BINDING_POWER_DEFINED, YP_ERR_NOT_EXPRESSION);
- yp_flip_flop(receiver);
+ yp_conditional_predicate(receiver);
}
return (yp_node_t *) yp_call_node_not_create(parser, receiver, &message, &arguments);
@@ -12633,7 +12657,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, YP_ERR_UNARY_RECEIVER_BANG);
yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "!");
- yp_flip_flop(receiver);
+ yp_conditional_predicate(receiver);
return (yp_node_t *) node;
}
case YP_TOKEN_TILDE: {