[ruby/yarp] Support parsing numbered parameters

ffc8f35e56
This commit is contained in:
Kevin Newton 2023-09-13 11:16:56 -04:00 committed by git
parent 236fe914af
commit 6031ab18c7
9 changed files with 103 additions and 167 deletions

View file

@ -440,10 +440,6 @@ module YARP
# arguments. We get rid of that here.
names = names.grep_v(Integer)
# TODO: We don't support numbered local variables yet, so we get rid
# of those here.
names = names.grep_v(/^_\d$/)
# For some reason, CRuby occasionally pushes this special local
# variable when there are splat arguments. We get rid of that here.
names = names.grep_v(:"#arg_rest")

View file

@ -1337,39 +1337,25 @@
├── closing_loc: ∅
├── block:
│ @ BlockNode (location: (724...737))
│ ├── locals: []
│ ├── locals: [:_1, :_2]
│ ├── parameters: ∅
│ ├── body:
│ │ @ StatementsNode (location: (728...735))
│ │ └── body: (length: 1)
│ │ └── @ CallNode (location: (728...735))
│ │ ├── receiver:
│ │ │ @ CallNode (location: (728...730))
│ │ │ ├── receiver: ∅
│ │ │ ├── call_operator_loc: ∅
│ │ │ ├── message_loc: (728...730) = "_1"
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ ├── block: ∅
│ │ │ ├── flags: variable_call
│ │ │ └── name: "_1"
│ │ │ @ LocalVariableReadNode (location: (728...730))
│ │ │ ├── name: :_1
│ │ │ └── depth: 0
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (731...732) = "+"
│ │ ├── opening_loc: ∅
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (733...735))
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ CallNode (location: (733...735))
│ │ │ ├── receiver: ∅
│ │ │ ├── call_operator_loc: ∅
│ │ │ ├── message_loc: (733...735) = "_2"
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ ├── block: ∅
│ │ │ ├── flags: variable_call
│ │ │ └── name: "_2"
│ │ │ └── @ LocalVariableReadNode (location: (733...735))
│ │ │ ├── name: :_2
│ │ │ └── depth: 0
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: ∅

View file

@ -4,7 +4,7 @@
@ StatementsNode (location: (0...22))
└── body: (length: 2)
├── @ LambdaNode (location: (0...16))
│ ├── locals: []
│ ├── locals: [:_1, :_2]
│ ├── operator_loc: (0...2) = "->"
│ ├── opening_loc: (3...4) = "{"
│ ├── closing_loc: (15...16) = "}"
@ -14,32 +14,18 @@
│ └── body: (length: 1)
│ └── @ CallNode (location: (7...14))
│ ├── receiver:
│ │ @ CallNode (location: (7...9))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (7...9) = "_1"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_1"
│ │ @ LocalVariableReadNode (location: (7...9))
│ │ ├── name: :_1
│ │ └── depth: 0
│ ├── call_operator_loc: ∅
│ ├── message_loc: (10...11) = "+"
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (12...14))
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (12...14))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (12...14) = "_2"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_2"
│ │ └── @ LocalVariableReadNode (location: (12...14))
│ │ ├── name: :_2
│ │ └── depth: 0
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅

View file

