Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions ext/rbs_extension/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,16 @@ static rbs_lexer_t *alloc_lexer_from_buffer(rbs_allocator_t *allocator, VALUE st
return lexer;
}

static rbs_parser_t *alloc_parser_from_buffer(VALUE buffer, int start_pos, int end_pos) {
// Build the parser options from the arguments the `_parse_*` entry points
// receive. The optional syntax these enable is not part of the public
// `RBS::Parser` API, so only the private entry points pass them through.
static rbs_parser_options_t parser_options(VALUE enable_forwarding_params) {
return (rbs_parser_options_t) {
.enable_forwarding_params = RB_TEST(enable_forwarding_params),
};
}

static rbs_parser_t *alloc_parser_from_buffer_with_options(VALUE buffer, int start_pos, int end_pos, rbs_parser_options_t options) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);

Expand All @@ -194,11 +203,12 @@ static rbs_parser_t *alloc_parser_from_buffer(VALUE buffer, int start_pos, int e
rb_encoding *encoding = rb_enc_get(string);
const char *encoding_name = rb_enc_name(encoding);

rbs_parser_t *parser = rbs_parser_new(
rbs_parser_t *parser = rbs_parser_new_with_options(
rbs_string_from_ruby_string(string),
rbs_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name))),
start_pos,
end_pos
end_pos,
options
);

if (parser == NULL) {
Expand All @@ -208,6 +218,10 @@ static rbs_parser_t *alloc_parser_from_buffer(VALUE buffer, int start_pos, int e
return parser;
}

static rbs_parser_t *alloc_parser_from_buffer(VALUE buffer, int start_pos, int end_pos) {
return alloc_parser_from_buffer_with_options(buffer, start_pos, end_pos, (rbs_parser_options_t) { 0 });
}

static VALUE rbsparser_parse_type(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof, VALUE void_allowed, VALUE self_allowed, VALUE classish_allowed) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);
Expand Down Expand Up @@ -254,12 +268,12 @@ static VALUE parse_method_type_try(VALUE a) {
return rbs_struct_to_ruby_value(ctx, (rbs_node_t *) method_type);
}

static VALUE rbsparser_parse_method_type(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof) {
static VALUE rbsparser_parse_method_type(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof, VALUE enable_forwarding_params) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);
rb_encoding *encoding = rb_enc_get(string);

