Class: Ibex::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/diff.rb,
sig/ibex/diff.rbs

Overview

Deterministic grammar and automaton change classification.

Instance Method Summary collapse

Constructor Details

#initialize(before, after) ⇒ Diff

Returns a new instance of Diff.

RBS:

  • (IR::Automaton before, IR::Automaton after) -> void

Parameters:



8
9
10
11
# File 'lib/ibex/diff.rb', line 8

def initialize(before, after)
  @before = before
  @after = after
end

Instance Method Details

#change(before, after) ⇒ Hash[Symbol, Integer]

RBS:

  • (Integer before, Integer after) -> Hash[Symbol, Integer]

Parameters:

  • before (Integer)
  • after (Integer)

Returns:

  • (Hash[Symbol, Integer])


136
137
138
# File 'lib/ibex/diff.rb', line 136

def change(before, after)
  { before: before, after: after, delta: after - before }
end

#classify(before, after) ⇒ Hash[Symbol, Object?]

RBS:

  • (Hash[String, Object?] before, Hash[String, Object?] after) -> Hash[Symbol, Object?]

Parameters:

  • before (Hash[String, Object?])
  • after (Hash[String, Object?])

Returns:

  • (Hash[Symbol, Object?])


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ibex/diff.rb', line 28

def classify(before, after)
  common = before.keys & after.keys
  {
    added: (after.keys - before.keys).sort.map { |key| { id: key, value: after.fetch(key) } },
    removed: (before.keys - after.keys).sort.map { |key| { id: key, value: before.fetch(key) } },
    changed: common.sort.filter_map do |key|
      next if before.fetch(key) == after.fetch(key)

      { id: key, before: before.fetch(key), after: after.fetch(key) }
    end
  }
end

#conflict_identity(grammar, conflict) ⇒ String

RBS:

  • (IR::Grammar grammar, IR::conflict conflict) -> String

Parameters:

Returns:

  • (String)


88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ibex/diff.rb', line 88

def conflict_identity(grammar, conflict)
  if conflict.fetch(:type).to_sym == :shift_reduce
    shift_reduce = conflict #: IR::shift_reduce_conflict
    reduction = production_shape(grammar, shift_reduce.fetch(:reduce))
    "shift_reduce:#{shift_reduce.fetch(:symbol)}:#{reduction}"
  else
    reduce_reduce = conflict #: IR::reduce_reduce_conflict
    reductions = reduce_reduce.fetch(:reductions).map { |id| production_shape(grammar, id) }.sort
    "reduce_reduce:#{reduce_reduce.fetch(:symbol)}:#{reductions.join('|')}"
  end
end

#conflict_records(automaton) ⇒ Hash[String, Object?]

RBS:

  • (IR::Automaton automaton) -> Hash[String, Object?]

Parameters:

Returns:

  • (Hash[String, Object?])


76
77
78
79
80
81
82
83
84
85
# File 'lib/ibex/diff.rb', line 76

def conflict_records(automaton)
  grouped = Hash.new { |hash, key| hash[key] = [] } #: Hash[String, Array[Object?]]
  automaton.states.each do |state|
    state.conflicts.each do |conflict|
      identity = conflict_identity(automaton.grammar, conflict)
      grouped[identity] << conflict.fetch(:resolution)
    end
  end
  grouped.transform_values { |resolutions| resolutions.sort_by(&:inspect) }
end

#numeric_metricsHash[Symbol, Hash[Symbol, Integer]]

RBS:

  • () -> Hash[Symbol, Hash[Symbol, Integer]]

Returns:

  • (Hash[Symbol, Hash[Symbol, Integer]])


125
126
127
128
129
130
131
132
133
# File 'lib/ibex/diff.rb', line 125

def numeric_metrics
  {
    states: change(@before.states.length, @after.states.length),
    productions: change(@before.grammar.productions.length, @after.grammar.productions.length),
    warnings: change(@before.grammar.warnings.length, @after.grammar.warnings.length),
    shift_reduce: change(@before.conflict_summary.fetch(:sr), @after.conflict_summary.fetch(:sr)),
    reduce_reduce: change(@before.conflict_summary.fetch(:rr), @after.conflict_summary.fetch(:rr))
  }
