Class: RBS::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/rbs/wasm/parser.rb,
lib/rbs/parser_aux.rb,
lib/rbs/parser/token.rb,
lib/rbs/parser/lex_result.rb,
sig/errors.rbs,
sig/parser.rbs,
ext/rbs_extension/main.c

Overview

WebAssembly-backed implementation of the parser primitives.

On CRuby these come from the C extension (ext/rbs_extension/main.c). JRuby loads this instead: it runs the parser inside WebAssembly, then rebuilds the AST with RBS::WASM::Deserializer. rbs/parser_aux.rb layers the public RBS::Parser API on top, exactly as it does for the C extension.

Defined Under Namespace

Classes: LexResult, LocatedValue, Token

Constant Summary collapse

KEYWORDS =

Returns:

  • (Hash[String, bot])
%w(
bool
bot
class
instance
interface
nil
self
singleton
top
void
type
unchecked
in
out
end
def
include
extend
prepend
alias
module
attr_reader
attr_writer
attr_accessor
public
private
untyped
true
false
).each_with_object({}) do |keyword, hash| #$ Hash[String, bot]
  hash[keyword] = _ = nil
end
SemanticsError =

Returns:

SyntaxError =

Returns:

LexerError =

Returns:

Class Method Summary collapse

Class Method Details

._lex(buffer, end_pos) ⇒ Array[[Symbol, Location[untyped, untyped]]]

Parameters:

Returns:

  • (Array[[Symbol, Location[untyped, untyped]]])


555
556
557
558
559
560
# File 'ext/rbs_extension/main.c', line 555

def _lex(buffer, end_pos)
  encoding = buffer.content.encoding.name
  _success, bytes = WASM::Runtime.instance.lex(buffer.content, encoding, end_pos)

  WASM::Deserializer.deserialize_tokens(bytes, buffer)
end

._parse_inline_leading_annotation(buffer, start_pos, end_pos, variables) ⇒ AST::Ruby::Annotations::leading_annotation

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])

Returns:

  • (AST::Ruby::Annotations::leading_annotation)


491
492
493
494
495
496
497
498
499
# File 'ext/rbs_extension/main.c', line 491

def _parse_inline_leading_annotation(buffer, start_pos, end_pos, variables)
  validate_position_range(start_pos, end_pos)
  validate_variables(variables)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_inline_leading_annotation(buffer.content, encoding, start_pos, end_pos, variables)
  raise_parsing_error(buffer, bytes) unless success

  deserialize_or_nil(bytes, buffer)
end

._parse_inline_trailing_annotation(buffer, start_pos, end_pos, variables) ⇒ AST::Ruby::Annotations::trailing_annotation

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])

Returns:

  • (AST::Ruby::Annotations::trailing_annotation)


534
535
536
537
538
539
540
541
542
# File 'ext/rbs_extension/main.c', line 534

def _parse_inline_trailing_annotation(buffer, start_pos, end_pos, variables)
  validate_position_range(start_pos, end_pos)
  validate_variables(variables)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_inline_trailing_annotation(buffer.content, encoding, start_pos, end_pos, variables)
  raise_parsing_error(buffer, bytes) unless success

  deserialize_or_nil(bytes, buffer)
end

._parse_method_type(buffer, start_pos, end_pos, variables, require_eof) ⇒ MethodType?

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])
  • require_eof (Boolean)

Returns:



235
236
237
238
239
240
241
242
243
# File 'ext/rbs_extension/main.c', line 235

def _parse_method_type(buffer, start_pos, end_pos, variables, require_eof)
  validate_position_range(start_pos, end_pos)
  validate_variables(variables)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_method_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof)
  raise_parsing_error(buffer, bytes) unless success

  deserialize_or_nil(bytes, buffer)
end

._parse_method_type_to_bytes(buffer, start_pos, end_pos, variables, require_eof) ⇒ String?

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])
  • require_eof (Boolean)

Returns:

  • (String, nil)


367
# File 'ext/rbs_extension/main.c', line 367

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

