Class: Ibex::Codegen::Ruby

Inherits:
Object
  • Object
show all
Includes:
RubyAST, RubyActions, RubyErrorMessages, RubyLexer, RubySyntax, RubyTableMetadata, RubyValuePrinters
Defined in:
lib/ibex/codegen/ruby.rb,
sig/ibex/codegen/ruby.rbs

Overview

Generates a standalone Ruby parser class from Automaton IR. rubocop:disable Metrics/ClassLength -- generation stages are split into focused mixins.

Constant Summary collapse

EMBEDDED_RUNTIME_SOURCES =

Returns:

  • (Array[String])
%w[
  ../runtime/version.rb
  ../runtime/table_format.rb
  ../runtime/location_span.rb
  ../runtime/cst/kind.rb
  ../runtime/cst/annotation.rb
  ../runtime/cst/green/trivia.rb
  ../runtime/cst/green/token.rb
  ../runtime/cst/green/node.rb
  ../runtime/cst/green/cache.rb
  ../runtime/cst/green/builder.rb
  ../runtime/cst/source_text.rb
  ../runtime/cst/syntax_token.rb
  ../runtime/cst/syntax_node.rb
  ../runtime/cst/cursor.rb
  ../runtime/cst/typed_node.rb
  ../runtime/cst/parse_result.rb
  ../runtime/cst/editing.rb
  ../runtime/cst/text_edit.rb
  ../runtime/cst/rewriter.rb
  ../runtime/cst/editor.rb
  ../runtime/cst/diff.rb
  ../runtime/cst/serialized_tree.rb
  ../runtime/cst/validator.rb
  ../runtime/cst/serialize.rb
  ../runtime/cst/incremental/token_memo.rb
  ../runtime/cst/incremental/relexer.rb
  ../runtime/cst/incremental/parse_memo.rb
  ../runtime/cst/incremental/lexed_syntax.rb
  ../runtime/cst/incremental/blender.rb
  ../runtime/cst/incremental/session.rb
  ../runtime/cst.rb
  ../runtime/ast_data.rb
  ../runtime/resource_limits.rb
  ../runtime/event_sanitizer.rb
  ../runtime/event.rb
  ../runtime/observation.rb
  ../runtime/repair.rb
  ../runtime/repair_priority_queue.rb
  ../runtime/repair_search.rb
  ../runtime/parser_sync_recovery.rb
  ../runtime/parser.rb
  ../runtime/lexer_input.rb
  ../runtime/generated_lexer.rb
  ../runtime/jsonl_tracer.rb
  ../runtime/event_jsonl_tracer.rb
  ../tables/compact.rb
  ../tables/compact_actions.rb
  ../tables/compact_productions.rb
].freeze

Instance Method Summary collapse

Methods included from RubySyntax

#append_repetition_accessors, #append_syntax, #append_syntax_node, #merge_syntax_fields, #syntax_node_definitions

Methods included from RubyAST

#append_ast, #append_listener, #append_listener_dispatch, #append_visit_children, #append_visitor, #ast_method_name, #ast_node_action_source, #ast_node_definitions

Methods included from RubyLexer

#append_lexer, #append_lexer_actions, #append_lexer_rules, #lexer_action_source, #lexer_rule_literal

Methods included from RubyValuePrinters

#append_value_printer, #append_value_printer_table, #append_value_printers, #value_printer_action_method_source

Methods included from RubyTableMetadata

#append_entry_methods, #append_table_metadata, #entry_table_fields, #recovery_table_fields

Methods included from RubyErrorMessages

#error_message_literal, #error_messages_literal

Methods included from RubyActions

#action_method?, #action_method_source, #append_action_method, #append_actions, #append_compiled_action_method, #append_composed_action_method, #append_composed_fragment_method, #append_composed_orchestrator, #append_composed_step, #composed_action?, #composed_fragment_name

Constructor Details

