Class: Ibex::IR::Validator::GrammarDocument

Inherits:
Base
  • Object
show all
Defined in:
lib/ibex/ir/validator/grammar.rb,
sig/ibex/ir/validator/grammar.rbs

Overview

Structural and referential validation for a versioned Grammar IR JSON object. rubocop:disable Metrics/ClassLength -- inline type contracts accompany one cohesive document validator.

Constant Summary collapse

ROOT_REQUIRED =

Returns:

  • (Array[String])
%w[
  ibex_ir schema_version class_name superclass start expect options symbols productions user_code
  conversions warnings
].freeze
ROOT_OPTIONAL =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[user_code_chunks expect_rr].freeze
V2_ROOT_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[source_provenance migration].freeze
V2_ROOT_OPTIONAL =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[params printers tests recovery lexer mode starts].freeze
SYMBOL_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[id name kind reserved prec loc].freeze
SYMBOL_OPTIONAL =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[display_name semantic_type].freeze
V2_SYMBOL_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[doc].freeze
PRODUCTION_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[id lhs rhs action prec_override origin].freeze
V2_PRODUCTION_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[doc expansion].freeze
V2_ACTION_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[composition].freeze
ORIGIN_KINDS =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[
  optional_expansion star_expansion plus_expansion separated_list_expansion group_expansion
].freeze
RUBY_KEYWORDS =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[
  __ENCODING__ __FILE__ __LINE__ alias and begin break case class def defined do else elsif end ensure false for
  if in module next nil not or redo rescue retry return self super then true undef unless until when while yield
].freeze

Constants inherited from Base

Base::POSITION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#array, #boolean, #child_path, #enum, #field, #integer, #invalid, #literal, #location, #metadata, #nonempty_string, #nonnegative_integer, #nullable_string, #object, #positive_integer, #record, #string

Constructor Details

#initialize(data, path: "$", version: data.fetch("schema_version")) ⇒ GrammarDocument

Returns a new instance of GrammarDocument.

RBS:

  • (Hash[String, untyped] data, ?path: String, ?version: Integer) -> void

Parameters:

  • data (Hash[String, untyped])
  • path: (String) (defaults to: "$")
  • version: (Integer) (defaults to: data.fetch("schema_version"))


39
40
41
42
43
44
45
46
47
# File 'lib/ibex/ir/validator/grammar.rb', line 39

def initialize(data, path: "$", version: data.fetch("schema_version"))
  super()
  @data = data
  @path = path
  @version = version
  @symbols_by_id = {}
  @symbols_by_name = {}
  @productions_by_id = {}
end

Instance Attribute Details

#productions_by_idHash[Integer, Hash[String, untyped]] (readonly)

Signature:

  • Hash[Integer, Hash[String, untyped]]

Returns:

  • (Hash[Integer, Hash[String, untyped]])


32
33
34
# File 'lib/ibex/ir/validator/grammar.rb', line 32

def productions_by_id
  @productions_by_id
end

#symbols_by_idHash[Integer, Hash[String, untyped]] (readonly)

Signature:

  • Array[String]

Returns:

  • (Hash[Integer, Hash[String, untyped]])


30
31
32
# File 'lib/ibex/ir/validator/grammar.rb', line 30

def symbols_by_id
  @symbols_by_id
end

#symbols_by_nameHash[String, Hash[String, untyped]] (readonly)

Signature:

  • Hash[String, Hash[String, untyped]]

Returns:

  • (Hash[String, Hash[String, untyped]])


31
32
33
# File 'lib/ibex/ir/validator/grammar.rb', line 31

def symbols_by_name
  @symbols_by_name
end

Instance Method Details

#validateself

RBS:

  • () -> self

Returns:

  • (self)


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ibex/ir/validator/grammar.rb', line 50

def validate
  required = ROOT_REQUIRED + (@version >= 2 ? V2_ROOT_REQUIRED : [])
  optional = ROOT_OPTIONAL + (@version >= 2 ? V2_ROOT_OPTIONAL : [])
  record(@data, @path, required, optional)
  validate_envelope
  validate_header
  validate_options
  validate_parser_parameters if @data.key?("params")
  validate_symbols
  validate_value_printers if @data.key?("printers")
  validate_grammar_tests if @data.key?("tests")
  validate_reserved_symbols
  validate_start
  validate_lexer if @data.key?("lexer")
  validate_recovery if @data.key?("recovery")
  validate_productions
  validate_string_map(@data["user_code"], "#{@path}.user_code")
  validate_string_map(@data["conversions"], "#{@path}.conversions")
  validate_warnings
  validate_user_code_chunks if @data.key?("user_code_chunks")
  if @version >= 2
    validate_source_provenance(@data["source_provenance"], "#{@path}.source_provenance")
    validate_migration
  end
  self