@ -4,7 +4,7 @@
@ StatementsNode (location: (0...65))
└── body: (length: 4)
├── @ LambdaNode (location: (0...17))
│ ├── locals: []
│ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
│ ├── operator_loc: (0...2) = "->"
│ ├── opening_loc: (3...5) = "do"
│ ├── closing_loc: (14...17) = "end"
@ -14,38 +14,24 @@
│ └── body: (length: 1)
│ └── @ CallNode (location: (6...13))
│ ├── receiver:
│ │ @ CallNode (location: (6...8))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (6...8) = "_1"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_1"
│ │ @ LocalVariableReadNode (location: (6...8))
│ │ ├── name: :_1
│ │ └── depth: 0
│ ├── call_operator_loc: ∅
│ ├── message_loc: (9...10) = "+"
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (11...13))
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (11...13))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (11...13) = "_9"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_9"
│ │ └── @ LocalVariableReadNode (location: (11...13))
│ │ ├── name: :_9
│ │ └── depth: 0
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
│ └── name: "+"
├── @ LambdaNode (location: (19...32))
│ ├── locals: []
│ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
│ ├── operator_loc: (19...21) = "->"
│ ├── opening_loc: (22...23) = "{"
│ ├── closing_loc: (31...32) = "}"
@ -55,32 +41,18 @@
│ └── body: (length: 1)
│ └── @ CallNode (location: (24...31))
│ ├── receiver:
│ │ @ CallNode (location: (24...26))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (24...26) = "_1"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_1"
│ │ @ LocalVariableReadNode (location: (24...26))
│ │ ├── name: :_1
│ │ └── depth: 0
│ ├── call_operator_loc: ∅
│ ├── message_loc: (27...28) = "+"
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (29...31))
│ │ └── arguments: (length: 1)
│ │ └── @ CallNode (location: (29...31))
│ │ ├── receiver: ∅
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (29...31) = "_9"
│ │ ├── opening_loc: ∅
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: variable_call
│ │ └── name: "_9"
│ │ └── @ LocalVariableReadNode (location: (29...31))
│ │ ├── name: :_9
│ │ └── depth: 0
│ ├── closing_loc: ∅
│ ├── block: ∅
│ ├── flags: ∅
@ -94,39 +66,25 @@
│ ├── closing_loc: ∅
│ ├── block:
│ │ @ BlockNode (location: (36...50))
│ │ ├── locals: []
│ │ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
│ │ ├── parameters: ∅
│ │ ├── body:
│ │ │ @ StatementsNode (location: (39...46))
│ │ │ └── body: (length: 1)
│ │ │ └── @ CallNode (location: (39...46))
│ │ │ ├── receiver:
│ │ │ │ @ CallNode (location: (39...41))
│ │ │ │ ├── receiver: ∅
│ │ │ │ ├── call_operator_loc: ∅
│ │ │ │ ├── message_loc: (39...41) = "_1"
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── arguments: ∅
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ ├── block: ∅
│ │ │ │ ├── flags: variable_call
│ │ │ │ └── name: "_1"
│ │ │ │ @ LocalVariableReadNode (location: (39...41))
│ │ │ │ ├── name: :_1
│ │ │ │ └── depth: 0
│ │ │ ├── call_operator_loc: ∅
│ │ │ ├── message_loc: (42...43) = "+"
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments:
│ │ │ │ @ ArgumentsNode (location: (44...46))
│ │ │ │ └── arguments: (length: 1)
│ │ │ │ └── @ CallNode (location: (44...46))
│ │ │ │ ├── receiver: ∅
│ │ │ │ ├── call_operator_loc: ∅
│ │ │ │ ├── message_loc: (44...46) = "_9"
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── arguments: ∅
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ ├── block: ∅
│ │ │ │ ├── flags: variable_call
│ │ │ │ └── name: "_9"
│ │ │ │ └── @ LocalVariableReadNode (location: (44...46))
│ │ │ │ ├── name: :_9
│ │ │ │ └── depth: 0
│ │ │ ├── closing_loc: ∅
│ │ │ ├── block: ∅
│ │ │ ├── flags: ∅
@ -144,39 +102,25 @@
├── closing_loc: ∅
├── block:
│ @ BlockNode (location: (54...65))
│ ├── locals: []
│ ├── locals: [:_1, :_2, :_3, :_4, :_5, :_6, :_7, :_8, :_9]
│ ├── parameters: ∅
│ ├── body:
│ │ @ StatementsNode (location: (56...63))
│ │ └── body: (length: 1)
│ │ └── @ CallNode (location: (56...63))
│ │ ├── receiver:
│ │ │ @ CallNode (location: (56...58))
│ │ │ ├── receiver: ∅
│ │ │ ├── call_operator_loc: ∅
│ │ │ ├── message_loc: (56...58) = "_1"
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ ├── block: ∅
│ │ │ ├── flags: variable_call
│ │ │ └── name: "_1"
│ │ │ @ LocalVariableReadNode (location: (56...58))
│ │ │ ├── name: :_1
│ │ │ └── depth: 0
│ │ ├── call_operator_loc: ∅
│ │ ├── message_loc: (59...60) = "+"
│ │ ├── opening_loc: ∅
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (61...63))
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ CallNode (location: (61...63))
│ │ │ ├── receiver: ∅
│ │ │ ├── call_operator_loc: ∅
│ │ │ ├── message_loc: (61...63) = "_9"
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ ├── block: ∅
│ │ │ ├── flags: variable_call
│ │ │ └── name: "_9"
│ │ │ └── @ LocalVariableReadNode (location: (61...63))
│ │ │ ├── name: :_9
│ │ │ └── depth: 0
│ │ ├── closing_loc: ∅
│ │ ├── block: ∅
│ │ ├── flags: ∅

