Class: Ibex::Frontend::BootstrapParser

Inherits:
Object
  • Object
show all
Includes:
BootstrapParserDeclarations, BootstrapParserParameters, BootstrapParserRules
Defined in:
lib/ibex/frontend/bootstrap_parser.rb,
sig/ibex/frontend/bootstrap_parser.rbs

Overview

Handwritten parser used only to regenerate the self-hosted frontend.

Constant Summary

Constants included from BootstrapParserRules

Ibex::Frontend::BootstrapParserRules::EXTENDED_SUFFIXES

Constants included from BootstrapParserDeclarations

Ibex::Frontend::BootstrapParserDeclarations::ASSOCIATIVITIES, Ibex::Frontend::BootstrapParserDeclarations::DECLARATIONS

Instance Method Summary collapse

Methods included from BootstrapParserParameters

#adjacent_tokens?, #parameter_definition_tail?, #parameterized_call?, #parameterized_rule_start?, #parse_parameterized_reference, #parse_rule_parameters

Methods included from BootstrapParserRules

#alternative_end?, #parse_action, #parse_alternative, #parse_group, #parse_item, #parse_named_reference, #parse_node_annotation, #parse_rule, #parse_rules, #parse_separated_list, #parse_suffix, #rule_start?, #separated_list?

Methods included from BootstrapParserDeclarations

#association_start?, #build_bootstrap_lexer_rule, #declaration_start?, #decode_conversion, #decode_quoted_literal, #expect_lexer_pattern, #expect_metadata_value, #parse_convert, #parse_declaration, #parse_declarations, #parse_expect, #parse_expect_rr, #parse_grammar_test, #parse_import, #parse_lexer, #parse_lexer_entries, #parse_lexer_entry, #parse_lexer_state, #parse_on_error_reduce, #parse_options, #parse_parameter, #parse_pragmas, #parse_precedence, #parse_printer, #parse_recovery, #parse_start, #parse_symbol_metadata, #parse_tokens, #precedence_levels, #tokens_on_line, #validate_metadata_line

Constructor Details

#initialize(source, file: "(grammar)", mode: :default) ⇒ BootstrapParser

Returns a new instance of BootstrapParser.

RBS:

  • (String | Array[Token] source, ?file: String, ?mode: Symbol) -> void

Parameters:

  • source (String, Array[Token])
  • file: (String) (defaults to: "(grammar)")
  • mode: (Symbol) (defaults to: :default)


16
17
18
19
20
21
22
23
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 16

def initialize(source, file: "(grammar)", mode: :default)
  raise ArgumentError, "mode must be :default or :extended" unless %i[default extended].include?(mode)

  @tokens = source.is_a?(Array) ? source : Lexer.new(source, file: file).tokenize
  @index = 0
  @mode = mode
  @pragmas = {} #: Hash[String, bool]
end

Instance Method Details

#accept(type) ⇒ Token, false

RBS:

  • (Symbol type) -> (Token | false)

Parameters:

  • type (Symbol)

Returns:



123
124
125
126
127
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 123

def accept(type)
  return false unless current.type == type

  advance
end

#advanceToken

RBS:

  • () -> Token

Returns:



147
148
149
150
151
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 147

def advance
  token = current
  @index += 1
  token
end

#currentToken

RBS:

  • () -> Token

Returns:



137
138
139
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 137

def current
  @tokens.fetch(@index)
end

#expect(type) ⇒ Token

RBS:

  • (Symbol type) -> Token

Parameters:

  • type (Symbol)

Returns:



130
131
132
133
134
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 130

def expect(type)
  return advance if current.type == type

  fail_expected(type)
end

#expect_keyword(value) ⇒ Token

RBS:

  • (String value) -> Token

Parameters:

  • value (String)

Returns:



116
117
118
119
120
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 116

def expect_keyword(value)
  return advance if keyword?(value)

  fail_expected(value)
end

#expect_one_of(values) ⇒ Token

RBS:

  • (Array[String] values) -> Token

Parameters:

  • values (Array[String])

Returns:



104
105
106
107
108
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 104

def expect_one_of(values)
  return advance if current.type == :identifier && values.include?(current.value)

  fail_expected(values.join(" or "))
end

#expect_symbolToken

RBS:

  • () -> Token

Returns:



97
98
99
100
101
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 97

def expect_symbol
  return advance if %i[identifier literal].include?(current.type)

  fail_expected("a grammar symbol")
end

#extended_only!(location, feature) ⇒ void

This method returns an undefined value.

RBS:

  • (Location location, String feature) -> void

Parameters:



165
166
167
168
169
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 165

def extended_only!(location, feature)
  return if @mode == :extended

  fail_at(location, "#{feature} require extended mode")
end

#fail_at(location, message) ⇒ bot

RBS:

  • (Location location, String message) -> bot

Parameters:

Returns:

  • (bot)


177
178
179
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 177

def fail_at(location, message)
  raise Ibex::Error, "#{location}: #{message}"
end

#fail_expected(expected) ⇒ bot

RBS:

  • (String | Symbol expected) -> bot

Parameters:

  • expected (String, Symbol)

Returns:

  • (bot)


172
173
174
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 172