#initialize(automaton, table: :compact, embedded: false, line_convert: true, debug: false, line_convert_all: false, omit_action_call: nil, superclass: nil, executable: nil, cst_trivia: :leading, runtime_require: "ibex/runtime", error_messages: {}) ⇒ Ruby

Returns a new instance of Ruby.

RBS:

  • (IR::Automaton automaton, ?table: Symbol | String, ?embedded: bool, ?line_convert: bool, ?line_convert_all: bool, ?debug: bool, ?omit_action_call: bool?, ?superclass: String?, ?executable: String?, ?cst_trivia: Symbol | String, ?runtime_require: String?, ?error_messages: Hash[Integer, String | { id: String, message: String }]) -> void

Parameters:

  • automaton (IR::Automaton)
  • table: (Symbol, String) (defaults to: :compact)
  • embedded: (Boolean) (defaults to: false)
  • line_convert: (Boolean) (defaults to: true)
  • line_convert_all: (Boolean) (defaults to: false)
  • debug: (Boolean) (defaults to: false)
  • omit_action_call: (Boolean, nil) (defaults to: nil)
  • superclass: (String, nil) (defaults to: nil)
  • executable: (String, nil) (defaults to: nil)
  • cst_trivia: (Symbol, String) (defaults to: :leading)
  • runtime_require: (String, nil) (defaults to: "ibex/runtime")
  • error_messages: (Hash[Integer, String | { id: String, message: String }]) (defaults to: {})


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ibex/codegen/ruby.rb', line 98

def initialize(automaton, table: :compact, embedded: false, line_convert: true, debug: false,
               line_convert_all: false, omit_action_call: nil, superclass: nil, executable: nil,
               cst_trivia: :leading, runtime_require: "ibex/runtime", error_messages: {})
  @automaton = automaton
  @grammar = automaton.grammar
  @table_format = table.to_sym
  @embedded = embedded
  @line_convert = line_convert
  @line_convert_all = line_convert_all
  @debug = debug
  @omit_action_call = omit_action_call.nil? ? @grammar.options[:omit_action_call] : omit_action_call
  @superclass = superclass || @grammar.superclass || "Ibex::Runtime::Parser"
  @executable = executable
  @cst_trivia = cst_trivia.to_sym
  @generated_action_abi = GeneratedActionABI::Cache.new
  @runtime_require = runtime_require
  @cst_trivia = :leading if @cst_trivia == :attach
  unless %i[leading balanced drop].include?(@cst_trivia)
    raise ArgumentError, "cst_trivia must be :leading, :balanced, or :drop"
  end

  @error_messages = error_messages.sort.to_h.freeze
end

Instance Method Details

#action_references_locations?(production, action) ⇒ Boolean

RBS:

  • (IR::Production production, IR::Action action) -> bool

Parameters:

Returns:

  • (Boolean)


462
463
464
465
# File 'lib/ibex/codegen/ruby.rb', line 462

def action_references_locations?(production, action)
  maximum = action.context_length.positive? ? action.context_length : production.rhs.length
  ActionLocations.new(action.code, maximum: maximum, location: action.location).references?
end

#append_cst_metadata(lines, indent) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, String indent) -> void

Parameters:

  • lines (Array[String])
  • indent (String)


192
193
194
195
196
197
198
199
200
# File 'lib/ibex/codegen/ruby.rb', line 192

def (lines, indent)
  return unless cst?

  lines << "#{indent}SYMBOL_NAMES = #{symbol_names_literal}.freeze"
  lines << "#{indent}CST_METADATA = #{deep_frozen_literal()}"
  lines << "#{indent}CST_KIND_MODEL = Ibex::Runtime::CST::Kind.new("
  lines << "#{indent}  CST_METADATA.fetch(:kinds), slots: CST_METADATA.fetch(:slots)"
  lines << "#{indent})"
end

#append_parameter_initializer(lines) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines) -> void

Parameters:

  • lines (Array[String])


229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/ibex/codegen/ruby.rb', line 229

