Module: Ibex::NormalizeDiagnostics

Included in:
Normalizer
Defined in:
lib/ibex/normalize/diagnostics.rb,
sig/ibex/normalize/diagnostics.rbs

Overview

Static grammar diagnostics used by Normalizer.

Instance Method Summary collapse

Instance Method Details

#productive_terminal?(grammar_symbol) ⇒ Boolean

RBS:

  • (IR::GrammarSymbol grammar_symbol) -> bool

Parameters:

Returns:

  • (Boolean)


164
165
166
167
# File 'lib/ibex/normalize/diagnostics.rb', line 164

def productive_terminal?(grammar_symbol)
  # @type self: Normalizer
  grammar_symbol.terminal? && !grammar_symbol.reserved
end

#productive_terminal_idsHash[Integer, true]

RBS:

  • () -> Hash[Integer, true]

Returns:

  • (Hash[Integer, true])


157
158
159
160
161
# File 'lib/ibex/normalize/diagnostics.rb', line 157

def productive_terminal_ids
  # @type self: Normalizer
  symbols = @symbols #: Array[IR::GrammarSymbol]
  symbols.select { |grammar_symbol| productive_terminal?(grammar_symbol) }.to_h { |symbol| [symbol.id, true] }
end

#reachable_symbol_idsHash[Integer, true]

RBS:

  • () -> Hash[Integer, true]

Returns:

  • (Hash[Integer, true])


66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ibex/normalize/diagnostics.rb', line 66

def reachable_symbol_ids
  # @type self: Normalizer
  start_names = @start_names #: Array[String]
  productions = @productions #: Array[IR::Production]
  starts = start_names.map { |name| required_symbol(name).id }
  reachable = starts.to_h { |id| [id, true] } #: Hash[Integer, true]
  loop do
    before = reachable.length
    productions.select { |production| reachable.key?(production.lhs) }.each do |production|
      production.rhs.each { |id| reachable[id] = true }
    end
    return reachable if reachable.length == before
  end
end

#validate_grammarvoid

This method returns an undefined value.

RBS:

  • () -> void



9
10
11
12
13
14
15
16
17
18
# File 'lib/ibex/normalize/diagnostics.rb', line 9

def validate_grammar
  # @type self: Normalizer
  
  warn_duplicate_productions
  warn_unreachable_nonterminals
  warn_unreachable_terminals
  warn_unused_terminals
  warn_unused_precedence
  warn_empty_language
end

#validate_symbol_metadatavoid

This method returns an undefined value.

RBS:

  • () -> void



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/ibex/normalize/diagnostics.rb', line 21

def 
  # @type self: Normalizer
  display_locations = @display_name_locations #: Hash[String, IR::location]
  semantic_locations = @semantic_type_locations #: Hash[String, IR::location]
  inline_rule_names = @inline_rule_names #: Set[String]
  { display: display_locations, type: semantic_locations }.each do |label, locations|
    locations.each do |name, location|
      fail_hash(location, "#{label} declaration references undefined symbol #{name}") unless
        symbol(name) || parameter_template?(name) || (label == :type && inline_rule_names.include?(name))
    end
  end
end

#warn_duplicate_productionsvoid

This method returns an undefined value.

RBS:

  • () -> void



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ibex/normalize/diagnostics.rb', line 35

def warn_duplicate_productions
  # @type self: Normalizer
  productions = @productions #: Array[IR::Production]
  warnings = @warnings #: Array[IR::grammar_warning]
  seen = {} #: Hash[[Integer, Array[Integer]], Integer]
  productions.each do |production|
    signature = [production.lhs, production.rhs] #: [Integer, Array[Integer]]
    if seen.key?(signature)
      location = production.origin[:loc] #: IR::location?
      warnings << { type: :duplicate_production, production: production.id, original: seen[signature],
                    loc: location }
    else
      seen[signature] = production.id
    end
  end
end

#warn_empty_languagevoid

This method returns an undefined value.

RBS:

  • () -> void



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/ibex/normalize/diagnostics.rb', line 135

