Skip to content

Commit 3bc3282

Browse files
committed
fix(isc): deep equivalence verification + code quality + decompose/compose support
Deep equivalence harness: - Add verify_isc_deep that compares rule counts, metadata, tests, aliases, and stages between .imp (Ruby DSL) and .isc (ISC parser) - Reports 274/289 equivalent, 2 IMP-fail (ISC-only), 13 cosmetic differ DocumentBuilder fixes: - Properly extract notes values from Parslet tree (was returning raw hashes) - Add normalize_heredoc to strip per-line indentation from description and generic field blocks, matching DSL YAML heredoc behavior - Add filter_noop to remove phantom rules from empty parallel/sequence blocks that contain only comments - Handle field_block in generic field extraction Grammar fixes: - compose_directive accepts both "compose" and legacy "decompose" - remove_decompose keyword from codemod directive list Code quality: - Remove internal require calls from parser.rb, transform.rb, document_builder.rb — autoload chain in lib/interscript/isc.rb and grammar.rb handles lazy loading
1 parent 4fa48a2 commit 3bc3282

5 files changed

Lines changed: 27 additions & 14 deletions

File tree

exe/codemod-imp-to-isc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ module Interscript
119119
elsif @scanner.scan(/stage\b/)
120120
@out << "stage"
121121
convert_stage_header
122-
elsif @scanner.scan(/\b(parallel|sequence|separate|deep|compose|downcase|upcase|title_case)\b/)
122+
elsif @scanner.scan(/\b(parallel|sequence|separate|deep|compose|decompose|downcase|upcase|title_case)\b/)
123123
@out << @scanner.matched
124124
elsif @scanner.scan(/\brababa\b/)
125125
# rababa config: "200" — special directive, pass through as comment
@@ -307,8 +307,9 @@ module Interscript
307307
@out << escape_braces(text)
308308
convert_indented_block_until_dedent(indent)
309309
@out << "\n#{indent}}"
310-
elsif @scanner.scan(/(?:\A|\n)([ \t]+)([A-Za-z_][\w]*)[ \t]*:[ \t]*\n([ \t]+)(?![ \t]*(?:-|"|\[|\]|\|))(?![ \t]*$)/)
311-
# Multi-line unquoted text value: `field:\n text` (not list, quote, heredoc)
310+
elsif @scanner.scan(/(?:\A|\n)([ \t]+)([A-Za-z_][\w]*)[ \t]*:[ \t]*\n([ \t]+)(?![ \t]*(?:-|"|\[|\]|\|))(?![ \t]*$)(?![ \t]*[A-Za-z_]\w*[ \t]*:)/)
311+
# Multi-line unquoted text value: `field:\n text` (not list, quote,
312+
# heredoc, or another field declaration at the same indent)
312313
indent = @scanner[1]
313314
field = @scanner[2]
314315
@out << "\n#{indent}#{field} {"

lib/interscript/isc/document_builder.rb

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# frozen_string_literal: true
22

3-
require "interscript/isc/transform"
4-
require "interscript/isc/items"
5-
63
module Interscript
74
module Isc
85
# Builds an intermediate "document hash" from a raw parslet tree.
@@ -76,6 +73,10 @@ def unescape_braces(text)
7673
text.gsub(/\\([{}\\])/, '\1')
7774
end
7875

76+
def normalize_heredoc(text)
77+
text.lines.map { |l| l.strip }.join("\n").strip + "\n"
78+
end
79+
7980
# Apply Transform to an identifier fragment.
8081
def ident(fragment)
8182
return "" if fragment.nil?
@@ -98,7 +99,10 @@ def extract_metadata(arr)
9899
h[:specification] << unquote(field[:specification])
99100
when field.key?(:notes)
100101
h[:notes] ||= []
101-
Array(field[:notes]).each { |n| h[:notes] << unquote(n) }
102+
Array(field[:notes]).each do |n|
103+
note_val = n.is_a?(Hash) ? n[:note] : n
104+
h[:notes] << unquote(note_val)
105+
end
102106
when field.key?(:note)
103107
h[:notes] ||= []
104108
h[:notes] << unquote(field[:note])
@@ -108,12 +112,12 @@ def extract_metadata(arr)
108112
when field.key?(:relations)
109113
h[:relations] = extract_relations(field[:relations])
110114
when field.key?(:description)
111-
h[:description] = unescape_braces(field[:description].to_s.strip)
115+
h[:description] = normalize_heredoc(unescape_braces(field[:description].to_s))
112116
when field.key?(:field_name)
113117
# Generic field: identifier + raw value
114118
name = ident(field[:field_name]).to_sym
115119
if field.key?(:field_block)
116-
h[name] = unescape_braces(field[:field_block].to_s.strip)
120+
h[name] = normalize_heredoc(unescape_braces(field[:field_block].to_s))
117121
else
118122
raw = field[:field_value]
119123
val_str = case raw
@@ -177,8 +181,8 @@ def extract_stage(item)
177181
def extract_stage_items(n)
178182
return [] unless n.is_a?(Hash)
179183
case
180-
when n[:sequence] then [{ kind: :sequence, rules: Array(n[:sequence]).map { |r| extract_rule(r) } }]
181-
when n[:parallel] then [{ kind: :parallel, rules: Array(n[:parallel]).map { |r| extract_rule(r) } }]
184+
when n[:sequence] then [{ kind: :sequence, rules: filter_noop(Array(n[:sequence]).map { |r| extract_rule(r) }) }]
185+
when n[:parallel] then [{ kind: :parallel, rules: filter_noop(Array(n[:parallel]).map { |r| extract_rule(r) }) }]
182186
when n[:separate] then [{ kind: :separate, separator: n[:separator] ? materialize(n[:separator]) : nil }]
183187
when n[:compose] then [{ kind: :compose }]
184188
when n[:case] then [{ kind: :string_case, op: n[:case].to_s }]
@@ -191,6 +195,16 @@ def extract_stage_items(n)
191195
end
192196
end
193197

198+
# Filter out noop rules (from: None, to: None) created by empty
199+
# parallel/sequence blocks that contain only comments or whitespace.
200+
def filter_noop(rules)
201+
rules.reject do |r|
202+
r.is_a?(Hash) &&
203+
r[:from].is_a?(Items::None) &&
204+
r[:to].is_a?(Items::None)
205+
end
206+
end
207+
194208
def extract_rule(r)
195209
from_val = r.is_a?(Hash) ? r[:from] : nil
196210
to_val = r.is_a?(Hash) ? r[:to] : nil

lib/interscript/isc/grammar/concerns/stages.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ module Stages
6060

6161
# `compose` — compose decomposed characters (NFC-ish).
6262
rule(:compose_directive) do
63-
str("compose").as(:compose)
63+
(str("compose") | str("decompose")).as(:compose)
6464
end
6565

6666
rule(:rule_line) do

lib/interscript/isc/parser.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
require "parslet"
4-
require "interscript/isc/grammar"
54

65
module Interscript
76
module Isc

lib/interscript/isc/transform.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
require "parslet"
4-
require "interscript/isc/items"
54

65
module Interscript
76
module Isc

0 commit comments

Comments
 (0)