def append_parameter_initializer(lines)
  parameters = @grammar.parser_parameters
  return if parameters.empty?

  keywords = parameters.map { |parameter| "#{parameter[:name]}:" }.join(", ")
  lines << "  IBEX_PARAMETER_INITIALIZER = Module.new do"
  lines << "    def initialize(#{keywords}, **_ibex_super_arguments)"
  parameters.each { |parameter| lines << "      @#{parameter[:name]} = #{parameter[:name]}" }
  lines << "      super(**_ibex_super_arguments)"
  lines << "    end"
  lines << "  end"
  lines << "  prepend IBEX_PARAMETER_INITIALIZER"
  lines << "  private_constant :IBEX_PARAMETER_INITIALIZER"
  lines << ""
end

#append_parser_tables(lines, indent, table_set) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, String indent, Tables::TableSet table_set) -> void

Parameters:



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/ibex/codegen/ruby.rb', line 203

def append_parser_tables(lines, indent, table_set)
  lines << "#{indent}PARSER_TABLES = { format_version: PARSER_TABLE_FORMAT_VERSION,"
  lines << "#{indent}                  grammar_digest: GRAMMAR_DIGEST,"
  lines << "#{indent}                  state_count: STATE_COUNT, production_count: PRODUCTION_COUNT,"
  lines << "#{indent}                  uses_locations: #{uses_locations?},"
  if @table_format == :compact
    default_codes = table_set.default_actions.map { |action| Tables::CompactActions.pack(action) }
    lines << "#{indent}                  compact_fast_driver: true, " \
             "compact_action_encoding: :signed, compact_default_actions: #{default_codes.inspect}.freeze,"
  end
  lines << "#{indent}                  cst: CST_METADATA, cst_kinds: CST_KIND_MODEL," if cst?
  lines << "#{indent}                  exact_expected_tokens: true," if @grammar.mode == :extended
  lines << "#{indent}                  tokens: TOKEN_IDS, token_names: TOKEN_NAMES, actions: ACTIONS,"
  lines << "#{indent}                  gotos: GOTOS, default_actions: DEFAULT_ACTIONS,"
  entries = entry_table_fields
  recovery = recovery_table_fields
  value_printers = @grammar.value_printers.empty? ? "" : " value_printers: VALUE_PRINTERS,"
  lines << "#{indent}                  productions: PRODUCTIONS,#{entries}#{value_printers} " \
           "#{recovery} eager_reductions: EAGER_REDUCTIONS, error_messages: ERROR_MESSAGES }.freeze"
  return unless shareable_parser_tables?

  lines << "#{indent}Ractor.make_shareable(PARSER_TABLES) " \
           "if defined?(Ractor) && Ractor.respond_to?(:make_shareable)"
end

#append_runtime(lines) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines) -> void

Parameters:

  • lines (Array[String])


150
151
152
153
154
155
156
157
# File 'lib/ibex/codegen/ruby.rb', line 150

def append_runtime(lines)
  if @embedded
    EMBEDDED_RUNTIME_SOURCES.each { |path| lines << embedded_source(path) }
  elsif @runtime_require
    lines << "require #{@runtime_require.inspect}"
  end
  lines << ""
end

#append_tables(lines) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines) -> void

Parameters:

  • lines (Array[String])


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/ibex/codegen/ruby.rb', line 166