end

#validate_action(value, path, rhs_length:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, rhs_length: Integer) -> void

Parameters:

  • value (Object)
  • path (String)
  • rhs_length: (Integer)


340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/ibex/ir/validator/grammar.rb', line 340

def validate_action(value, path, rhs_length:)
  return if value.nil?

  required = %w[code loc named_refs context_length] + (@version >= 2 ? V2_ACTION_REQUIRED : [])
  action = record(value, path, required)
  string(action["code"], "#{path}.code")
  location(action["loc"], "#{path}.loc", nullable: false)
  context_length = nonnegative_integer(action["context_length"], "#{path}.context_length")
  validate_named_refs(action["named_refs"], "#{path}.named_refs", limit: [rhs_length, context_length].max)
  return unless @version >= 2

  validate_action_composition(
    action["composition"], "#{path}.composition", rhs_length: rhs_length
  )
end

#validate_action_composition(value, path, rhs_length:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, rhs_length: Integer) -> void

Parameters:

  • value (Object)
  • path (String)
  • rhs_length: (Integer)


526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
# File 'lib/ibex/ir/validator/grammar.rb', line 526

def validate_action_composition(value, path, rhs_length:)
  return if value.nil?

  composition = record(value, path, %w[strategy fragments], %w[plan])
  literal(composition["strategy"], "#{path}.strategy", "sequence")
  fragments = array(composition["fragments"], "#{path}.fragments")
  invalid("#{path}.fragments", "must not be empty") if fragments.empty?
  fragments.each_with_index do |value, index|
    fragment_path = "#{path}.fragments[#{index}]"
    fragment = record(value, fragment_path, %w[kind source])
    enum(fragment["kind"], "#{fragment_path}.kind", %w[rule inline])
    validate_source_provenance(fragment["source"], "#{fragment_path}.source")
  end
  validate_action_composition_plan(composition["plan"], "#{path}.plan", rhs_length, fragments) if
    composition.key?("plan")
end

#validate_action_composition_plan(value, path, rhs_length, fragments) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, Integer rhs_length, Array[untyped] fragments) -> void

Parameters:

  • value (Object)
  • path (String)
  • rhs_length (Integer)
  • fragments (Array[untyped])


544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/ibex/ir/validator/grammar.rb', line 544

def validate_action_composition_plan(value, path, rhs_length, fragments)
  plan = record(value, path, %w[version physical steps])
  literal(plan["version"], "#{path}.version", 1)
  physical = nonnegative_integer(plan["physical"], "#{path}.physical")
  invalid("#{path}.physical", "must equal production RHS length #{rhs_length}") unless physical == rhs_length
  steps = array(plan["steps"], "#{path}.steps")
  invalid("#{path}.steps", "must not be empty") if steps.empty?
  invalid("#{path}.steps", "must align one-to-one with fragments") unless steps.length == fragments.length
  steps.each_with_index do |step, index|
    validate_action_composition_step(step, "#{path}.steps[#{index}]", physical, index)
    next if step["kind"] == fragments.fetch(index)["kind"]

    invalid("#{path}.steps[#{index}].kind", "must match the corresponding fragment")
  end
end

#validate_action_composition_step(value, path, physical, step_index) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, Integer physical, Integer step_index) -> void

Parameters:

  • value (Object)
  • path (String)
  • physical (Integer)
  • step_index (Integer)


561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# File 'lib/ibex/ir/validator/grammar.rb', line 561