._parse_signature(buffer, start_pos, end_pos) ⇒ [Array[AST::Directives::t], Array[AST::Declarations::t]]

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)

Returns:

  • ([Array[AST::Directives::t], Array[AST::Declarations::t]])


274
275
276
277
278
279
280
281
# File 'ext/rbs_extension/main.c', line 274

def _parse_signature(buffer, start_pos, end_pos)
  validate_position_range(start_pos, end_pos)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_signature(buffer.content, encoding, start_pos, end_pos)
  raise_parsing_error(buffer, bytes) unless success

  WASM::Deserializer.deserialize(bytes, buffer)
end

._parse_signature_to_bytes(buffer, start_pos, end_pos) ⇒ String

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)

Returns:

  • (String)


400
# File 'ext/rbs_extension/main.c', line 400

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

._parse_type(buffer, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed) ⇒ Types::t?

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])
  • require_eof (Boolean)
  • void_allowed (Boolean)
  • self_allowed (Boolean)
  • classish_allowed (Boolean)

Returns:

  • (Types::t, nil)


189
190
191
192
193
194
195
196
197
# File 'ext/rbs_extension/main.c', line 189

def _parse_type(buffer, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
  validate_position_range(start_pos, end_pos)
  validate_variables(variables)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
  raise_parsing_error(buffer, bytes) unless success

  deserialize_or_nil(bytes, buffer)
end

._parse_type_params(buffer, start_pos, end_pos, module_type_params) ⇒ Array[AST::TypeParam]

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • module_type_params (Boolean)

Returns:



449
450
451
452
453
454
455
456
# File 'ext/rbs_extension/main.c', line 449

def _parse_type_params(buffer, start_pos, end_pos, module_type_params)
  validate_position_range(start_pos, end_pos)
  encoding = buffer.content.encoding.name
  success, bytes = WASM::Runtime.instance.parse_type_params(buffer.content, encoding, start_pos, end_pos, module_type_params)
  raise_parsing_error(buffer, bytes) unless success

  bytes.empty? ? nil : WASM::Deserializer.deserialize_node_list(bytes, buffer)
end

._parse_type_to_bytes(buffer, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed) ⇒ String?

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.

Parameters:

  • (Buffer)
  • start_pos (Integer)
  • end_pos (Integer)
  • variables (Array[Symbol])
  • require_eof (Boolean)
  • void_allowed (Boolean)
  • self_allowed (Boolean)
  • classish_allowed (Boolean)

Returns:

  • (String, nil)


147
# File 'sig/parser.rbs', line 147

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?

.buffer(source) ⇒ Buffer

Parameters:

Returns:



79
80
81
82
83
84
85
86
# File 'lib/rbs/parser_aux.rb', line 79

def self.buffer(source)
  case source
  when String
    Buffer.new(content: source, name: Pathname("a.rbs"))
  when Buffer
    source
  end
end

.byte_range(char_range, content) ⇒ Range[Integer?]

Parameters:

  • char_range (Range[Integer?])
  • content (String)

Returns:

  • (Range[Integer?])


134
135
136
137
138
139
140
141
142
# File 'lib/rbs/parser_aux.rb', line 134

def self.byte_range(char_range, content)
  start_offset = char_range.begin
  end_offset = char_range.end

  start_prefix = content[0, start_offset] or raise if start_offset
  end_prefix = content[0, end_offset] or raise if end_offset

  start_prefix&.bytesize...end_prefix&.bytesize
end

.lex(source) ⇒ LexResult

Parse whole RBS file and return result.

RBS::Parser.lex("# Comment\nmodule A\nend\n").value.map(&:type)
# => [:tLINECOMMENT, :kMODULE, :tUIDENT, :kEND, :pEOF]

Parameters:

Returns:



115
116
117
118
119
120
121
122
# File 'sig/parser.rbs', line 115

def self.lex(source)
  buf = buffer(source)
  list = _lex(buf, buf.content.bytesize)
  value = list.map do |type, location|
    Token.new(type: type, location: location)
  end
  LexResult.new(buffer: buf, value: value)
end

.magic_comment(buf) ⇒ AST::Directives::ResolveTypeNames?

Returns the magic comment from the buffer

Parameters:

Returns:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'sig/parser.rbs', line 107

def self.magic_comment(buf)
  start_pos = 0

  while true
    case
    when match = /\A#\s*(?<keyword>resolve-type-names)\s*(?<colon>:)\s+(?<value>true|false)$/.match(buf.content, start_pos)
      value = match[:value] or raise

      kw_offset = match.offset(:keyword) #: [Integer, Integer]
      colon_offset = match.offset(:colon) #: [Integer, Integer]
      value_offset = match.offset(:value) #: [Integer, Integer]

      location = Location.new(buf, kw_offset[0], value_offset[1])
      location.add_required_child(:keyword, kw_offset[0]...kw_offset[1])
      location.add_required_child(:colon, colon_offset[0]...colon_offset[1])
      location.add_required_child(:value, value_offset[0]...value_offset[1])

      return AST::Directives::ResolveTypeNames.new(value: value == "true", location: location)
    else
      return
    end
  end
end

.parse_inline_leading_annotation(source, range, variables: []) ⇒ AST::Ruby::Annotations::leading_annotation

Parse a leading annotation and return it

Raises an exception if the source text contains a syntax error.

Parameters:

  • (Buffer, String)
  • (Range[Integer?])
  • variables: (Array[Symbol]) (defaults to: [])

Returns:

  • (AST::Ruby::Annotations::leading_annotation)


123
124
125
126
127
# File 'sig/parser.rbs', line 123

def self.parse_inline_leading_annotation(source, range, variables: [])
  buf = buffer(source)
  byte_range = byte_range(range, buf.content)
  _parse_inline_leading_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
end

.parse_inline_trailing_annotation(source, range, variables: []) ⇒ AST::Ruby::Annotations::trailing_annotation

Parse a leading annotation and return it

Raises an exception if the source text contains a syntax error.

Parameters:

  • (Buffer, String)
  • (Range[Integer?])
  • variables: (Array[Symbol]) (defaults to: [])

Returns:

  • (AST::Ruby::Annotations::trailing_annotation)


129
130
131
132
133
# File 'sig/parser.rbs', line 129

def self.parse_inline_trailing_annotation(source, range, variables: [])
  buf = buffer(source)
  byte_range = byte_range(range, buf.content)
  _parse_inline_trailing_annotation(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables)
end

.parse_method_type(arg0, byte_range:, variables:, require_eof:) ⇒ MethodType? .parse_method_typevoid

Parse a method type and return it

When byte_range keyword is specified, it starts parsing from the begin to the end of the range.

RBS::Parser.parse_method_type("() -> void")                                     # => `() -> void`
RBS::Parser.parse_method_type("() -> void", byte_range: 0...)                   # => `() -> void`
RBS::Parser.parse_method_type("() -> void () -> String", byte_range: 11...)     # => `() -> String`
RBS::Parser.parse_method_type("() -> void () -> String", byte_range: 23...)     # => nil

When require_eof is true, an error is raised if more tokens are left in the input. (Defaults to false.)

RBS::Parser.parse_method_type("() -> void () -> String", require_eof: false)    # => `() -> void`
RBS::Parser.parse_method_type("() -> void () -> String", require_eof: true)     # => Raises an error

RBS::Parser.parse_method_type("", require_eof: true)                            # => nil

Overloads:

  • .parse_method_type(arg0, byte_range:, variables:, require_eof:) ⇒ MethodType?

    Parameters:

    • arg0 (Buffer, String)
    • byte_range: (Range[Integer?])
    • variables: (Array[Symbol])
    • require_eof: (Boolean)

    Returns:

  • .parse_method_typevoid

    This method returns an undefined value.



42
43
44
45
46
# File 'sig/parser.rbs', line 42

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)
end