def append_tables(lines)
  table_set = Tables.build(@automaton, format: @table_format)
  indent = "  "
  (lines, indent)
  lines << "#{indent}TOKEN_IDS = #{token_ids_literal}.freeze"
  lines << "#{indent}TOKEN_NAMES = #{token_names_literal}.freeze"
  (lines, indent)
  lines << "#{indent}ACTIONS = #{table_literal(table_set.actions)}"
  lines << "#{indent}GOTOS = #{table_literal(table_set.gotos)}"
  lines << "#{indent}DEFAULT_ACTIONS = #{table_set.default_actions.inspect}.freeze"
  eager_type = "Hash[Integer, [:reduce, Integer]]"
  lines << "#{indent}eager_reductions = #{eager_reductions_literal} # @type var eager_reductions: #{eager_type}"
  lines << "#{indent}EAGER_REDUCTIONS = eager_reductions.freeze #: #{eager_type}"
  lines << "#{indent}PRODUCTIONS = #{productions_literal}.freeze"
  append_value_printer_table(lines, indent)
  declaration = "#{indent}error_messages = #{error_messages_literal}"
  type = "Hash[Integer, String | { id: String, message: String }]"
  lines << "#{declaration} # @type var error_messages: #{type}"
  lines << "#{indent}ERROR_MESSAGES = error_messages.freeze #: #{type}"
  append_parser_tables(lines, indent, table_set)
  lines << "#{indent}def self.parser_tables = PARSER_TABLES"
  lines << "#{indent}DEBUG_ENABLED = #{@debug}"
  lines << ""
end

#append_user_code(lines, name, indent: 0) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, String name, ?indent: Integer) -> void

Parameters:

  • lines (Array[String])
  • name (String)
  • indent: (Integer) (defaults to: 0)


473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/ibex/codegen/ruby.rb', line 473

def append_user_code(lines, name, indent: 0)
  code = @grammar.user_code[name]
  return if code.nil? || code.empty?

  prefix = " " * indent
  chunks = @grammar.user_code_chunks.fetch(name, Array.new(0))
  if map_user_code?(name) && !chunks.empty?
    chunks.each do |chunk|
      location = chunk.location
      lines << "#{prefix}eval(#{chunk.code.dump}, binding, #{location[:file].inspect}, #{location[:line]})"
    end
  elsif @line_convert_all
    raise Ibex::Error, "(codegen):1:1: source locations are required to convert #{name} user code"
  else
    code.lines.each { |line| lines << "#{prefix}#{line.rstrip}" }
  end
  lines << ""
end

#class_parts[ Array[String], String ]

RBS:

  • () -> [Array[String], String]

Returns:

  • ([ Array[String], String ])


498
499
500
501
# File 'lib/ibex/codegen/ruby.rb', line 498

def class_parts
  parts = @grammar.class_name.split("::")
  [parts.take(parts.length - 1), parts.fetch(-1)]
end

#compact_production_flags(production) ⇒ Integer

RBS:

  • (IR::Production production) -> Integer

Parameters:

Returns:

  • (Integer)


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/ibex/codegen/ruby.rb', line 379

def compact_production_flags(production)
  return 0 unless action_method?(production)

  value = 0
  if @generated_action_abi.positional_values?(production)
    value |= Tables::CompactProductions::POSITIONAL_ACTION
  elsif @generated_action_abi.values_only?(production)
    value |= Tables::CompactProductions::VALUES_ACTION
    value |= Tables::CompactProductions::BORROWED_VALUES_ACTION if
      @generated_action_abi.borrowed_values?(production)
  else
    value |= Tables::CompactProductions::LOCATION_ACTION
  end
  value |= Tables::CompactProductions::COMPOSITION_ACTION if composed_action?(production)
  value
end

#compact_production_metadata_literalString

RBS:

  • () -> String

Returns:

  • (String)


397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/ibex/codegen/ruby.rb', line 397

def 
  entries = @grammar.productions.filter_map do |production|
    fields = [] #: Array[String]
    fields << "rhs: #{production.rhs.inspect}.freeze" if cst?
    action = production.action
    if action
      fields << "location_context_length: #{action.context_length}" if action.context_length.positive?
      names = action.named_refs.to_h { |reference| [reference[:name].to_sym, reference[:index]] }
      fields << "location_names: #{names.inspect}.freeze" unless names.empty?
    end
    next if fields.empty?

    "#{production.id} => { #{fields.join(', ')} }.freeze"
  end
  entries.empty? ? "Hash.new.freeze" : "{ #{entries.join(', ')} }.freeze"
end

#compact_productions_literalString

RBS:

  • () -> String

Returns:

  • (String)