def fail_expected(expected)
  fail_at(current.location, "expected #{expected}, got #{current.value || current.type}")
end

#fragment_root_declaration_name(declaration) ⇒ String?

RBS:

  • (AST::declaration declaration) -> String?

Parameters:

  • declaration (AST::declaration)

Returns:

  • (String, nil)


70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 70

def fragment_root_declaration_name(declaration)
  return "options" if declaration.is_a?(AST::Options)
  return "expect" if declaration.is_a?(AST::Expect)
  return "%expect-rr" if declaration.is_a?(AST::ExpectRR)
  return "%param" if declaration.is_a?(AST::Parameter)
  return "start" if declaration.is_a?(AST::Start)
  return "%recover" if declaration.is_a?(AST::Recovery)
  return "%on_error_reduce" if declaration.is_a?(AST::OnErrorReduce)
  return "%test" if declaration.is_a?(AST::GrammarTest)
  return "lexer" if declaration.is_a?(AST::Lexer)

  nil
end

#keyword?(value) ⇒ Boolean

RBS:

  • (String value) -> bool

Parameters:

  • value (String)

Returns:

  • (Boolean)


111
112
113
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 111

def keyword?(value)
  current.type == :identifier && current.value == value
end

#lookaheadToken

RBS:

  • () -> Token

Returns:



142
143
144
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 142

def lookahead
  @tokens.fetch(@index + 1) { @tokens.fetch(-1) }
end

#parseAST::Root

RBS:

  • () -> AST::Root

Returns:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 26

def parse
  location = expect_keyword("class").location
  class_name = parse_constant_path
  superclass = accept(:<) ? parse_constant_path : nil
  parse_pragmas
  declarations = parse_declarations
  expect_keyword("rule")
  rules = parse_rules
  expect_keyword("end") unless current.type == :user_code
  user_code = parse_user_code
  expect(:eof)
  AST::Root.new(class_name: class_name, superclass: superclass, declarations: declarations,
                rules: rules, user_code: user_code, loc: location,
                extended: @mode == :extended || @pragmas.key?("extended") || @pragmas.key?("cst"),
                cst: @pragmas.key?("cst"))
end

#parse_constant_pathString

RBS:

  • () -> String

Returns:

  • (String)


85
86
87
88
89
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 85

def parse_constant_path
  parts = [token_string(expect(:identifier))]
  parts << token_string(expect(:identifier)) while accept(:scope)
  parts.join("::")
end

#parse_fragmentAST::Fragment

RBS:

  • () -> AST::Fragment

Returns:



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 44

def parse_fragment
  location = expect_keyword("fragment").location
  extended_only!(location, "fragments")
  declarations = parse_declarations
  reject_fragment_root_declarations(declarations)
  expect_keyword("rule")
  rules = keyword?("end") ? Array.new(0) : parse_rules #: Array[AST::Rule]
  expect_keyword("end")
  user_code = parse_user_code
  fail_at(user_code.values.flatten.first.loc, "user code is not allowed in fragments") unless user_code.empty?
  expect(:eof)
  AST::Fragment.new(declarations: declarations, rules: rules, loc: location)
end

#parse_symbol_nameString

RBS:

  • () -> String

Returns:

  • (String)


92
93
94
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 92

def parse_symbol_name
  token_string(expect_symbol)
end

#parse_user_codeAST::user_code

RBS:

  • () -> AST::user_code

Returns:

  • (AST::user_code)


154
155
156
157
158
159
160
161
162
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 154

def parse_user_code
  blocks = Hash.new { |hash, key| hash[key] = Array.new(0) } #: AST::user_code
  while current.type == :user_code
    token = advance
    value = token_user_code(token)
    blocks[value[:name]] << AST::UserCode.new(name: value[:name], code: value[:code], loc: token.location)
  end
  blocks
end

#reject_fragment_root_declarations(declarations) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[AST::declaration] declarations) -> void

Parameters:

  • declarations (Array[AST::declaration])


61
62
63
64
65
66
67
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 61

def reject_fragment_root_declarations(declarations)
  root_only = declarations.find { |declaration| fragment_root_declaration_name(declaration) }
  return unless root_only

  name = fragment_root_declaration_name(root_only)
  fail_at(root_only.loc, "#{name} declarations are not allowed in fragments")
end

#token_integer(token) ⇒ Integer

RBS:

  • (Token token) -> Integer

Parameters:

Returns:

  • (Integer)


190
191
192
193
194
195
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 190

def token_integer(token)
  value = token.value
  return value if value.is_a?(Integer)

  fail_at(token.location, "expected integer token")
end

#token_string(token) ⇒ String

RBS:

  • (Token token) -> String

Parameters:

Returns:

  • (String)


182
183
184
185
186
187
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 182

def token_string(token)
  value = token.value
  return value if value.is_a?(String)

  fail_at(token.location, "expected text token")
end

#token_user_code(token) ⇒ user_code_token

RBS:

  • (Token token) -> user_code_token

Parameters:

Returns:

  • (user_code_token)


198
199
200
201
202
203
# File 'lib/ibex/frontend/bootstrap_parser.rb', line 198

def token_user_code(token)
  value = token.value
  return value if value.is_a?(Hash)

  fail_at(token.location, "expected user-code token")
end