def validate_action_composition_step(value, path, physical, step_index)
  step = record(
    value, path,
    %w[kind rule code loc named_refs context_length inputs stack_inputs lookahead result_var],
    %w[result_type]
  )
  kind = enum(step["kind"], "#{path}.kind", %w[rule inline])
  rule = nullable_string(step["rule"], "#{path}.rule")
  invalid("#{path}.rule", "must name an inline rule") if kind == "inline" && (!rule || rule.empty?)
  nullable_string(step["code"], "#{path}.code")
  location(step["loc"], "#{path}.loc", nullable: false)
  context = nonnegative_integer(step["context_length"], "#{path}.context_length")
  inputs = array(step["inputs"], "#{path}.inputs")
  limit = physical + step_index
  validate_action_slots(inputs, "#{path}.inputs", limit)
  stack_inputs = array(step["stack_inputs"], "#{path}.stack_inputs")
  validate_action_slots(stack_inputs, "#{path}.stack_inputs", limit)
  validate_named_refs(step["named_refs"], "#{path}.named_refs", limit: [inputs.length, context].max)
  validate_action_lookahead(step["lookahead"], "#{path}.lookahead", physical)
  boolean(step["result_var"], "#{path}.result_var")
  nullable_string(step["result_type"], "#{path}.result_type")
end

#validate_action_lookahead(value, path, physical) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, Integer physical) -> void

Parameters:

  • value (Object)
  • path (String)
  • physical (Integer)


594
595
596
597
598
599
# File 'lib/ibex/ir/validator/grammar.rb', line 594

def validate_action_lookahead(value, path, physical)
  return if value.nil?

  boundary = nonnegative_integer(value, path)
  invalid(path, "must reference a physical slot below #{physical}") if boundary >= physical
end

#validate_action_slots(inputs, path, limit) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[untyped] inputs, String path, Integer limit) -> void

Parameters:

  • inputs (Array[untyped])
  • path (String)
  • limit (Integer)


585
586
587
588
589
590
591
# File 'lib/ibex/ir/validator/grammar.rb', line 585

def validate_action_slots(inputs, path, limit)
  inputs.each_with_index do |input, index|
    slot_path = "#{path}[#{index}]"
    slot = nonnegative_integer(input, slot_path)
    invalid(slot_path, "must reference an available slot below #{limit}") if slot >= limit
  end
end

#validate_byte_span(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


469
470
471
472
473
474
475
476
# File 'lib/ibex/ir/validator/grammar.rb', line 469

def validate_byte_span(value, path)
  return if value.nil?

  span = record(value, path, %w[start end])
  start_byte = nonnegative_integer(span["start"], "#{path}.start")
  end_byte = nonnegative_integer(span["end"], "#{path}.end")
  invalid("#{path}.end", "must be greater than or equal to start") if end_byte < start_byte
end

#validate_envelopevoid

This method returns an undefined value.

RBS:

  • () -> void



80
81
82
83
# File 'lib/ibex/ir/validator/grammar.rb', line 80

def validate_envelope
  literal(@data["ibex_ir"], "#{@path}.ibex_ir", "grammar")
  literal(@data["schema_version"], "#{@path}.schema_version", @version)
end

#validate_expansion(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


495
496
497
498
499
500
501
502
503
504
# File 'lib/ibex/ir/validator/grammar.rb', line 495

def validate_expansion(value, path)
  return if value.nil?

  expansion = record(value, path, %w[parameter inline include_chain])
  validate_parameter_expansion(expansion["parameter"], "#{path}.parameter")
  validate_inline_expansion(expansion["inline"], "#{path}.inline")
  array(expansion["include_chain"], "#{path}.include_chain").each_with_index do |source, index|
    validate_source_provenance(source, "#{path}.include_chain[#{index}]", nullable: false)
  end
end

#validate_grammar_testsvoid

This method returns an undefined value.

RBS:

  • () -> void



140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/ibex/ir/validator/grammar.rb', line 140

def validate_grammar_tests
  invalid("#{@path}.mode", "must be extended for grammar tests") unless @data["mode"] == "extended"
  seen = {} #: Hash[[String, String], bool]
  array(@data["tests"], "#{@path}.tests").each_with_index do |value, index|
    path = "#{@path}.tests[#{index}]"
    test = record(value, path, %w[expectation source loc])
    expectation = enum(test["expectation"], "#{path}.expectation", %w[accept reject])
    source = string(test["source"], "#{path}.source")
    key = [expectation, source] #: [String, String]
    invalid(path, "duplicates grammar test") if seen[key]
    seen[key] = true
    location(test["loc"], "#{path}.loc", nullable: false)
  end
end

#validate_headervoid

This method returns an undefined value.

RBS:

  • () -> void



