diff --git a/test/yarp/location_test.rb b/test/yarp/location_test.rb index 3ef3da3faf..1c9aa104a5 100644 --- a/test/yarp/location_test.rb +++ b/test/yarp/location_test.rb @@ -410,6 +410,20 @@ module YARP assert_location(ImaginaryNode, "1ri") end + def test_ImplicitNode + assert_location(ImplicitNode, "{ foo: }", 2...6) do |node| + node.elements.first.value + end + + assert_location(ImplicitNode, "{ Foo: }", 2..6) do |node| + node.elements.first.value + end + + assert_location(ImplicitNode, "foo = 1; { foo: }", 11..15) do |node| + node.elements.first.value + end + end + def test_InNode assert_location(InNode, "case foo; in bar; end", 10...16) do |node| node.conditions.first diff --git a/test/yarp/snapshots/if.txt b/test/yarp/snapshots/if.txt index ebafafe114..7feb336de8 100644 --- a/test/yarp/snapshots/if.txt +++ b/test/yarp/snapshots/if.txt @@ -240,7 +240,19 @@ │ │ │ │ ├── value_loc: (248...249) = "b" │ │ │ │ ├── closing_loc: (249...250) = ":" │ │ │ │ └── unescaped: "b" - │ │ │ ├── value: ∅ + │ │ │ ├── value: + │ │ │ │ @ ImplicitNode (location: (248...250)) + │ │ │ │ └── value: + │ │ │ │ @ CallNode (location: (248...250)) + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── message_loc: (248...249) = "b" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── block: ∅ + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: "b" │ │ │ └── operator_loc: ∅ │ │ ├── closing_loc: ∅ │ │ ├── block: ∅ diff --git a/test/yarp/snapshots/rescue.txt b/test/yarp/snapshots/rescue.txt index 3176c864cf..40b84f7bfa 100644 --- a/test/yarp/snapshots/rescue.txt +++ b/test/yarp/snapshots/rescue.txt @@ -340,7 +340,19 @@ │ │ │ │ ├── value_loc: (303...304) = "b" │ │ │ │ ├── closing_loc: (304...305) = ":" │ │ │ │ └── unescaped: "b" - │ │ │ ├── value: ∅ + │ │ │ ├── value: + │ │ │ │ @ ImplicitNode (location: (303...305)) + │ │ │ │ └── value: + │ │ │ │ @ CallNode (location: (303...305)) + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── message_loc: (303...304) = "b" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── block: ∅ + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: "b" │ │ │ └── operator_loc: ∅ │ │ ├── closing_loc: ∅ │ │ ├── block: ∅ diff --git a/test/yarp/snapshots/seattlerb/assoc__bare.txt b/test/yarp/snapshots/seattlerb/assoc__bare.txt index 87ae0f72aa..fa86e4fa2a 100644 --- a/test/yarp/snapshots/seattlerb/assoc__bare.txt +++ b/test/yarp/snapshots/seattlerb/assoc__bare.txt @@ -13,6 +13,18 @@ │ │ ├── value_loc: (2...3) = "y" │ │ ├── closing_loc: (3...4) = ":" │ │ └── unescaped: "y" - │ ├── value: ∅ + │ ├── value: + │ │ @ ImplicitNode (location: (2...4)) + │ │ └── value: + │ │ @ CallNode (location: (2...4)) + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── message_loc: (2...3) = "y" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── block: ∅ + │ │ ├── flags: ∅ + │ │ └── name: "y" │ └── operator_loc: ∅ └── closing_loc: (5...6) = "}" diff --git a/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt b/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt index 09d4853a51..9b6d43af07 100644 --- a/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt +++ b/test/yarp/snapshots/whitequark/hash_pair_value_omission.txt @@ -13,7 +13,11 @@ │ │ │ ├── value_loc: (1...4) = "BAR" │ │ │ ├── closing_loc: (4...5) = ":" │ │ │ └── unescaped: "BAR" - │ │ ├── value: ∅ + │ │ ├── value: + │ │ │ @ ImplicitNode (location: (1...5)) + │ │ │ └── value: + │ │ │ @ ConstantReadNode (location: (1...5)) + │ │ │ └── name: :BAR │ │ └── operator_loc: ∅ │ └── closing_loc: (5...6) = "}" ├── @ HashNode (location: (8...16)) @@ -26,7 +30,19 @@ │ │ │ │ ├── value_loc: (9...10) = "a" │ │ │ │ ├── closing_loc: (10...11) = ":" │ │ │ │ └── unescaped: "a" - │ │ │ ├── value: ∅ + │ │ │ ├── value: + │ │ │ │ @ ImplicitNode (location: (9...11)) + │ │ │ │ └── value: + │ │ │ │ @ CallNode (location: (9...11)) + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── message_loc: (9...10) = "a" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── block: ∅ + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: "a" │ │ │ └── operator_loc: ∅ │ │ └── @ AssocNode (location: (13...15)) │ │ ├── key: @@ -35,7 +51,19 @@ │ │ │ ├── value_loc: (13...14) = "b" │ │ │ ├── closing_loc: (14...15) = ":" │ │ │ └── unescaped: "b" - │ │ ├── value: ∅ + │ │ ├── value: + │ │ │ @ ImplicitNode (location: (13...15)) + │ │ │ └── value: + │ │ │ @ CallNode (location: (13...15)) + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── message_loc: (13...14) = "b" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── block: ∅ + │ │ │ ├── flags: ∅ + │ │ │ └── name: "b" │ │ └── operator_loc: ∅ │ └── closing_loc: (15...16) = "}" └── @ HashNode (location: (18...25)) @@ -48,6 +76,18 @@ │ │ ├── value_loc: (19...23) = "puts" │ │ ├── closing_loc: (23...24) = ":" │ │ └── unescaped: "puts" - │ ├── value: ∅ + │ ├── value: + │ │ @ ImplicitNode (location: (19...24)) + │ │ └── value: + │ │ @ CallNode (location: (19...24)) + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── message_loc: (19...23) = "puts" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── block: ∅ + │ │ ├── flags: ∅ + │ │ └── name: "puts" │ └── operator_loc: ∅ └── closing_loc: (24...25) = "}" diff --git a/test/yarp/snapshots/whitequark/keyword_argument_omission.txt b/test/yarp/snapshots/whitequark/keyword_argument_omission.txt index 6808bf12f7..bbcb0d36dc 100644 --- a/test/yarp/snapshots/whitequark/keyword_argument_omission.txt +++ b/test/yarp/snapshots/whitequark/keyword_argument_omission.txt @@ -20,7 +20,19 @@ │ │ │ ├── value_loc: (4...5) = "a" │ │ │ ├── closing_loc: (5...6) = ":" │ │ │ └── unescaped: "a" - │ │ ├── value: ∅ + │ │ ├── value: + │ │ │ @ ImplicitNode (location: (4...6)) + │ │ │ └── value: + │ │ │ @ CallNode (location: (4...6)) + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── message_loc: (4...5) = "a" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── block: ∅ + │ │ │ ├── flags: ∅ + │ │ │ └── name: "a" │ │ └── operator_loc: ∅ │ └── @ AssocNode (location: (8...10)) │ ├── key: @@ -29,7 +41,19 @@ │ │ ├── value_loc: (8...9) = "b" │ │ ├── closing_loc: (9...10) = ":" │ │ └── unescaped: "b" - │ ├── value: ∅ + │ ├── value: + │ │ @ ImplicitNode (location: (8...10)) + │ │ └── value: + │ │ @ CallNode (location: (8...10)) + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── message_loc: (8...9) = "b" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── block: ∅ + │ │ ├── flags: ∅ + │ │ └── name: "b" │ └── operator_loc: ∅ ├── closing_loc: (10...11) = ")" ├── block: ∅ diff --git a/yarp/config.yml b/yarp/config.yml index b446742eab..fbaea52f4b 100644 --- a/yarp/config.yml +++ b/yarp/config.yml @@ -1413,6 +1413,19 @@ nodes: 1.0i ^^^^ + - name: ImplicitNode + fields: + - name: value + type: node + comment: | + Represents a node that is implicitly being added to the tree but doesn't + correspond directly to a node in the source. + + { foo: } + ^^^^ + + { Foo: } + ^^^^ - name: InNode fields: - name: pattern diff --git a/yarp/yarp.c b/yarp/yarp.c index d271627e2f..3eabc55600 100644 --- a/yarp/yarp.c +++ b/yarp/yarp.c @@ -2737,6 +2737,22 @@ yp_else_node_end_keyword_loc_set(yp_else_node_t *node, const yp_token_t *keyword node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword); } +// Allocate and initialize a new ImplicitNode node. +static yp_implicit_node_t * +yp_implicit_node_create(yp_parser_t *parser, yp_node_t *value) { + yp_implicit_node_t *node = YP_ALLOC_NODE(parser, yp_implicit_node_t); + + *node = (yp_implicit_node_t) { + { + .type = YP_IMPLICIT_NODE, + .location = value->location + }, + .value = value + }; + + return node; +} + // Allocate and initialize a new IntegerNode node. static yp_integer_node_t * yp_integer_node_create(yp_parser_t *parser, yp_node_flags_t base, const yp_token_t *token) { @@ -8715,14 +8731,32 @@ parse_assocs(yp_parser_t *parser, yp_node_t *node) { break; } case YP_TOKEN_LABEL: { + yp_token_t label = parser->current; parser_lex(parser); - yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); + yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &label); yp_token_t operator = not_provided(parser); yp_node_t *value = NULL; if (token_begins_expression_p(parser->current.type)) { value = parse_expression(parser, YP_BINDING_POWER_DEFINED, YP_ERR_HASH_EXPRESSION_AFTER_LABEL); + } else { + if (parser->encoding.isupper_char(label.start, (label.end - 1) - label.start)) { + yp_token_t constant = { .type = YP_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 }; + value = (yp_node_t *) yp_constant_read_node_create(parser, &constant); + } else { + int depth = yp_parser_local_depth(parser, &((yp_token_t) { .type = YP_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 })); + yp_token_t identifier = { .type = YP_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; + + if (depth == -1) { + value = (yp_node_t *) yp_call_node_variable_call_create(parser, &identifier); + } else { + value = (yp_node_t *) yp_local_variable_read_node_create(parser, &identifier, (uint32_t) depth); + } + } + + value->location.end++; + value = (yp_node_t *) yp_implicit_node_create(parser, value); } element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value);