Module: Ibex::CLIIRTools
- Defined in:
- lib/ibex/cli/ir_tools.rb,
sig/ibex/cli/ir_tools.rbs
Overview
CLI validation and structural comparison for current IR documents.
Instance Method Summary collapse
- #compare_grammars(before, after) ⇒ Hash[Symbol, Object?]
- #compare_ir(before, after) ⇒ Hash[Symbol, Object?]
- #compare_lexers(before, after) ⇒ Hash[Symbol, Object?]
- #numeric_change(before, after) ⇒ Hash[Symbol, Integer]
- #production_shapes(grammar) ⇒ Array[String]
- #run_compare_command(arguments) ⇒ Integer
- #run_validate_ir_command(arguments) ⇒ Integer
- #single_ir_path(arguments, command) ⇒ String
- #summary_count(automaton, kind) ⇒ Integer
- #transition_count(automaton) ⇒ Integer
Instance Method Details
#compare_grammars(before, after) ⇒ Hash[Symbol, Object?]
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/ibex/cli/ir_tools.rb', line 84 def compare_grammars(before, after) before_symbols = before.symbols.map(&:name) after_symbols = after.symbols.map(&:name) before_productions = production_shapes(before) after_productions = production_shapes(after) { kind: "grammar", symbols: { added: (after_symbols - before_symbols).sort, removed: (before_symbols - after_symbols).sort }, productions: { added: (after_productions - before_productions).sort, removed: (before_productions - after_productions).sort, count: numeric_change(before.productions.length, after.productions.length) }, warnings: numeric_change(before.warnings.length, after.warnings.length) } end |
#compare_ir(before, after) ⇒ Hash[Symbol, Object?]
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/ibex/cli/ir_tools.rb', line 49 def compare_ir(before, after) return compare_grammars(before, after) if before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar) if before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton) return { kind: "automaton", algorithm: { before: before.algorithm, after: after.algorithm }, states: numeric_change(before.states.length, after.states.length), transitions: numeric_change(transition_count(before), transition_count(after)), conflicts: %i[sr resolved_sr rr].to_h do |kind| [kind, numeric_change(summary_count(before, kind), summary_count(after, kind))] end, grammar: compare_grammars(before.grammar, after.grammar) } end return compare_lexers(before, after) if before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer) raise Ibex::Error, "(cli):1:1: unsupported IR comparison" end |
#compare_lexers(before, after) ⇒ Hash[Symbol, Object?]
69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/ibex/cli/ir_tools.rb', line 69 def compare_lexers(before, after) before_rules = before.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.] } after_rules = after.rules.map { |rule| [rule.state, rule.kind, rule.token, rule.pattern, rule.] } { kind: "lexer", states: { added: after.states - before.states, removed: before.states - after.states }, rules: { added: after_rules - before_rules, removed: before_rules - after_rules, count: numeric_change(before.rules.length, after.rules.length) }, warnings: numeric_change(before.warnings.length, after.warnings.length) } end |
#numeric_change(before, after) ⇒ Hash[Symbol, Integer]
124 125 126 |
# File 'lib/ibex/cli/ir_tools.rb', line 124 def numeric_change(before, after) { before: before, after: after, delta: after - before } end |
#production_shapes(grammar) ⇒ Array[String]
102 103 104 105 106 107 108 |
# File 'lib/ibex/cli/ir_tools.rb', line 102 def production_shapes(grammar) grammar.productions.map do |production| lhs = grammar.symbol_by_id(production.lhs)&.name || production.lhs.to_s rhs = production.rhs.map { |id| grammar.symbol_by_id(id)&.name || id.to_s } "#{lhs} -> #{rhs.join(' ')}" end end |
#run_compare_command(arguments) ⇒ Integer
26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/ibex/cli/ir_tools.rb', line 26 def run_compare_command(arguments) raise Ibex::Error, "(cli):1:1: compare requires exactly two IR files" unless arguments.length == 2 before = IR::Validator.validate(File.read(arguments.fetch(0))) after = IR::Validator.validate(File.read(arguments.fetch(1))) compatible = (before.is_a?(IR::Grammar) && after.is_a?(IR::Grammar)) || (before.is_a?(IR::Automaton) && after.is_a?(IR::Automaton)) || (before.is_a?(IR::Lexer) && after.is_a?(IR::Lexer)) raise Ibex::Error, "(cli):1:1: cannot compare different IR kinds" unless compatible @stdout.puts(JSON.pretty_generate(compare_ir(before, after))) 0 end |
#run_validate_ir_command(arguments) ⇒ Integer
11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/ibex/cli/ir_tools.rb', line 11 def run_validate_ir_command(arguments) path = single_ir_path(arguments, "validate-ir") value = IR::Validator.validate(File.read(path)) kind = if value.is_a?(IR::Grammar) "grammar" elsif value.is_a?(IR::Lexer) "lexer" else "automaton" end @stdout.puts("valid current #{kind} IR") 0 end |
#single_ir_path(arguments, command) ⇒ String
41 42 43 44 45 |
# File 'lib/ibex/cli/ir_tools.rb', line 41 def single_ir_path(arguments, command) return arguments.first if arguments.length == 1 raise Ibex::Error, "(cli):1:1: #{command} requires exactly one IR file" end |
#summary_count(automaton, kind) ⇒ Integer
116 117 118 119 120 121 |
# File 'lib/ibex/cli/ir_tools.rb', line 116 def summary_count(automaton, kind) value = automaton.conflict_summary.fetch(kind) return value if value.is_a?(Integer) raise Ibex::Error, "(ir):1:1: conflict count #{kind} must be an integer" end |
#transition_count(automaton) ⇒ Integer
111 112 113 |
# File 'lib/ibex/cli/ir_tools.rb', line 111 def transition_count(automaton) automaton.states.sum { |state| state.transitions.length } end |