def warn_empty_language
  # @type self: Normalizer
  productions = @productions #: Array[IR::Production]
  start_names = @start_names #: Array[String]
  warnings = @warnings #: Array[IR::grammar_warning]
  productive = productive_terminal_ids
  loop do
    before = productive.length
    productions.each do |production|
      productive[production.lhs] = true if production.rhs.all? { |id| productive.key?(id) }
    end
    break if productive.length == before
  end
  start_names.each do |name|
    next if productive.key?(required_symbol(name).id)

    start_symbol = required_symbol(name)
    warnings << { type: :empty_language, symbol: name, loc: start_symbol.location }
  end
end

#warn_unreachable_nonterminalsvoid

This method returns an undefined value.

RBS:

  • () -> void



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ibex/normalize/diagnostics.rb', line 53

def warn_unreachable_nonterminals
  # @type self: Normalizer
  symbols = @symbols #: Array[IR::GrammarSymbol]
  warnings = @warnings #: Array[IR::grammar_warning]
  reachable = reachable_symbol_ids
  symbols.select(&:nonterminal?).each do |grammar_symbol|
    next if reachable.key?(grammar_symbol.id) || grammar_symbol.name.start_with?("$")

    warnings << { type: :unreachable_nonterminal, symbol: grammar_symbol.name, loc: grammar_symbol.location }
  end
end

#warn_unreachable_terminalsvoid

This method returns an undefined value.

RBS:

  • () -> void



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/ibex/normalize/diagnostics.rb', line 97

def warn_unreachable_terminals
  # @type self: Normalizer
  productions = @productions #: Array[IR::Production]
  declared_tokens = @declared_tokens #: Hash[String, IR::location]
  warnings = @warnings #: Array[IR::grammar_warning]
  reachable = reachable_symbol_ids
  used = productions.flat_map(&:rhs).to_h { |id| [id, true] } #: Hash[Integer, true]
  declared_tokens.each_key do |name|
    grammar_symbol = required_symbol(name)
    next unless used.key?(grammar_symbol.id)
    next if reachable.key?(grammar_symbol.id)

    warnings << { type: :unreachable_terminal, symbol: name, loc: declared_tokens[name] }
  end
end

#warn_unused_precedencevoid

This method returns an undefined value.

RBS:

  • () -> void



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ibex/normalize/diagnostics.rb', line 114

def warn_unused_precedence
  # @type self: Normalizer
  productions = @productions #: Array[IR::Production]
  precedence = @precedence #: Hash[String, IR::precedence]
  precedence_locations = @precedence_locations #: Hash[String, IR::location]
  warnings = @warnings #: Array[IR::grammar_warning]
  referenced = {} # @type var referenced: Hash[Integer, true]
  productions.each do |production|
    symbol_ids = referenced
    production.rhs.each { |id| symbol_ids[id] = true }
    symbol_ids[production.precedence_override] = true if production.precedence_override
  end
  precedence.each_key do |name|
    grammar_symbol = required_symbol(name)
    next if referenced.key?(grammar_symbol.id)

    warnings << { type: :unused_precedence, symbol: name, loc: precedence_locations[name] }
  end
end

#warn_unused_terminalsvoid

This method returns an undefined value.

RBS:

  • () -> void



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/ibex/normalize/diagnostics.rb', line 82

def warn_unused_terminals
  # @type self: Normalizer
  productions = @productions #: Array[IR::Production]
  symbols = @symbols #: Array[IR::GrammarSymbol]
  precedence = @precedence #: Hash[String, IR::precedence]
  warnings = @warnings #: Array[IR::grammar_warning]
  used = productions.flat_map(&:rhs).to_h { |id| [id, true] } #: Hash[Integer, true]
  symbols.select(&:terminal?).each do |grammar_symbol|
    next if grammar_symbol.reserved || used.key?(grammar_symbol.id) || precedence.key?(grammar_symbol.name)

    warnings << { type: :unused_terminal, symbol: grammar_symbol.name, loc: grammar_symbol.location }
  end
end