86
87
88
89
90
91
92
93
# File 'lib/ibex/ir/validator/grammar.rb', line 86

def validate_header
  nonempty_string(@data["class_name"], "#{@path}.class_name")
  nullable_string(@data["superclass"], "#{@path}.superclass")
  nonempty_string(@data["start"], "#{@path}.start")
  enum(@data["mode"], "#{@path}.mode", %w[default extended]) if @data.key?("mode")
  nonnegative_integer(@data["expect"], "#{@path}.expect")
  nonnegative_integer(@data["expect_rr"], "#{@path}.expect_rr") if @data.key?("expect_rr")
end

#validate_inline_expansion(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


518
519
520
521
522
523
# File 'lib/ibex/ir/validator/grammar.rb', line 518

def validate_inline_expansion(value, path)
  return if value.nil?

  inline = record(value, path, %w[rule])
  nonempty_string(inline["rule"], "#{path}.rule")
end

#validate_lexervoid

This method returns an undefined value.

RBS:

  • () -> void



431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/ibex/ir/validator/grammar.rb', line 431

def validate_lexer
  value = @data.fetch("lexer")
  LexerDocument.new(value, path: "#{@path}.lexer").validate
  value.fetch("rules").each_with_index do |rule, index|
    next unless rule["kind"] == "token"

    name = rule["token"]
    symbol = @symbols_by_name[name]
    invalid("#{@path}.lexer.rules[#{index}].token", "references missing terminal #{name.inspect}") unless
      symbol&.fetch("kind") == "terminal"
  end
end

#validate_lhs(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


324
325
326
327
328
329
# File 'lib/ibex/ir/validator/grammar.rb', line 324

def validate_lhs(value, path)
  id = nonnegative_integer(value, path)
  symbol = @symbols_by_id[id]
  invalid(path, "references missing symbol id #{id}") unless symbol
  invalid(path, "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
end

#validate_migrationvoid

This method returns an undefined value.

RBS:

  • () -> void



479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/ibex/ir/validator/grammar.rb', line 479

def validate_migration
  path = "#{@path}.migration"
  value = @data["migration"]
  return if value.nil?

  migration = record(value, path, %w[from_schema_version unavailable])
  literal(migration["from_schema_version"], "#{path}.from_schema_version", 1)
  values = array(migration["unavailable"], "#{path}.unavailable")
  invalid("#{path}.unavailable", "must not be empty") if values.empty?
  values.each_with_index do |name, index|
    enum(name, "#{path}.unavailable[#{index}]", Migration::UNAVAILABLE_V1_METADATA)
  end
  invalid("#{path}.unavailable", "must contain unique names") unless values.uniq.length == values.length
end

#validate_multiple_starts(start) ⇒ void

This method returns an undefined value.

RBS:

  • (String start) -> void

Parameters:

  • start (String)


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/ibex/ir/validator/grammar.rb', line 216

def validate_multiple_starts(start)
  starts = array(@data["starts"], "#{@path}.starts")
  invalid("#{@path}.starts", "must not be empty") if starts.empty?
  invalid("#{@path}.starts[0]", "must equal start #{start.inspect}") unless starts.first == start
  invalid("#{@path}.mode", "must be extended for multiple start symbols") unless @data["mode"] == "extended"
  seen = {} #: Hash[String, bool]
  starts.each_with_index do |name, index|
    name = nonempty_string(name, "#{@path}.starts[#{index}]")
    invalid("#{@path}.starts[#{index}]", "duplicates start symbol #{name.inspect}") if seen[name]
    seen[name] = true
    definition = @symbols_by_name[name]
    invalid("#{@path}.starts[#{index}]", "references missing symbol #{name.inspect}") unless definition
    invalid("#{@path}.starts[#{index}]", "must reference a nonterminal") unless
      definition["kind"] == "nonterminal"
  end
end

#validate_named_refs(value, path, limit:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, limit: Integer) -> void

Parameters:

  • value (Object)
  • path (String)
  • limit: (Integer)


357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/ibex/ir/validator/grammar.rb', line 357

def validate_named_refs(value, path, limit:)
  names = {} #: Hash[String, bool]
  array(value, path).each_with_index do |entry, index|
    entry_path = "#{path}[#{index}]"
    reference = record(entry, entry_path, %w[name index])
    name = nonempty_string(reference["name"], "#{entry_path}.name")
    invalid("#{entry_path}.name", "duplicates named reference #{name.inspect}") if names.key?(name)
    names[name] = true
    reference_index = nonnegative_integer(reference["index"], "#{entry_path}.index")
    if reference_index >= limit
      invalid("#{entry_path}.index", "must be less than the action context length #{limit}")
    end
  end
end

#validate_node_annotation(value, path, rhs_length:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, rhs_length: Integer) -> void

Parameters:

  • value (Object)
  • path (String)
  • rhs_length: (Integer)


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/ibex/ir/validator/grammar.rb', line 304

def validate_node_annotation(value, path, rhs_length:)
  return if value.nil?

  node = record(value, path, %w[name fields loc])
  name = nonempty_string(node["name"], "#{path}.name")
  invalid("#{path}.name", "must be a Ruby constant identifier") unless name.match?(/\A[A-Z][A-Za-z0-9_]*\z/)
  fields = array(node["fields"], "#{path}.fields")
  invalid("#{path}.fields", "must contain #{rhs_length} entries") unless fields.length == rhs_length
  seen = {} #: Hash[String, bool]
  fields.each_with_index do |value, index|
    field = nonempty_string(value, "#{path}.fields[#{index}]")
    invalid("#{path}.fields[#{index}]", "must be a Ruby local identifier") unless
      field.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) && !RUBY_KEYWORDS.include?(field)
    invalid("#{path}.fields[#{index}]", "duplicates field #{field.inspect}") if seen[field]
    seen[field] = true
  end
  location(node["loc"], "#{path}.loc", nullable: false)