View file

@ -28,7 +28,7 @@
│ │ │ │ │ ├── operator_loc: (7...8) = "="
│ │ │ │ │ └── value:
│ │ │ │ │ @ LambdaNode (location: (9...15))
│ │ │ │ │ ├── locals: []
│ │ │ │ │ ├── locals: [:_1]
│ │ │ │ │ ├── operator_loc: (9...11) = "->"
│ │ │ │ │ ├── opening_loc: (11...12) = "{"
│ │ │ │ │ ├── closing_loc: (14...15) = "}"
@ -36,16 +36,9 @@
│ │ │ │ │ └── body:
│ │ │ │ │ @ StatementsNode (location: (12...14))
│ │ │ │ │ └── body: (length: 1)
│ │ │ │ │ └── @ CallNode (location: (12...14))
│ │ │ │ │ ├── receiver: ∅
│ │ │ │ │ ├── call_operator_loc: ∅
│ │ │ │ │ ├── message_loc: (12...14) = "_1"
│ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ ├── arguments: ∅
│ │ │ │ │ ├── closing_loc: ∅
│ │ │ │ │ ├── block: ∅
│ │ │ │ │ ├── flags: variable_call
│ │ │ │ │ └── name: "_1"
│ │ │ │ │ └── @ LocalVariableReadNode (location: (12...14))
│ │ │ │ │ ├── name: :_1
│ │ │ │ │ └── depth: 0
│ │ │ │ ├── rest: ∅
│ │ │ │ ├── posts: (length: 0)
│ │ │ │ ├── keywords: (length: 0)
@ -91,7 +84,7 @@
│ │ │ │ ├── name_loc: (27...29) = "a:"
│ │ │ │ └── value:
│ │ │ │ @ LambdaNode (location: (30...36))
│ │ │ │ ├── locals: []
│ │ │ │ ├── locals: [:_1]
│ │ │ │ ├── operator_loc: (30...32) = "->"
│ │ │ │ ├── opening_loc: (32...33) = "{"
│ │ │ │ ├── closing_loc: (35...36) = "}"
@ -99,16 +92,9 @@
│ │ │ │ └── body:
│ │ │ │ @ StatementsNode (location: (33...35))
│ │ │ │ └── body: (length: 1)
│ │ │ │ └── @ CallNode (location: (33...35))
│ │ │ │ ├── receiver: ∅
│ │ │ │ ├── call_operator_loc: ∅
│ │ │ │ ├── message_loc: (33...35) = "_1"
│ │ │ │ ├── opening_loc: ∅
│ │ │ │ ├── arguments: ∅
│ │ │ │ ├── closing_loc: ∅
│ │ │ │ ├── block: ∅
│ │ │ │ ├── flags: variable_call
│ │ │ │ └── name: "_1"
│ │ │ │ └── @ LocalVariableReadNode (location: (33...35))
│ │ │ │ ├── name: :_1
│ │ │ │ └── depth: 0
│ │ │ ├── keyword_rest: ∅
│ │ │ └── block: ∅
│ │ ├── locals: (length: 0)

View file