.parse_signature(source) ⇒ [Buffer, Array[AST::Directives::t], Array[AST::Declarations::t]]

Parse whole RBS file and return an array of declarations

Parameters:

Returns:

  • ([Buffer, Array[AST::Directives::t], Array[AST::Declarations::t]])


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'sig/parser.rbs', line 85

def self.parse_signature(source)
  buf = buffer(source)

  resolved = magic_comment(buf)
  start_pos =
    if resolved
      (resolved.location || raise).end_pos
    else
      0
    end
  content = buf.content
  dirs, decls = _parse_signature(buf, start_pos, content.bytesize)

  if resolved
    dirs = dirs.dup if dirs.frozen?
    dirs.unshift(resolved)
  end

  [buf, dirs, decls]
end

.parse_type(arg0, byte_range:, variables:, require_eof:, void_allowed:, self_allowed:, classish_allowed:) ⇒ Types::t? .parse_typevoid

Parse a type and return it

When byte_range keyword is specified, it starts parsing from the begin to the end of the range.

RBS::Parser.parse_type("String")                               # => `String`
RBS::Parser.parse_type("String", byte_range: 0...)             # => `String`
RBS::Parser.parse_type("String Integer", byte_range: 7...)     # => `Integer`
RBS::Parser.parse_type("String Integer", byte_range: 14...)    # => nil