369
370
371
372
373
374
375
376
# File 'lib/ibex/codegen/ruby.rb', line 369

def compact_productions_literal
  lhs_ids = @grammar.productions.map(&:lhs)
  lengths = @grammar.productions.map { |production| production.rhs.length }
  flags = @grammar.productions.map { |production| compact_production_flags(production) }
   = 
  "Ibex::Tables::CompactProductions.packed(#{packed_integers_literal(lhs_ids)}, " \
    "#{packed_integers_literal(lengths)}, #{packed_integers_literal(flags)}, metadata: #{})"
end

#cst?Boolean

RBS:

  • () -> bool

Returns:

  • (Boolean)


468
469
470
# File 'lib/ibex/codegen/ruby.rb', line 468

def cst?
  @grammar.options[:cst] == true
end

#cst_metadataHash[Symbol, untyped]

RBS:

  • () -> Hash[Symbol, untyped]

Returns:

  • (Hash[Symbol, untyped])


254
255
256
# File 'lib/ibex/codegen/ruby.rb', line 254

def 
  @cst_metadata ||= CSTMetadata.new(@grammar, trivia_policy: @cst_trivia).build
end

#deep_frozen_literal(value) ⇒ String

RBS:

  • (untyped value) -> String

Parameters:

  • value (Object)

Returns:

  • (String)


259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/ibex/codegen/ruby.rb', line 259

def deep_frozen_literal(value)
  case value
  when Array
    "[#{value.map { |item| deep_frozen_literal(item) }.join(', ')}].freeze"
  when Hash
    entries = value.map do |key, item|
      "#{deep_frozen_literal(key)} => #{deep_frozen_literal(item)}"
    end
    "{ #{entries.join(', ')} }.freeze"
  when String
    "#{value.inspect}.freeze"
  else
    value.inspect
  end
end

#eager_lexer_feedback_reduction(state) ⇒ IR::reduce_action?

RBS:

  • (IR::AutomatonState state) -> IR::reduce_action?

Parameters:

Returns:

  • (IR::reduce_action, nil)


431
432
433
434
435
436
437
438
439
440
441
# File 'lib/ibex/codegen/ruby.rb', line 431

def eager_lexer_feedback_reduction(state)
  candidates = state.actions.values
  candidates += [state.default_action] if state.default_action
  actions = candidates.reject { |action| action[:type] == :error }.uniq
  return unless actions.one?
  return unless actions.first[:type] == :reduce

  reduction = actions.first #: IR::reduce_action
  production = @grammar.productions.fetch(reduction.fetch(:production))
  reduction if production.action&.code&.match?(/\blexer_state\s*=/)
end

#eager_reductions_literalString

Reductions that are the sole non-error behavior of a state may run before requesting another lexer token. This enables parser actions to select lexer_state without speculative lookahead.

RBS:

  • () -> String

Returns:

  • (String)


418
419
420
421
422
423
424
425
426
427
428
# File 'lib/ibex/codegen/ruby.rb', line 418

def eager_reductions_literal
  return "{}" unless @grammar.lexer

  entries = @automaton.states.filter_map do |state|
    action = eager_lexer_feedback_reduction(state)
    next unless action

    "#{state.id} => [:reduce, #{action.fetch(:production)}].freeze"
  end
  "{ #{entries.join(', ')} }"
end

#embedded_source(relative_path) ⇒ String

RBS:

  • (String relative_path) -> String

Parameters:

  • relative_path (String)

Returns:

  • (String)


160
161
162
163
# File 'lib/ibex/codegen/ruby.rb', line 160

def embedded_source(relative_path)
  path = File.expand_path(relative_path, File.dirname(__FILE__))
  File.read(path).lines.reject { |line| line.start_with?("# frozen_string_literal:") }.join.rstrip
end

#external_token_expression(terminal) ⇒ String

RBS:

  • (IR::GrammarSymbol terminal) -> String

Parameters:

Returns:

  • (String)