end

#validate_optionsvoid

This method returns an undefined value.

RBS:

  • () -> void



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ibex/ir/validator/grammar.rb', line 96

def validate_options
  path = "#{@path}.options"
  options = record(@data["options"], path, %w[result_var omit_action_call], %w[cst])
  boolean(options["result_var"], "#{path}.result_var")
  boolean(options["omit_action_call"], "#{path}.omit_action_call")
  return unless options.key?("cst")

  invalid("#{path}.cst", "requires schema_version 2") unless @version >= 2
  literal(options["cst"], "#{path}.cst", true)
  invalid("#{@path}.mode", "must be extended when CST construction is enabled") unless
    @data["mode"] == "extended"
end

#validate_origin(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


383
384
385
386
387
388
389
390
391
# File 'lib/ibex/ir/validator/grammar.rb', line 383

def validate_origin(value, path)
  origin = object(value, path)
  kind = string(field(origin, "kind", path), "#{path}.kind")
  optional = ORIGIN_KINDS.include?(kind) ? %w[expression] : [] # @type var optional: Array[String]
  record(origin, path, %w[kind loc], optional)
  enum(kind, "#{path}.kind", %w[user inline_action] + ORIGIN_KINDS)
  string(origin["expression"], "#{path}.expression") if origin.key?("expression")
  location(origin["loc"], "#{path}.loc", nullable: false)
end

#validate_parameter_expansion(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


507
508
509
510
511
512
513
514
515
# File 'lib/ibex/ir/validator/grammar.rb', line 507

def validate_parameter_expansion(value, path)
  return if value.nil?

  parameter = record(value, path, %w[rule arguments])
  nonempty_string(parameter["rule"], "#{path}.rule")
  array(parameter["arguments"], "#{path}.arguments").each_with_index do |argument, index|
    nonempty_string(argument, "#{path}.arguments[#{index}]")
  end
end

#validate_parser_parametersvoid

This method returns an undefined value.

RBS:

  • () -> void



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ibex/ir/validator/grammar.rb', line 110

def validate_parser_parameters
  names = {} #: Hash[String, bool]
  array(@data["params"], "#{@path}.params").each_with_index do |value, index|
    path = "#{@path}.params[#{index}]"
    parameter = record(value, path, %w[name semantic_type])
    name = nonempty_string(parameter["name"], "#{path}.name")
    invalid("#{path}.name", "must be a Ruby local identifier") unless name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/)
    invalid("#{path}.name", "must not be a Ruby keyword") if RUBY_KEYWORDS.include?(name)
    invalid("#{path}.name", "duplicates parameter #{name.inspect}") if names.key?(name)
    names[name] = true
    (parameter["semantic_type"], "#{path}.semantic_type")
  end
