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 current 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 source_provenance parser_contract
].freeze
ROOT_OPTIONAL =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[
  user_code_chunks expect_rr 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
SYMBOL_METADATA_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
PRODUCTION_METADATA_REQUIRED =

Signature:

  • Array[String]

Returns:

  • (Array[String])
%w[doc expansion].freeze
ACTION_COMPOSITION_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:

  • (json_object data, ?path: String, ?version: Integer) -> void

Parameters:

  • data (json_object)
  • 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, json_object] (readonly)

Signature:

  • Hash[Integer, json_object]

Returns:

  • (Hash[Integer, json_object])


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

def productions_by_id
  @productions_by_id
end

#symbols_by_idHash[Integer, json_object] (readonly)

Signature:

  • Array[String]

Returns:

  • (Hash[Integer, json_object])


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

def symbols_by_id
  @symbols_by_id
end

#symbols_by_nameHash[String, json_object] (readonly)

Signature:

  • Hash[String, json_object]

Returns:

  • (Hash[String, json_object])


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
# File 'lib/ibex/ir/validator/grammar.rb', line 50

def validate
  validate_root_record
  validate_envelope
  validate_header
  validate_options
  validate_parser_parameters_if_present
  validate_symbols
  
  validate_reserved_symbols
  validate_start
  validate_productions
  validate_string_map(@data["user_code"], "#{@path}.user_code")
  validate_string_map(@data["conversions"], "#{@path}.conversions")
  validate_warnings
  
  self
end

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

This method returns an undefined value.

RBS:

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

Parameters:

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


354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/ibex/ir/validator/grammar.rb', line 354

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

  required = %w[code loc named_refs context_length] + ACTION_COMPOSITION_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)
  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:

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

Parameters:

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


554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/ibex/ir/validator/grammar.rb', line 554

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:

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

Parameters:

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


572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/ibex/ir/validator/grammar.rb', line 572

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)
    step_record = step #: json_object
    fragment_record = fragments.fetch(index) #: json_object
    next if step_record["kind"] == fragment_record["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:

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

Parameters:

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


591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
# File 'lib/ibex/ir/validator/grammar.rb', line 591

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:

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

Parameters:

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


624
625
626
627
628
629
# File 'lib/ibex/ir/validator/grammar.rb', line 624

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[json_value] inputs, String path, Integer limit) -> void

Parameters:

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


615
616
617
618
619
620
621
# File 'lib/ibex/ir/validator/grammar.rb', line 615

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


484
485
486
487
488
489
490
491
# File 'lib/ibex/ir/validator/grammar.rb', line 484

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_contract_entry(value, path, allowed) ⇒ Hash[String, untyped]

RBS:

  • (untyped value, String, Array[String]) -> Hash[String, untyped]

Parameters:

  • value (Object)
  • (String)
  • (Array[String])

Returns:

  • (Hash[String, untyped])


508
509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/ibex/ir/validator/grammar.rb', line 508

def validate_contract_entry(value, path, allowed)
  entry = record(value, path, %w[value explicit loc])
  explicit = boolean(entry["explicit"], "#{path}.explicit")
  unless explicit
    literal(entry["value"], "#{path}.value", nil)
    literal(entry["loc"], "#{path}.loc", nil)
    return entry
  end

  enum(entry["value"], "#{path}.value", allowed)
  location(entry["loc"], "#{path}.loc", nullable: false)
  entry
end

#validate_envelopevoid

This method returns an undefined value.

RBS:

  • () -> void



96
97
98
99
# File 'lib/ibex/ir/validator/grammar.rb', line 96

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


523
524
525
526
527
528
529
530
531
532
# File 'lib/ibex/ir/validator/grammar.rb', line 523

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



155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ibex/ir/validator/grammar.rb', line 155

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



102
103
104
105
106
107
108
109
# File 'lib/ibex/ir/validator/grammar.rb', line 102

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