rbs_parser_t *parser = alloc_parser_from_buffer(buffer, FIX2INT(start_pos), FIX2INT(end_pos));
rbs_parser_t *parser = alloc_parser_from_buffer_with_options(buffer, FIX2INT(start_pos), FIX2INT(end_pos), parser_options(enable_forwarding_params));
declare_type_variables(parser, variables, buffer);
struct parse_method_type_arg arg = {
.buffer = buffer,
Expand Down Expand Up @@ -293,12 +307,12 @@ static VALUE parse_signature_try(VALUE a) {
return rbs_struct_to_ruby_value(ctx, (rbs_node_t *) signature);
}

static VALUE rbsparser_parse_signature(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos) {
static VALUE rbsparser_parse_signature(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE enable_forwarding_params) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);
rb_encoding *encoding = rb_enc_get(string);

rbs_parser_t *parser = alloc_parser_from_buffer(buffer, FIX2INT(start_pos), FIX2INT(end_pos));
rbs_parser_t *parser = alloc_parser_from_buffer_with_options(buffer, FIX2INT(start_pos), FIX2INT(end_pos), parser_options(enable_forwarding_params));
struct parse_signature_arg arg = {
.buffer = buffer,
.encoding = encoding,
Expand Down Expand Up @@ -386,12 +400,12 @@ static VALUE parse_method_type_to_bytes_try(VALUE a) {
return serialized_node_to_string(parser, (rbs_node_t *) method_type);
}

static VALUE rbsparser_parse_method_type_to_bytes(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof) {
static VALUE rbsparser_parse_method_type_to_bytes(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof, VALUE enable_forwarding_params) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);
rb_encoding *encoding = rb_enc_get(string);

rbs_parser_t *parser = alloc_parser_from_buffer(buffer, FIX2INT(start_pos), FIX2INT(end_pos));
rbs_parser_t *parser = alloc_parser_from_buffer_with_options(buffer, FIX2INT(start_pos), FIX2INT(end_pos), parser_options(enable_forwarding_params));
declare_type_variables(parser, variables, buffer);
struct parse_method_type_arg arg = {
.buffer = buffer,
Expand Down Expand Up @@ -419,12 +433,12 @@ static VALUE parse_signature_to_bytes_try(VALUE a) {
return serialized_node_to_string(parser, (rbs_node_t *) signature);
}

static VALUE rbsparser_parse_signature_to_bytes(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos) {
static VALUE rbsparser_parse_signature_to_bytes(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE enable_forwarding_params) {
VALUE string = rb_funcall(buffer, rb_intern("content"), 0);
StringValue(string);
rb_encoding *encoding = rb_enc_get(string);

rbs_parser_t *parser = alloc_parser_from_buffer(buffer, FIX2INT(start_pos), FIX2INT(end_pos));
rbs_parser_t *parser = alloc_parser_from_buffer_with_options(buffer, FIX2INT(start_pos), FIX2INT(end_pos), parser_options(enable_forwarding_params));
struct parse_signature_arg arg = {
.buffer = buffer,
.encoding = encoding,
Expand Down Expand Up @@ -609,11 +623,11 @@ void rbs__init_parser(void) {
rb_gc_register_mark_object(EMPTY_HASH);

rb_define_singleton_method(RBS_Parser, "_parse_type", rbsparser_parse_type, 8);
rb_define_singleton_method(RBS_Parser, "_parse_method_type", rbsparser_parse_method_type, 5);
rb_define_singleton_method(RBS_Parser, "_parse_signature", rbsparser_parse_signature, 3);
rb_define_singleton_method(RBS_Parser, "_parse_method_type", rbsparser_parse_method_type, 6);
rb_define_singleton_method(RBS_Parser, "_parse_signature", rbsparser_parse_signature, 4);
rb_define_singleton_method(RBS_Parser, "_parse_type_to_bytes", rbsparser_parse_type_to_bytes, 8);
rb_define_singleton_method(RBS_Parser, "_parse_method_type_to_bytes", rbsparser_parse_method_type_to_bytes, 5);
rb_define_singleton_method(RBS_Parser, "_parse_signature_to_bytes", rbsparser_parse_signature_to_bytes, 3);
rb_define_singleton_method(RBS_Parser, "_parse_method_type_to_bytes", rbsparser_parse_method_type_to_bytes, 6);
rb_define_singleton_method(RBS_Parser, "_parse_signature_to_bytes", rbsparser_parse_signature_to_bytes, 4);
rb_define_singleton_method(RBS_Parser, "_parse_type_params", rbsparser_parse_type_params, 4);
rb_define_singleton_method(RBS_Parser, "_parse_inline_leading_annotation", rbsparser_parse_inline_leading_annotation, 4);
rb_define_singleton_method(RBS_Parser, "_parse_inline_trailing_annotation", rbsparser_parse_inline_trailing_annotation, 4);
Expand Down
27 changes: 27 additions & 0 deletions include/rbs/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ typedef struct rbs_error_t {
bool syntax_error;
} rbs_error_t;

/**
* Options that control which syntax the parser accepts.
*
* Zero-initializing the struct gives the default configuration, where
* every optional syntax is disabled.
* */
typedef struct {
/**
* Accept `(...)` forwarding parameters in method types.
*
* The syntax is experimental and disabled by default.
* */
bool enable_forwarding_params;
} rbs_parser_options_t;

/**
* An RBS parser is a LL(3) parser.
* */
Expand All @@ -57,6 +72,8 @@ typedef struct {
rbs_constant_pool_t constant_pool;
rbs_allocator_t *allocator;
rbs_error_t *error;

rbs_parser_options_t options;
} rbs_parser_t;

/**
Expand Down Expand Up @@ -107,6 +124,16 @@ RBS_NODISCARD rbs_lexer_t *rbs_lexer_new(rbs_allocator_t *, rbs_string_t string,
* Returns `NULL` for a `start_pos` that `rbs_lexer_new` rejects.
* */
RBS_NODISCARD rbs_parser_t *rbs_parser_new(rbs_string_t string, const rbs_encoding_t *encoding, int start_pos, int end_pos);

/**
* Allocate new rbs_parser_t object with the given options.
*
* `rbs_parser_new` is equivalent to passing a zero-initialized
* `rbs_parser_options_t`, which disables every optional syntax.
*
* Returns `NULL` for a `start_pos` that `rbs_lexer_new` rejects.
* */
RBS_NODISCARD rbs_parser_t *rbs_parser_new_with_options(rbs_string_t string, const rbs_encoding_t *encoding, int start_pos, int end_pos, rbs_parser_options_t options);
void rbs_parser_free(rbs_parser_t *parser);

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/rbs/parser_aux.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def self.parse_type(source, range: nil, byte_range: 0..., variables: [], require
def self.parse_method_type(source, range: nil, byte_range: 0..., variables: [], require_eof: false)
buf = buffer(source)
byte_range = byte_range(range, buf.content) if range
_parse_method_type(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables, require_eof)
_parse_method_type(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables, require_eof, false)
end

def self.parse_signature(source)
Expand All @@ -28,7 +28,7 @@ def self.parse_signature(source)
0
end
content = buf.content
dirs, decls = _parse_signature(buf, start_pos, content.bytesize)
dirs, decls = _parse_signature(buf, start_pos, content.bytesize, false)

if resolved
dirs = dirs.dup if dirs.frozen?
Expand Down
15 changes: 13 additions & 2 deletions lib/rbs/wasm/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ module RBS
# RBS::Parser API on top, exactly as it does for the C extension.
class Parser
class << self
def _parse_signature(buffer, start_pos, end_pos)
def _parse_signature(buffer, start_pos, end_pos, enable_forwarding_params)
validate_position_range(buffer, start_pos, end_pos)
validate_parser_options(enable_forwarding_params)
encoding = buffer.content.encoding.name
status, bytes = WASM::Runtime.instance.parse_signature(buffer.content, encoding, start_pos, end_pos)
raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
Expand All @@ -31,9 +32,10 @@ def _parse_type(buffer, start_pos, end_pos, variables, require_eof, void_allowed
deserialize_or_nil(bytes, buffer)
end

def _parse_method_type(buffer, start_pos, end_pos, variables, require_eof)
def _parse_method_type(buffer, start_pos, end_pos, variables, require_eof, enable_forwarding_params)
validate_position_range(buffer, start_pos, end_pos)
validate_variables(variables)
validate_parser_options(enable_forwarding_params)
encoding = buffer.content.encoding.name
status, bytes = WASM::Runtime.instance.parse_method_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof)
raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
Expand Down Expand Up @@ -99,6 +101,15 @@ def validate_position_range(buffer, start_pos, end_pos)
end
end

# The WebAssembly entry points (rbs_wasm.c) build their parsers with the
# default options, so the optional syntax the C extension can enable is
# not reachable here. The public RBS::Parser API never enables it.
def validate_parser_options(enable_forwarding_params)
if enable_forwarding_params
raise NotImplementedError, "forwarding parameter syntax is not supported by the WebAssembly parser"
end
end

# Reject anything that is not nil or an Array of Symbols, matching
# declare_type_variables in the C extension (main.c).
def validate_variables(variables)
Expand Down
8 changes: 4 additions & 4 deletions sig/parser.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,19 @@ module RBS

def self._parse_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool void_allowed, bool self_allowed, bool classish_allowed) -> Types::t?

def self._parse_method_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof) -> MethodType?
def self._parse_method_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool enable_forwarding_params) -> MethodType?

def self._parse_signature: (Buffer, Integer start_pos, Integer end_pos) -> [Array[AST::Directives::t], Array[AST::Declarations::t]]
def self._parse_signature: (Buffer, Integer start_pos, Integer end_pos, bool enable_forwarding_params) -> [Array[AST::Directives::t], Array[AST::Declarations::t]]

# Parse and serialize the result to the binary format consumed by
# RBS::WASM::Deserializer (see ext/rbs_extension/main.c and
# docs/wasm_serialization.md). The `_to_bytes` variants exist so the
# round-trip can be exercised on CRuby.
def self._parse_type_to_bytes: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool void_allowed, bool self_allowed, bool classish_allowed) -> String?

def self._parse_method_type_to_bytes: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof) -> String?
def self._parse_method_type_to_bytes: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool enable_forwarding_params) -> String?

def self._parse_signature_to_bytes: (Buffer, Integer start_pos, Integer end_pos) -> String
def self._parse_signature_to_bytes: (Buffer, Integer start_pos, Integer end_pos, bool enable_forwarding_params) -> String

def self._parse_type_params: (Buffer, Integer start_pos, Integer end_pos, bool module_type_params) -> Array[AST::TypeParam]

Expand Down
11 changes: 11 additions & 0 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,11 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool forwa
return false;
}

if (!parser->options.enable_forwarding_params) {
rbs_parser_set_error(parser, parser->next_token, true, "forwarding parameter syntax is not enabled");
return false;
}

rbs_parser_advance(parser);
params->forwarding = (rbs_node_t *) rbs_types_function_forwarding_param_new(
ALLOCATOR(),
Expand Down Expand Up @@ -3569,6 +3574,10 @@ rbs_lexer_t *rbs_lexer_new(rbs_allocator_t *allocator, rbs_string_t string, cons
}

rbs_parser_t *rbs_parser_new(rbs_string_t string, const rbs_encoding_t *encoding, int start_pos, int end_pos) {
return rbs_parser_new_with_options(string, encoding, start_pos, end_pos, (rbs_parser_options_t) { 0 });
}

rbs_parser_t *rbs_parser_new_with_options(rbs_string_t string, const rbs_encoding_t *encoding, int start_pos, int end_pos, rbs_parser_options_t options) {
rbs_allocator_t *allocator = rbs_allocator_init();

rbs_lexer_t *lexer = rbs_lexer_new(allocator, string, encoding, start_pos, end_pos);
Expand All @@ -3593,6 +3602,8 @@ rbs_parser_t *rbs_parser_new(rbs_string_t string, const rbs_encoding_t *encoding
.constant_pool = { 0 },
.allocator = allocator,
.error = NULL,

.options = options,
};

// The parser's constant pool is mainly used for storing the names of type variables, which usually aren't many.
Expand Down
Loading
Loading