end

#validate_precedence(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


182
183
184
185
186
187
188
# File 'lib/ibex/ir/validator/grammar.rb', line 182

def validate_precedence(value, path)
  return if value.nil?

  precedence = record(value, path, %w[associativity level])
  enum(precedence["associativity"], "#{path}.associativity", %w[left right nonassoc precedence])
  positive_integer(precedence["level"], "#{path}.level")
end

#validate_precedence_override(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


373
374
375
376
377
378
379
380
# File 'lib/ibex/ir/validator/grammar.rb', line 373

def validate_precedence_override(value, path)
  return if value.nil?

  id = nonnegative_integer(value, path)
  symbol = @symbols_by_id[id]
  invalid(path, "references missing symbol id #{id}") unless symbol
  invalid(path, "must reference a terminal") unless symbol["kind"] == "terminal"
end

#validate_production(value, index) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, Integer index) -> void

Parameters:

  • value (Object)
  • index (Integer)


283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/ibex/ir/validator/grammar.rb', line 283

def validate_production(value, index)
  path = "#{@path}.productions[#{index}]"
  required = PRODUCTION_REQUIRED + (@version >= 2 ? V2_PRODUCTION_REQUIRED : [])
  optional = @version >= 2 ? %w[node] : Array.new(0) #: Array[String]
  production = record(value, path, required, optional)
  id = nonnegative_integer(production["id"], "#{path}.id")
  invalid("#{path}.id", "must equal its array index #{index}") unless id == index
  @productions_by_id[id] = production
  validate_lhs(production["lhs"], "#{path}.lhs")
  validate_rhs(production["rhs"], "#{path}.rhs")
  validate_action(production["action"], "#{path}.action", rhs_length: production["rhs"].length)
  validate_precedence_override(production["prec_override"], "#{path}.prec_override")
  validate_origin(production["origin"], "#{path}.origin")
  return unless @version >= 2

  nullable_string(production["doc"], "#{path}.doc")
  validate_expansion(production["expansion"], "#{path}.expansion")
  validate_node_annotation(production["node"], "#{path}.node", rhs_length: production["rhs"].length)
end

#validate_productionsvoid

This method returns an undefined value.

RBS:

  • () -> void



276
277
278
279
280
# File 'lib/ibex/ir/validator/grammar.rb', line 276

def validate_productions
  array(@data["productions"], "#{@path}.productions").each_with_index do |value, index|
    validate_production(value, index)
  end
end

#validate_recoveryvoid

This method returns an undefined value.

RBS:

  • () -> void



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/ibex/ir/validator/grammar.rb', line 234

def validate_recovery
  path = "#{@path}.recovery"
  recovery = record(@data["recovery"], path, %w[sync_tokens on_error_reduce])
  invalid("#{@path}.mode", "must be extended for recovery declarations") unless @data["mode"] == "extended"
  validate_recovery_symbols(recovery["sync_tokens"], "#{path}.sync_tokens", kind: "terminal")
  groups = array(recovery["on_error_reduce"], "#{path}.on_error_reduce")
  seen = {} #: Hash[String, bool]
  groups.each_with_index do |group, index|
    group_path = "#{path}.on_error_reduce[#{index}]"
    names = array(group, group_path)
    invalid(group_path, "must not be empty") if names.empty?
    names.each_with_index do |name, name_index|
      validate_recovery_symbol(
        name, "#{group_path}[#{name_index}]", kind: "nonterminal", seen: seen
      )
    end
  end
  return unless array(recovery["sync_tokens"], "#{path}.sync_tokens").empty? && groups.empty?

  invalid(path, "must declare at least one recovery policy")
end

#validate_recovery_symbol(value, path, kind:, seen:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, kind: String, seen: Hash[String, bool]) -> void

Parameters:

  • value (Object)
  • path (String)
  • kind: (String)
  • seen: (Hash[String, bool])


265
266
267
268
269
270
271
272
273
# File 'lib/ibex/ir/validator/grammar.rb', line 265

def validate_recovery_symbol(value, path, kind:, seen:)
  name = nonempty_string(value, path)
  invalid(path, "duplicates symbol #{name.inspect}") if seen[name]
  seen[name] = true
  symbol = @symbols_by_name[name]
  invalid(path, "references missing symbol #{name.inspect}") unless symbol
  invalid(path, "must reference a #{kind}") unless symbol["kind"] == kind
  invalid(path, "must not reference the synthetic error token") if name == "error"