310
311
312
313
314
315
316
# File 'lib/ibex/codegen/ruby.rb', line 310

def external_token_expression(terminal)
  conversion = @grammar.conversions[terminal.name]
  return "(#{conversion})" if conversion
  return terminal.name if terminal.name.start_with?("'", '"')

  ":#{terminal.name}"
end

#generateString

RBS:

  • () -> String

Returns:

  • (String)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/ibex/codegen/ruby.rb', line 123

def generate
  lines = [] #: Array[String]
  lines << "#!#{@executable}" if @executable
  lines.push("# frozen_string_literal: true", "# Generated by Ibex #{grammar_digest_comment}", "")
  append_user_code(lines, "header")
  append_runtime(lines)
  modules, class_name = class_parts
  modules.each { |name| lines << "module #{name}" }
  lines << "class #{class_name} < #{@superclass}"
  append_ast(lines)
  append_syntax(lines)
  append_tables(lines)
  append_parameter_initializer(lines)
  append_entry_methods(lines)
  append_lexer(lines)
  append_value_printers(lines)
  append_actions(lines)
  append_user_code(lines, "inner", indent: 2)
  lines << "end"
  modules.reverse_each { lines << "end" }
  append_user_code(lines, "footer")
  "#{lines.join("\n")}\n"
end

#grammar_digest_commentString

RBS:

  • () -> String

Returns:

  • (String)


504
505
506
# File 'lib/ibex/codegen/ruby.rb', line 504

def grammar_digest_comment
  @automaton.grammar_digest.delete_prefix("sha256:").chars.first(12).join
end

#map_user_code?(name) ⇒ Boolean

RBS:

  • (String name) -> bool

Parameters:

  • name (String)

Returns:

  • (Boolean)


493
494
495
# File 'lib/ibex/codegen/ruby.rb', line 493

def map_user_code?(name)
  @line_convert_all || (@line_convert && name == "inner")
end

#packed_integers_literal(values) ⇒ String

RBS:

  • (Array[Integer?] values) -> String

Parameters:

  • values (Array[Integer?])

Returns:

  • (String)


290
291
292
# File 'lib/ibex/codegen/ruby.rb', line 290

def packed_integers_literal(values)
  Tables::PackedIntegers.encode(values).inspect
end

#packed_signed_integers_literal(values) ⇒ String

RBS:

  • (Array[Integer?] values) -> String

Parameters:

  • values (Array[Integer?])

Returns:

  • (String)


295
296
297
# File 'lib/ibex/codegen/ruby.rb', line 295

def packed_signed_integers_literal(values)
  Tables::PackedIntegers.encode_signed(values).inspect
end

#plain_productions_literalString

RBS:

  • () -> String

Returns:

  • (String)


340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/ibex/codegen/ruby.rb', line 340

def plain_productions_literal
  entries = @grammar.productions.map do |production|
    generated_action = action_method?(production)
    action = generated_action ? ":_ibex_action_#{production.id}" : "nil"
    action_abi = production_action_abi_literal(production, generated_action)
    composition_action = composed_action?(production) ? ", composition_action: true" : ""
    cst_rhs = cst? ? ", rhs: #{production.rhs.inspect}.freeze" : ""
    location_context, named_locations = (production.action)
    "{ lhs: #{production.lhs}, length: #{production.rhs.length}, action: #{action}" \
      "#{action_abi}#{composition_action}#{cst_rhs}#{location_context}#{named_locations} }"
  end
  "[#{entries.join(', ')}]"
end

#production_action_abi_literal(production, generated_action) ⇒ String

RBS:

  • (IR::Production production, bool generated_action) -> String

Parameters:

Returns:

  • (String)


355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/ibex/codegen/ruby.rb', line 355

def production_action_abi_literal(production, generated_action)
  return "" unless generated_action
  return ", location_action: true" unless @generated_action_abi.values_only?(production)
  return ", positional_action: true" if @generated_action_abi.positional_values?(production)

  borrowed = if @generated_action_abi.borrowed_values?(production)
               ", borrowed_values_action: true"
             else
               ""
             end
  ", values_action: true#{borrowed}"
