Class: Ibex::Verify::LanguageWitness
- Inherits:
-
Object
- Object
- Ibex::Verify::LanguageWitness
show all
- Defined in:
- lib/ibex/verify/language_witness.rb,
sig/ibex/verify/language_witness.rbs
Overview
Compares canonical and submitted parser acceptance over a bounded set of
terminal sequences. This is a semantic witness, not a proof of language
equivalence: the reference collection is complete within its budgets and
the token enumeration is deliberately finite.
Defined Under Namespace
Classes: CanonicalMachine, Difference, Machine, Result
Instance Method Summary
collapse
-
#initialize(grammar, target, max_tokens: 6, max_cases: 10_000, max_states: 100_000, max_items: 1_000_000) ⇒ LanguageWitness
constructor
A new instance of LanguageWitness.
-
#input_tokens ⇒ Array[IR::GrammarSymbol]
-
#simulate_target(entry, tokens) ⇒ witness_status
-
#token_name(id) ⇒ String
-
#verify ⇒ Result
-
#verify_entry(entry, token_ids, differences, explored) ⇒ [ Integer, bool ]
Constructor Details
#initialize(grammar, target, max_tokens: 6, max_cases: 10_000, max_states: 100_000, max_items: 1_000_000) ⇒ LanguageWitness
Returns a new instance of LanguageWitness.
61
62
63
64
65
66
67
68
69
70
71
72
73
|
# File 'lib/ibex/verify/language_witness.rb', line 61
def initialize(grammar, target, max_tokens: 6, max_cases: 10_000, max_states: 100_000,
max_items: 1_000_000)
raise ArgumentError, "max_tokens must be nonnegative" if max_tokens.negative?
raise ArgumentError, "max_cases must be positive" unless max_cases.positive?
@grammar = grammar
@target = target
@max_tokens = max_tokens
@max_cases = max_cases
@canonical = CanonicalMachine.new(
grammar, max_states: max_states, max_items: max_items
)
end
|
Instance Method Details
96
97
98
|
# File 'lib/ibex/verify/language_witness.rb', line 96
def input_tokens
@grammar.terminals.reject { |terminal| ["$eof", "error"].include?(terminal.name) }
end
|
#simulate_target(entry, tokens) ⇒ witness_status
132
133
134
135
136
137
138
|
# File 'lib/ibex/verify/language_witness.rb', line 132
def simulate_target(entry, tokens)
initial = @target.entry_states[entry]
return :missing_entry unless initial
machine = Machine.new(@target.states, @grammar)
machine.simulate(initial, tokens)
end
|
#token_name(id) ⇒ String
127
128
129
|
# File 'lib/ibex/verify/language_witness.rb', line 127
def token_name(id)
@grammar.symbol_by_id(id)&.name || raise(Ibex::Error, "missing terminal #{id}")
end
|
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'lib/ibex/verify/language_witness.rb', line 76
def verify
differences = [] explored = 0
truncated = false
token_ids = input_tokens.map(&:id)
@grammar.starts.each do |entry|
explored, truncated = verify_entry(entry, token_ids, differences, explored)
break if truncated
end
Result.new(
differences: differences.freeze, explored: explored, truncated: truncated,
max_tokens: @max_tokens, max_cases: @max_cases
)
end
|
#verify_entry(entry, token_ids, differences, explored) ⇒ [ Integer, bool ]
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
# File 'lib/ibex/verify/language_witness.rb', line 101
def verify_entry(entry, token_ids, differences, explored)
queue = [[]] until queue.empty?
tokens = queue.shift
return [explored, true] if explored >= @max_cases
explored += 1
canonical = @canonical.simulate(entry, tokens)
target = simulate_target(entry, tokens)
if canonical != target
differences << Difference.new(
entry: entry, tokens: tokens.map { |id| token_name(id) },
canonical: canonical, target: target
)
return [explored, false]
end
queue.concat(token_ids.map { |token_id| tokens + [token_id] }) if
tokens.length < @max_tokens
end
[explored, false]
end
|