end

#validate_recovery_symbols(values, path, kind:) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped values, String path, kind: String) -> void

Parameters:

  • values (Object)
  • path (String)
  • kind: (String)


257
258
259
260
261
262
# File 'lib/ibex/ir/validator/grammar.rb', line 257

def validate_recovery_symbols(values, path, kind:)
  seen = {} #: Hash[String, bool]
  array(values, path).each_with_index do |name, index|
    validate_recovery_symbol(name, "#{path}[#{index}]", kind: kind, seen: seen)
  end
end

#validate_reserved_symbol(id, name) ⇒ void

This method returns an undefined value.

RBS:

  • (Integer id, String name) -> void

Parameters:

  • id (Integer)
  • name (String)


197
198
199
200
201
202
# File 'lib/ibex/ir/validator/grammar.rb', line 197

def validate_reserved_symbol(id, name)
  symbol = @symbols_by_id[id]
  invalid("#{@path}.symbols", "must contain reserved symbol #{name.inspect} at id #{id}") unless symbol
  invalid("#{@path}.symbols[#{id}]", "must be reserved terminal #{name.inspect}") unless
    symbol["name"] == name && symbol["kind"] == "terminal" && symbol["reserved"]
end

#validate_reserved_symbolsvoid

This method returns an undefined value.

RBS:

  • () -> void



191
192
193
194
# File 'lib/ibex/ir/validator/grammar.rb', line 191

def validate_reserved_symbols
  validate_reserved_symbol(0, "$eof")
  validate_reserved_symbol(1, "error")
end

#validate_rhs(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


332
333
334
335
336
337
# File 'lib/ibex/ir/validator/grammar.rb', line 332

def validate_rhs(value, path)
  array(value, path).each_with_index do |id, index|
    id = nonnegative_integer(id, "#{path}[#{index}]")
    invalid("#{path}[#{index}]", "references missing symbol id #{id}") unless @symbols_by_id.key?(id)
  end
end

#validate_source_provenance(value, path, nullable: true) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path, ?nullable: bool) -> void

Parameters:

  • value (Object)
  • path (String)
  • nullable: (Boolean) (defaults to: true)


459
460
461
462
463
464
465
466
# File 'lib/ibex/ir/validator/grammar.rb', line 459

def validate_source_provenance(value, path, nullable: true)
  return if nullable && value.nil?

  source = record(value, path, %w[file root byte_span])
  nullable_string(source["file"], "#{path}.file")
  nullable_string(source["root"], "#{path}.root")
  validate_byte_span(source["byte_span"], "#{path}.byte_span")
end

#validate_startvoid

This method returns an undefined value.

RBS:

  • () -> void



205
206
207
208
209
210
211
212
213
# File 'lib/ibex/ir/validator/grammar.rb', line 205

def validate_start
  start = @data["start"]
  symbol = @symbols_by_name[start]
  invalid("#{@path}.start", "references missing symbol #{start.inspect}") unless symbol
  invalid("#{@path}.start", "must reference a nonterminal") unless symbol["kind"] == "nonterminal"
  return unless @data.key?("starts")

  validate_multiple_starts(start)
end

#validate_string_map(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


394
395
396
397
398
399
# File 'lib/ibex/ir/validator/grammar.rb', line 394

def validate_string_map(value, path)
  object(value, path).each do |key, item|
    string(key, path)
    string(item, child_path(path, key))
  end
end

#validate_symbol(value, index) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, Integer index) -> void

Parameters:

  • value (Object)
  • index (Integer)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ibex/ir/validator/grammar.rb', line 163

def validate_symbol(value, index)
  path = "#{@path}.symbols[#{index}]"
  required = SYMBOL_REQUIRED + (@version >= 2 ? V2_SYMBOL_REQUIRED : [])
  symbol = record(value, path, required, SYMBOL_OPTIONAL)
  id = nonnegative_integer(symbol["id"], "#{path}.id")
  invalid("#{path}.id", "must equal its array index #{index}") unless id == index
  name = nonempty_string(symbol["name"], "#{path}.name")
  invalid("#{path}.name", "duplicates symbol #{name.inspect}") if @symbols_by_name.key?(name)
  @symbols_by_id[id] = symbol
  @symbols_by_name[name] = symbol
  enum(symbol["kind"], "#{path}.kind", %w[terminal nonterminal])
  boolean(symbol["reserved"], "#{path}.reserved")
  validate_precedence(symbol["prec"], "#{path}.prec")
  location(symbol["loc"], "#{path}.loc")
  SYMBOL_OPTIONAL.each { |key| (symbol[key], "#{path}.#{key}") if symbol.key?(key) }
  nullable_string(symbol["doc"], "#{path}.doc") if @version >= 2