end

#production_location_metadata(action) ⇒ [ String, String ]

RBS:

  • (IR::Action? action) -> [String, String]

Parameters:

Returns:

  • ([ String, String ])


444
445
446
447
448
449
450
451
# File 'lib/ibex/codegen/ruby.rb', line 444

def (action)
  return ["", ""] unless action

  context = action.context_length.positive? ? ", location_context_length: #{action.context_length}" : ""
  names = action.named_refs.to_h { |reference| [reference[:name].to_sym, reference[:index]] }
  named = names.empty? ? "" : ", location_names: #{names.inspect}.freeze"
  [context, named]
end

#productions_literalString

RBS:

  • () -> String

Returns:

  • (String)


333
334
335
336
337
# File 'lib/ibex/codegen/ruby.rb', line 333

def productions_literal
  return compact_productions_literal if @table_format == :compact

  plain_productions_literal
end

#shareable_parser_tables?Boolean

Custom conversion expressions can return caller-owned mutable or inherently unshareable objects, so only standard token mappings are safe to freeze transitively.

RBS:

  • () -> bool

Returns:

  • (Boolean)


249
250
251
# File 'lib/ibex/codegen/ruby.rb', line 249

def shareable_parser_tables?
  @grammar.conversions.empty?
end

#symbol_names_literalString

RBS:

  • () -> String

Returns:

  • (String)


327
328
329
330
# File 'lib/ibex/codegen/ruby.rb', line 327

def symbol_names_literal
  entries = @grammar.symbols.map { |symbol| "#{symbol.id} => #{symbol.name.inspect}" }
  "{ #{entries.join(', ')} }"
end

#table_literal(table) ⇒ String

RBS:

  • (untyped table) -> String

Parameters:

  • table (Object)

Returns:

  • (String)


276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/ibex/codegen/ruby.rb', line 276

def table_literal(table)
  if table.is_a?(Tables::CompactActions)
    return "Ibex::Tables::CompactActions.packed(#{packed_integers_literal(table.offsets)}, " \
           "#{packed_signed_integers_literal(table.codes)}, #{packed_integers_literal(table.checks)}, " \
           "row_count: #{table.row_count}, encoding: :signed, column_count: #{table.column_count})"
  end
  return "#{table.inspect}.freeze" unless table.is_a?(Tables::Compact)

  "Ibex::Tables::Compact.packed(#{packed_integers_literal(table.offsets)}, " \
    "#{packed_integers_literal(table.values)}, #{packed_integers_literal(table.checks)}, " \
    "row_count: #{table.row_count}, dense_width: #{table.dense_width})"
end

#token_ids_literalString

RBS:

  • () -> String

Returns:

  • (String)


300
301
302
303
304
305
306
307
# File 'lib/ibex/codegen/ruby.rb', line 300

def token_ids_literal
  pairs = @grammar.terminals.filter_map do |terminal|
    next if terminal.id.zero? || terminal.name == "error"

    "#{external_token_expression(terminal)} => #{terminal.id}"
  end
  "{ #{pairs.join(', ')} }"
end

#token_names_literalString

RBS:

  • () -> String

Returns:

  • (String)


319
320
321
322
323
324
# File 'lib/ibex/codegen/ruby.rb', line 319

def token_names_literal
  entries = @grammar.terminals.map do |terminal|
    "#{terminal.id} => #{(terminal.display_name || terminal.name).inspect}"
  end
  "{ #{entries.join(', ')} }"
end

#uses_locations?Boolean

RBS:

  • () -> bool

Returns:

  • (Boolean)


454
455
456
457
458
459
# File 'lib/ibex/codegen/ruby.rb', line 454

def uses_locations?
  cst? || @grammar.productions.any? do |production|
    action = production.action
    action && (!action.composition.nil? || action_references_locations?(production, action))
  end
end