end

#production_record(grammar, production) ⇒ Hash[Symbol, Object?]

RBS:

  • (IR::Grammar grammar, IR::Production production) -> Hash[Symbol, Object?]

Parameters:

Returns:

  • (Hash[Symbol, Object?])


64
65
66
67
68
69
70
71
72
73
# File 'lib/ibex/diff.rb', line 64

def production_record(grammar, production)
  precedence = production.precedence_override
  {
    rhs: production.rhs.map { |id| symbol_name(grammar, id) },
    precedence: precedence && symbol_name(grammar, precedence),
    node: production.node && {
      name: production.node.fetch(:name), fields: production.node.fetch(:fields)
    }
  }
end

#production_shape(grammar, id) ⇒ String

RBS:

  • (IR::Grammar grammar, Integer id) -> String

Parameters:

Returns:

  • (String)


101
102
103
104
105
106
# File 'lib/ibex/diff.rb', line 101

def production_shape(grammar, id)
  production = grammar.productions.fetch(id)
  lhs = symbol_name(grammar, production.lhs)
  rhs = production.rhs.map { |symbol| symbol_name(grammar, symbol) }
  "#{lhs}->#{rhs.join(' ')}"
end

#rule_records(grammar) ⇒ Hash[String, Object?]

RBS:

  • (IR::Grammar grammar) -> Hash[String, Object?]

Parameters:

Returns:

  • (Hash[String, Object?])


55
56
57
58
59
60
61
# File 'lib/ibex/diff.rb', line 55

def rule_records(grammar)
  grammar.productions.group_by(&:lhs).to_h do |lhs, productions|
    name = grammar.symbol_by_id(lhs)&.name || lhs.to_s
    alternatives = productions.map { |production| production_record(grammar, production) }
    [name, alternatives]
  end
end

#symbol_name(grammar, id) ⇒ String

RBS:

  • (IR::Grammar grammar, Integer id) -> String

Parameters:

Returns:

  • (String)


109
110
111
# File 'lib/ibex/diff.rb', line 109

def symbol_name(grammar, id)
  grammar.symbol_by_id(id)&.name || id.to_s
end

#symbol_records(grammar) ⇒ Hash[String, Object?]

RBS:

  • (IR::Grammar grammar) -> Hash[String, Object?]

Parameters:

Returns:

  • (Hash[String, Object?])


42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ibex/diff.rb', line 42

def symbol_records(grammar)
  grammar.symbols.to_h do |symbol|
    [
      symbol.name,
      {
        kind: symbol.kind, reserved: symbol.reserved, display_name: symbol.display_name,
        semantic_type: symbol.semantic_type, precedence: symbol.precedence
      }
    ]
  end
end

#to_hHash[Symbol, Object?]

RBS:

  • () -> Hash[Symbol, Object?]

Returns:

  • (Hash[Symbol, Object?])


14
15
16
17
18
19
20
21
22
23
# File 'lib/ibex/diff.rb', line 14

def to_h
  {
    ibex_report: "diff", schema_version: 1,
    symbols: classify(symbol_records(@before.grammar), symbol_records(@after.grammar)),
    rules: classify(rule_records(@before.grammar), rule_records(@after.grammar)),
    conflicts: classify(conflict_records(@before), conflict_records(@after)),
    warnings: classify(warning_records(@before.grammar), warning_records(@after.grammar)),
    metrics: numeric_metrics
  }
end

#warning_records(grammar) ⇒ Hash[String, Object?]

RBS:

  • (IR::Grammar grammar) -> Hash[String, Object?]

Parameters:

Returns:

  • (Hash[String, Object?])


114
115
116
117
118
119
120
121
122
# File 'lib/ibex/diff.rb', line 114

def warning_records(grammar)
  grouped = Hash.new { |hash, key| hash[key] = [] } #: Hash[String, Array[Object?]]
  grammar.warnings.each do |warning|
    value = warning.except(:loc)
    identity = [warning.fetch(:type), value.except(:message).inspect].join(":")
    grouped[identity] << value
  end
  grouped.transform_values { |values| values.sort_by(&:inspect) }
end