end

#validate_symbolsvoid

This method returns an undefined value.

RBS:

  • () -> void



156
157
158
159
160
# File 'lib/ibex/ir/validator/grammar.rb', line 156

def validate_symbols
  array(@data["symbols"], "#{@path}.symbols").each_with_index do |value, index|
    validate_symbol(value, index)
  end
end

#validate_user_code_chunksvoid

This method returns an undefined value.

RBS:

  • () -> void



445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/ibex/ir/validator/grammar.rb', line 445

def validate_user_code_chunks
  path = "#{@path}.user_code_chunks"
  object(@data["user_code_chunks"], path).each do |key, chunks|
    string(key, path)
    array(chunks, child_path(path, key)).each_with_index do |chunk, index|
      chunk_path = "#{child_path(path, key)}[#{index}]"
      chunk = record(chunk, chunk_path, %w[code loc])
      string(chunk["code"], "#{chunk_path}.code")
      location(chunk["loc"], "#{chunk_path}.loc", nullable: false)
    end
  end
end

#validate_value_printersvoid

This method returns an undefined value.

RBS:

  • () -> void



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ibex/ir/validator/grammar.rb', line 125

def validate_value_printers
  names = {} #: Hash[String, bool]
  array(@data["printers"], "#{@path}.printers").each_with_index do |value, index|
    path = "#{@path}.printers[#{index}]"
    printer = record(value, path, %w[symbol code loc])
    name = nonempty_string(printer["symbol"], "#{path}.symbol")
    invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
    invalid("#{path}.symbol", "duplicates printer for #{name.inspect}") if names.key?(name)
    names[name] = true
    string(printer["code"], "#{path}.code")
    location(printer["loc"], "#{path}.loc", nullable: false)
  end
end

#validate_warning(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (untyped value, String path) -> void

Parameters:

  • value (Object)
  • path (String)


409
410
411
412
413
414
415
416
# File 'lib/ibex/ir/validator/grammar.rb', line 409

def validate_warning(value, path)
  warning = record(value, path, %w[type loc], %w[symbol production original])
  nonempty_string(warning["type"], "#{path}.type")
  location(warning["loc"], "#{path}.loc")
  validate_warning_symbol(warning, path) if warning.key?("symbol")
  validate_warning_production(warning, "production", path) if warning.key?("production")
  validate_warning_production(warning, "original", path) if warning.key?("original")
end

#validate_warning_production(warning, field_name, path) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[String, untyped] warning, String field_name, String path) -> void

Parameters:

  • warning (Hash[String, untyped])
  • field_name (String)
  • path (String)


425
426
427
428
# File 'lib/ibex/ir/validator/grammar.rb', line 425

def validate_warning_production(warning, field_name, path)
  id = nonnegative_integer(warning[field_name], "#{path}.#{field_name}")
  invalid("#{path}.#{field_name}", "references missing production id #{id}") unless @productions_by_id.key?(id)
end

#validate_warning_symbol(warning, path) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[String, untyped] warning, String path) -> void

Parameters:

  • warning (Hash[String, untyped])
  • path (String)


419
420
421
422
# File 'lib/ibex/ir/validator/grammar.rb', line 419

def validate_warning_symbol(warning, path)
  name = nonempty_string(warning["symbol"], "#{path}.symbol")
  invalid("#{path}.symbol", "references missing symbol #{name.inspect}") unless @symbols_by_name.key?(name)
end

#validate_warningsvoid

This method returns an undefined value.

RBS:

  • () -> void



402
403
404
405
406
# File 'lib/ibex/ir/validator/grammar.rb', line 402

def validate_warnings
  array(@data["warnings"], "#{@path}.warnings").each_with_index do |value, index|
    validate_warning(value, "#{@path}.warnings[#{index}]")
  end
end