When require_eof is true, an error is raised if more tokens are left in the input. (Defaults to false.)

RBS::Parser.parse_type("String untyped", require_eof: false)    # => `String`
RBS::Parser.parse_type("String untyped", require_eof: true)     # => Raises an error

RBS::Parser.parse_type("", require_eof: true)                   # => nil

The void_allowed keyword controls whether void is allowed as a type.

RBS::Parser.parse_type("void", void_allowed: true)          # => `void`
RBS::Parser.parse_type("void", void_allowed: false)         # => Raises an syntax error

The self_allowed keyword controls whether self is allowed as a type.

RBS::Parser.parse_type("self", self_allowed: true)          # => `self`
RBS::Parser.parse_type("self", self_allowed: false)         # => Raises an syntax error

Overloads:

  • .parse_type(arg0, byte_range:, variables:, require_eof:, void_allowed:, self_allowed:, classish_allowed:) ⇒ Types::t?

    Parameters:

    • arg0 (Buffer, String)
    • byte_range: (Range[Integer?])
    • variables: (Array[Symbol])
    • require_eof: (Boolean)
    • void_allowed: (Boolean)
    • self_allowed: (Boolean)
    • classish_allowed: (Boolean)

    Returns:

    • (Types::t, nil)
  • .parse_typevoid

    This method returns an undefined value.



80
81
82
83
84
# File 'sig/parser.rbs', line 80

def self.parse_type(source, range: nil, byte_range: 0..., variables: [], require_eof: false, void_allowed: true, self_allowed: true, classish_allowed: true)
  buf = buffer(source)
  byte_range = byte_range(range, buf.content) if range
  _parse_type(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables, require_eof, void_allowed, self_allowed, classish_allowed)
end

.parse_type_params(source, module_type_params: true) ⇒ Array[AST::TypeParam]

Parse a list of type parameters and return it

RBS::Parser.parse_type_params("")                          # => nil
RBS::Parser.parse_type_params("[U, V]")                    # => `[:U, :V]`
RBS::Parser.parse_type_params("[in U, V < Integer]")       # => `[:U, :V]`

When module_type_params is false, an error is raised if unchecked, in or out are used.

RBS::Parser.parse_type_params("[unchecked U]", module_type_params: false)   # => Raises an error
RBS::Parser.parse_type_params("[out U]", module_type_params: false)         # => Raises an error
RBS::Parser.parse_type_params("[in U]", module_type_params: false)          # => Raises an error

Parameters:

  • (Buffer, String)
  • module_type_params: (Boolean) (defaults to: true)

Returns:



103
104
105
106
# File 'sig/parser.rbs', line 103

def self.parse_type_params(source, module_type_params: true)
  buf = buffer(source)
  _parse_type_params(buf, 0, buf.content.bytesize, module_type_params)
end