@ -184,8 +184,9 @@ static const char* const diagnostic_messages[YP_DIAGNOSTIC_ID_LEN] = {
[YP_ERR_MULTI_ASSIGN_MULTI_SPLATS] = "Multiple splats in multiple assignment",
[YP_ERR_NOT_EXPRESSION] = "Expected an expression after `not`",
[YP_ERR_NUMBER_LITERAL_UNDERSCORE] = "Number literal ending with a `_`",
[YP_ERR_OPERATOR_WRITE_BLOCK] = "Unexpected operator after a call with a block",
[YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED] = "Numbered parameters are not allowed alongside explicit parameters",
[YP_ERR_OPERATOR_MULTI_ASSIGN] = "Unexpected operator for a multiple assignment",
[YP_ERR_OPERATOR_WRITE_BLOCK] = "Unexpected operator after a call with a block",
[YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = "Unexpected multiple `**` splat parameters",
[YP_ERR_PARAMETER_BLOCK_MULTI] = "Multiple block parameters; only one block is allowed",
[YP_ERR_PARAMETER_NAME_REPEAT] = "Repeated parameter name",

View file

@ -150,6 +150,7 @@ typedef enum {
YP_ERR_MULTI_ASSIGN_MULTI_SPLATS,
YP_ERR_NOT_EXPRESSION,
YP_ERR_NUMBER_LITERAL_UNDERSCORE,
YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED,
YP_ERR_OPERATOR_MULTI_ASSIGN,
YP_ERR_OPERATOR_WRITE_BLOCK,
YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI,

View file

@ -107,10 +107,10 @@ yp_constant_pool_init(yp_constant_pool_t *pool, size_t capacity) {
}
// Insert a constant into a constant pool and return its index in the pool.
static size_t
yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
static inline yp_constant_id_t
yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t length, bool owned) {
if (pool->size >= (pool->capacity / 4 * 3)) {
if (!yp_constant_pool_resize(pool)) return pool->capacity;
if (!yp_constant_pool_resize(pool)) return 0;
}
size_t hash = yp_constant_pool_hash(start, length);
@ -122,7 +122,24 @@ yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t l
// same as the content we are trying to insert. If it is, then we can
// return the id of the existing constant.
if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
return index;
// Since we have found a match, we need to check if this is
// attempting to insert a shared or an owned constant. We want to
// prefer shared constants since they don't require allocations.
if (owned) {
// If we're attempting to insert an owned constant and we have
// an existing constant, then either way we don't want the given
// memory. Either it's duplicated with the existing constant or
// it's not necessary because we have a shared version.
free((void *) start);
} else if (constant->owned) {
// If we're attempting to insert a shared constant and the
// existing constant is owned, then we can free the owned
// constant and replace it with the shared constant.
free((void *) constant->start);
constant->start = start;
}
return constant->id;
}
index = (index + 1) % pool->capacity;
@ -131,22 +148,22 @@ yp_constant_pool_insert(yp_constant_pool_t *pool, const uint8_t *start, size_t l
pool->size++;
assert(pool->size < ((size_t) (1 << 31)));
pool->constants[index] = (yp_constant_t) {
*constant = (yp_constant_t) {
.id = (unsigned int) (pool->size & 0x7FFFFFFF),
.owned = owned & 0x1,
.start = start,
.length = length,
.hash = hash
};
return index;
return constant->id;
}
// Insert a constant into a constant pool. Returns the id of the constant, or 0
// if any potential calls to resize fail.
yp_constant_id_t
yp_constant_pool_insert_shared(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
size_t index = yp_constant_pool_insert(pool, start, length);
return index == pool->capacity ? 0 : ((yp_constant_id_t) pool->constants[index].id);
return yp_constant_pool_insert(pool, start, length, false);
}
// Insert a constant into a constant pool from memory that is now owned by the
@ -154,12 +171,7 @@ yp_constant_pool_insert_shared(yp_constant_pool_t *pool, const uint8_t *start, s
// resize fail.
yp_constant_id_t
yp_constant_pool_insert_owned(yp_constant_pool_t *pool, const uint8_t *start, size_t length) {
size_t index = yp_constant_pool_insert(pool, start, length);
if (index == pool->capacity) return 0;
yp_constant_t *constant = &pool->constants[index];
constant->owned = true;
return ((yp_constant_id_t) constant->id);
return yp_constant_pool_insert(pool, start, length, true);
}
// Free the memory associated with a constant pool.

View file

@ -10110,6 +10110,30 @@ parse_variable_call(yp_parser_t *parser) {
return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
}
if (!parser->current_scope->closed && token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
// Now that we know we have a numbered parameter, we need to check
// if it's allowed in this context. If it is, then we will create a
// local variable read. If it's not, then we'll create a normal call
// node but add an error.
if (parser->current_scope->explicit_params) {
yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, YP_ERR_NUMBERED_PARAMETER_NOT_ALLOWED);
} else {
uint8_t number = parser->previous.start[1];
uint8_t current = '1';
uint8_t *value;
while (current < number) {
value = malloc(2);
value[0] = '_';
value[1] = current++;
yp_parser_local_add_owned(parser, value, 2);
}
yp_parser_local_add_token(parser, &parser->previous);
return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, 0);
}
}
flags |= YP_CALL_NODE_FLAGS_VARIABLE_CALL;
}