546
547
548
549
550
551
# File 'lib/ibex/ir/validator/grammar.rb', line 546

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



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

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

    name = nonempty_string(rule_record["token"], "#{@path}.lexer.rules[#{index}].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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


338
339
340
341
342
343
# File 'lib/ibex/ir/validator/grammar.rb', line 338

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_multiple_starts(start) ⇒ void

This method returns an undefined value.

RBS:

  • (String start) -> void

Parameters:

  • start (String)


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/ibex/ir/validator/grammar.rb', line 231

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:

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

Parameters:

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


369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/ibex/ir/validator/grammar.rb', line 369

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:

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

Parameters:

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


318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/ibex/ir/validator/grammar.rb', line 318

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_optional_parser_metadatavoid

This method returns an undefined value.

RBS:

  • () -> void



76
77
78
79
80
81
82
# File 'lib/ibex/ir/validator/grammar.rb', line 76

def 
  validate_value_printers if @data.key?("printers")
  validate_grammar_tests if @data.key?("tests")
  validate_lexer if @data.key?("lexer")
  validate_recovery if @data.key?("recovery")
  validate_parser_contract
end

#validate_optional_source_metadatavoid

This method returns an undefined value.

RBS:

  • () -> void



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

def 
  validate_user_code_chunks if @data.key?("user_code_chunks")
  validate_source_provenance(@data["source_provenance"], "#{@path}.source_provenance")
end

#validate_optionsvoid

This method returns an undefined value.

RBS:

  • () -> void



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

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

  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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


395
396
397
398
399
400
401
402
403
# File 'lib/ibex/ir/validator/grammar.rb', line 395

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


535
536
537
538
539
540
541
542
543
# File 'lib/ibex/ir/validator/grammar.rb', line 535

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_contractvoid

This method returns an undefined value.

RBS:

  • () -> void



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

def validate_parser_contract
  path = "#{@path}.parser_contract"
  contract = record(@data["parser_contract"], path, %w[algorithm entries cst_trivia])
  validate_contract_entry(contract["algorithm"], "#{path}.algorithm", %w[slr lalr ielr lr1])
  validate_contract_entry(contract["entries"], "#{path}.entries", %w[shared isolated])
  cst = validate_contract_entry(
    contract["cst_trivia"], "#{path}.cst_trivia", %w[leading balanced drop]
  )
  return unless cst["explicit"]

  invalid("#{path}.cst_trivia", "requires options.cst") unless @data.dig("options", "cst") == true
end

#validate_parser_parametersvoid

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_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_parser_parameters_if_presentvoid

This method returns an undefined value.

RBS:

  • () -> void



85
86
87
# File 'lib/ibex/ir/validator/grammar.rb', line 85

def validate_parser_parameters_if_present
  validate_parser_parameters if @data.key?("params")
end

#validate_precedence(value, path) ⇒ void

This method returns an undefined value.

RBS:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


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

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


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

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:

  • (json_value value, Integer index) -> void

Parameters:

  • value (json_value)
  • index (Integer)


298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/ibex/ir/validator/grammar.rb', line 298

def validate_production(value, index)
  path = "#{@path}.productions[#{index}]"
  required = PRODUCTION_REQUIRED + PRODUCTION_METADATA_REQUIRED
  optional = %w[node] #: 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")
  rhs = array(production["rhs"], "#{path}.rhs")
  validate_rhs(rhs, "#{path}.rhs")
  validate_action(production["action"], "#{path}.action", rhs_length: rhs.length)
  validate_precedence_override(production["prec_override"], "#{path}.prec_override")
  validate_origin(production["origin"], "#{path}.origin")
  nullable_string(production["doc"], "#{path}.doc")
  validate_expansion(production["expansion"], "#{path}.expansion")
  validate_node_annotation(production["node"], "#{path}.node", rhs_length: rhs.length)
end

#validate_productionsvoid

This method returns an undefined value.

RBS:

  • () -> void



291
292
293
294
295
# File 'lib/ibex/ir/validator/grammar.rb', line 291

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



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/ibex/ir/validator/grammar.rb', line 249

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:

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

Parameters:

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


280
281
282
283
284
285
286
287
288
# File 'lib/ibex/ir/validator/grammar.rb', line 280

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:

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

Parameters:

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


272
273
274
275
276
277
# File 'lib/ibex/ir/validator/grammar.rb', line 272

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)


212
213
214
215
216
217
# File 'lib/ibex/ir/validator/grammar.rb', line 212

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



206
207
208
209
# File 'lib/ibex/ir/validator/grammar.rb', line 206

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


346
347
348
349
350
351
# File 'lib/ibex/ir/validator/grammar.rb', line 346

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_root_recordvoid

This method returns an undefined value.

RBS:

  • () -> void



71
72
73
# File 'lib/ibex/ir/validator/grammar.rb', line 71

def validate_root_record
  record(@data, @path, ROOT_REQUIRED, ROOT_OPTIONAL)
end

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

This method returns an undefined value.

RBS:

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

Parameters:

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


474
475
476
477
478
479
480
481
# File 'lib/ibex/ir/validator/grammar.rb', line 474

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



220
221
222
223
224
225
226
227
228
# File 'lib/ibex/ir/validator/grammar.rb', line 220

def validate_start
  start = nonempty_string(@data["start"], "#{@path}.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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


406
407
408
409
410
411
# File 'lib/ibex/ir/validator/grammar.rb', line 406

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:

  • (json_value value, Integer index) -> void

Parameters:

  • value (json_value)
  • index (Integer)


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/ibex/ir/validator/grammar.rb', line 178

def validate_symbol(value, index)
  path = "#{@path}.symbols[#{index}]"
  required = SYMBOL_REQUIRED + SYMBOL_METADATA_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")
end

#validate_symbolsvoid

This method returns an undefined value.

RBS:

  • () -> void



171
172
173
174
175
# File 'lib/ibex/ir/validator/grammar.rb', line 171

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



460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/ibex/ir/validator/grammar.rb', line 460

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



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

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:

  • (json_value value, String path) -> void

Parameters:

  • value (json_value)
  • path (String)


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

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:

  • (json_object warning, String field_name, String path) -> void

Parameters:

  • warning (json_object)
  • field_name (String)
  • path (String)


437
438
439
440
# File 'lib/ibex/ir/validator/grammar.rb', line 437

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:

  • (json_object warning, String path) -> void

Parameters:

  • warning (json_object)
  • path (String)


431
432
433
434
# File 'lib/ibex/ir/validator/grammar.rb', line 431

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



414
415
416
417
418
# File 'lib/ibex/ir/validator/grammar.rb', line 414

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