Skip to content

Commit 893b73f

Browse files
committed
Implement annotation parser
1 parent 044577e commit 893b73f

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

src/parser.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,43 @@ static bool parse_type_list(rbs_parser_t *parser, enum RBSTokenType eol, rbs_nod
269269
return true;
270270
}
271271

272+
/*
273+
type_list_with_commas ::= {} type `,` ... <`,`> eol
274+
| {} type `,` ... `,` <type> eol
275+
*/
276+
NODISCARD
277+
static bool parse_type_list_with_commas(rbs_parser_t *parser, enum RBSTokenType eol, rbs_node_list_t *types, rbs_location_list_t *comma_locations) {
278+
while (true) {
279+
rbs_node_t *type;
280+
CHECK_PARSE(rbs_parse_type(parser, &type));
281+
rbs_node_list_append(types, type);
282+
283+
if (parser->next_token.type == pCOMMA) {
284+
rbs_location_t *comma_loc = rbs_location_new(ALLOCATOR(), parser->next_token.range);
285+
rbs_location_list_append(comma_locations, comma_loc);
286+
rbs_parser_advance(parser);
287+
288+
if (parser->next_token.type == eol) {
289+
// Handle trailing comma - for type applications, this is an error
290+
if (eol == pRBRACKET) {
291+
rbs_parser_set_error(parser, parser->next_token, true, "unexpected trailing comma");
292+
return false;
293+
}
294+
break;
295+
}
296+
} else {
297+
if (parser->next_token.type == eol) {
298+
break;
299+
} else {
300+
rbs_parser_set_error(parser, parser->next_token, true, "comma delimited type list is expected");
301+
return false;
302+
}
303+
}
304+
}
305+
306+
return true;
307+
}
308+
272309
static bool is_keyword_token(enum RBSTokenType type) {
273310
switch (type) {
274311
case tLIDENT:
@@ -3750,6 +3787,43 @@ static bool parse_inline_trailing_annotation(rbs_parser_t *parser, rbs_ast_ruby_
37503787
);
37513788
return true;
37523789
}
3790+
case pLBRACKET: {
3791+
rbs_parser_advance(parser);
3792+
3793+
rbs_node_list_t *type_args = rbs_node_list_new(ALLOCATOR());
3794+
rbs_location_list_t *comma_locations = rbs_location_list_new(ALLOCATOR());
3795+
3796+
// Check for empty type args
3797+
if (parser->next_token.type == pRBRACKET) {
3798+
rbs_parser_set_error(parser, parser->next_token, true, "type application cannot be empty");
3799+
return false;
3800+
}
3801+
3802+
// Parse type list with comma tracking
3803+
CHECK_PARSE(parse_type_list_with_commas(parser, pRBRACKET, type_args, comma_locations));
3804+
3805+
rbs_range_t close_bracket_range = parser->next_token.range;
3806+
rbs_location_t *close_bracket_loc = rbs_location_new(ALLOCATOR(), close_bracket_range);
3807+
rbs_parser_advance(parser); // consume ]
3808+
3809+
rbs_range_t full_range = {
3810+
.start = prefix_range.start,
3811+
.end = close_bracket_range.end
3812+
};
3813+
3814+
rbs_location_t *full_loc = rbs_location_new(ALLOCATOR(), full_range);
3815+
rbs_location_t *prefix_loc = rbs_location_new(ALLOCATOR(), prefix_range);
3816+
3817+
*annotation = (rbs_ast_ruby_annotations_t *) rbs_ast_ruby_annotations_type_application_annotation_new(
3818+
ALLOCATOR(),
3819+
full_loc,
3820+
prefix_loc,
3821+
type_args,
3822+
close_bracket_loc,
3823+
comma_locations
3824+
);
3825+
return true;
3826+
}
37533827
default: {
37543828
rbs_parser_set_error(parser, parser->next_token, true, "unexpected token for inline trailing annotation");
37553829
return false;

test/rbs/inline_annotation_parsing_test.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,45 @@ def test_parse__return
115115
assert_equal "-- some comment here", annot.comment_location.source
116116
end
117117
end
118+
119+
def test_parse__type_application
120+
Parser.parse_inline_trailing_annotation("[String]", 0...).tap do |annot|
121+
assert_instance_of AST::Ruby::Annotations::TypeApplicationAnnotation, annot
122+
assert_equal "[String]", annot.location.source
123+
assert_equal "[", annot.prefix_location.source
124+
assert_equal "]", annot.close_bracket_location.source
125+
assert_equal 1, annot.type_args.size
126+
assert_equal "String", annot.type_args[0].location.source
127+
assert_empty annot.comma_locations
128+
end
129+
130+
Parser.parse_inline_trailing_annotation("[String, Integer]", 0...).tap do |annot|
131+
assert_instance_of AST::Ruby::Annotations::TypeApplicationAnnotation, annot
132+
assert_equal "[String, Integer]", annot.location.source
133+
assert_equal "[", annot.prefix_location.source
134+
assert_equal "]", annot.close_bracket_location.source
135+
assert_equal 2, annot.type_args.size
136+
assert_equal "String", annot.type_args[0].location.source
137+
assert_equal "Integer", annot.type_args[1].location.source
138+
assert_equal [","], annot.comma_locations.map(&:source)
139+
end
140+
end
141+
142+
def test_error__type_application
143+
assert_raises RBS::ParsingError do
144+
Parser.parse_inline_trailing_annotation("[String", 0...)
145+
end
146+
147+
assert_raises RBS::ParsingError do
148+
Parser.parse_inline_trailing_annotation("[]", 0...)
149+
end
150+
151+
assert_raises RBS::ParsingError do
152+
Parser.parse_inline_trailing_annotation("[String,]", 0...)
153+
end
154+
155+
assert_raises RBS::ParsingError do
156+
Parser.parse_inline_trailing_annotation("[,String]", 0...)
157+
end
158+
end
118159
end

0 commit comments

Comments
 (0)