Skip to content

Commit 6810040

Browse files
committed
fix(isc): NodeAdapter correctness + 15 specs + run_stage_only extraction
NodeAdapter fixes: - Boundaries are Aliases referencing Stdlib symbols (not separate classes) - CaptureRef (not Capture) for ref(N) items - CaptureGroup wraps converted inner item - Run rules use Node::Item::Stage for stage references DocumentBuilder fix: - run_stage_only: extract stage name from Parslet tree inner hash (was passing the outer hash, producing raw inspection string) 15 new NodeAdapter specs covering: metadata, tests, stages, parallel blocks, sub rules, aliases, captures, any(), boundaries, constraints, run directives, and end-to-end transliteration.
1 parent 19bb688 commit 6810040

3 files changed

Lines changed: 261 additions & 23 deletions

File tree

lib/interscript/isc/document_builder.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def extract_stage_items(n)
218218
when n[:compose] then [{ kind: :compose }]
219219
when n[:case] then [{ kind: :string_case, op: n[:case].to_s }]
220220
when n[:dep] then [{ kind: :run, dependency: ident(n[:dep]), stage: ident(n[:stage]) }]
221-
when n[:run_stage_only] then [{ kind: :run, dependency: nil, stage: ident(n[:run_stage_only]) }]
221+
when n[:run_stage_only] then [{ kind: :run, dependency: nil, stage: ident(n[:run_stage_only][:stage]) }]
222222
when n[:bare_rule] then [{ kind: :bare_rule, rule: extract_rule(n[:bare_rule]) }]
223223
when n[:comment] then []
224224
when n[:noop] then []

lib/interscript/isc/node_adapter.rb

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,11 @@ def build_rule(rule_def)
106106
end
107107

108108
def build_run_rule(item)
109-
dep = item[:dependency]
110-
stage = item[:stage]
111-
Interscript::Node::Rule::Run.new(
112-
Interscript::Node::Rule::Run::Map.new(
113-
(dep ? "#{dep}:" : "") + stage.to_s,
114-
),
109+
stage_ref = Interscript::Node::Item::Stage.new(
110+
item[:stage].to_sym,
111+
map: item[:dependency]&.to_sym,
115112
)
113+
Interscript::Node::Rule::Run.new(stage_ref)
116114
end
117115

118116
def convert_item(item)
@@ -126,13 +124,13 @@ def convert_item(item)
126124
when Items::AliasRef
127125
Interscript::Node::Item::Alias.new(item.name.to_sym)
128126
when Items::Capture
129-
Interscript::Node::Item::Capture.new(item.index)
127+
Interscript::Node::Item::CaptureRef.new(item.index)
130128
when Items::Function
131129
item.name.to_sym
132130
when Items::Concat
133131
convert_concat(item)
134132
when Items::CaptureGroup
135-
convert_item(item.inner)
133+
Interscript::Node::Item::CaptureGroup.new(convert_item(item.inner))
136134
when Items::Maybe
137135
Interscript::Node::Item::Maybe.new(convert_item(item.inner))
138136
when Items::Some
@@ -149,20 +147,9 @@ def convert_item(item)
149147
end
150148

151149
def convert_primitive(item)
152-
case item.name
153-
when "boundary"
154-
Interscript::Node::Item::BeginBoundary.new
155-
when "line_start"
156-
Interscript::Node::Item::StartBoundary.new
157-
when "line_end"
158-
Interscript::Node::Item::EndBoundary.new
159-
when "word_boundary"
160-
Interscript::Node::Item::WordBoundary.new
161-
when "space"
162-
Interscript::Node::Item::String.new(" ")
163-
when "non_boundary"
164-
Interscript::Node::Item::NonBoundary.new
165-
end
150+
# In the Ruby runtime, zero-width primitives are represented as
151+
# Alias nodes referencing Stdlib symbols. See Stdlib::ALIASES.
152+
Interscript::Node::Item::Alias.new(item.name.to_sym)
166153
end
167154

168155
def convert_concat(concat)
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# frozen_string_literal: true
2+
3+
require "interscript"
4+
require "interscript/isc"
5+
6+
RSpec.describe Interscript::Isc::NodeAdapter do
7+
let(:parser) { Interscript::Isc::Parser.new }
8+
9+
def parse_and_adapt(src)
10+
tree = parser.parse(src, filename: "test.isc")
11+
doc = Interscript::Isc::DocumentBuilder.build(tree, filename: "test.isc")
12+
described_class.to_interscript_node(doc)
13+
end
14+
15+
describe ".to_interscript_node" do
16+
it "produces a Node::Document" do
17+
node = parse_and_adapt(<<~ISC)
18+
system "TEST:eng-Latn:Latn:2026" {
19+
metadata {
20+
authority_id test
21+
name "Test"
22+
}
23+
stage main {
24+
sub "a" "b"
25+
}
26+
}
27+
ISC
28+
expect(node).to be_a(Interscript::Node::Document)
29+
end
30+
31+
it "extracts metadata" do
32+
node = parse_and_adapt(<<~ISC)
33+
system "TEST:eng-Latn:Latn:2026" {
34+
metadata {
35+
authority_id alalc
36+
id 1997
37+
name "Test Map"
38+
}
39+
stage main { }
40+
}
41+
ISC
42+
expect(node.metadata.data[:authority_id]).to eq("alalc")
43+
expect(node.metadata.data[:id]).to eq("1997")
44+
expect(node.metadata.data[:name]).to eq("Test Map")
45+
end
46+
47+
it "extracts tests" do
48+
node = parse_and_adapt(<<~ISC)
49+
system "TEST:eng-Latn:Latn:2026" {
50+
metadata { name "T" }
51+
tests {
52+
"hello" -> "world"
53+
}
54+
stage main { }
55+
}
56+
ISC
57+
expect(node.tests).to be_a(Interscript::Node::Tests)
58+
expect(node.tests.data.size).to eq(1)
59+
expect(node.tests.data[0][0]).to eq("hello")
60+
expect(node.tests.data[0][1]).to eq("world")
61+
end
62+
63+
it "builds a main stage" do
64+
node = parse_and_adapt(<<~ISC)
65+
system "TEST:eng-Latn:Latn:2026" {
66+
metadata { name "T" }
67+
stage main {
68+
sub "a" "b"
69+
}
70+
}
71+
ISC
72+
expect(node.stages).to have_key(:main)
73+
expect(node.stages[:main]).to be_a(Interscript::Node::Stage)
74+
end
75+
76+
it "converts parallel blocks" do
77+
node = parse_and_adapt(<<~ISC)
78+
system "TEST:eng-Latn:Latn:2026" {
79+
metadata { name "T" }
80+
stage main {
81+
parallel {
82+
sub "a" "b"
83+
sub "c" "d"
84+
}
85+
}
86+
}
87+
ISC
88+
stage = node.stages[:main]
89+
parallel = stage.children.find { |c| c.is_a?(Interscript::Node::Group::Parallel) }
90+
expect(parallel).not_to be_nil
91+
expect(parallel.children.size).to eq(2)
92+
end
93+
94+
it "converts sub rules with from/to" do
95+
node = parse_and_adapt(<<~ISC)
96+
system "TEST:eng-Latn:Latn:2026" {
97+
metadata { name "T" }
98+
stage main {
99+
sub "x" "y"
100+
}
101+
}
102+
ISC
103+
rule = node.stages[:main].children.first
104+
expect(rule).to be_a(Interscript::Node::Rule::Sub)
105+
expect(rule.from).to be_a(Interscript::Node::Item::String)
106+
expect(rule.to).to be_a(Interscript::Node::Item::String)
107+
end
108+
109+
it "converts block-form sub rules" do
110+
node = parse_and_adapt(<<~ISC)
111+
system "TEST:eng-Latn:Latn:2026" {
112+
metadata { name "T" }
113+
stage main {
114+
sub {
115+
from "a" + "b"
116+
to "c"
117+
}
118+
}
119+
}
120+
ISC
121+
rule = node.stages[:main].children.first
122+
expect(rule.from).to be_a(Interscript::Node::Item::String)
123+
expect(rule.to).to be_a(Interscript::Node::Item::String)
124+
end
125+
126+
it "converts aliases" do
127+
node = parse_and_adapt(<<~ISC)
128+
system "TEST:eng-Latn:Latn:2026" {
129+
metadata { name "T" }
130+
aliases {
131+
my_alias = "abc"
132+
}
133+
stage main {
134+
sub my_alias "x"
135+
}
136+
}
137+
ISC
138+
expect(node.aliases).to have_key(:my_alias)
139+
rule = node.stages[:main].children.first
140+
expect(rule.from).to be_a(Interscript::Node::Item::Alias)
141+
end
142+
143+
it "converts capture and ref" do
144+
node = parse_and_adapt(<<~ISC)
145+
system "TEST:eng-Latn:Latn:2026" {
146+
metadata { name "T" }
147+
stage main {
148+
sub capture("x") ref(1)
149+
}
150+
}
151+
ISC
152+
rule = node.stages[:main].children.first
153+
expect(rule.from).to be_a(Interscript::Node::Item::CaptureGroup)
154+
expect(rule.to).to be_a(Interscript::Node::Item::CaptureRef)
155+
end
156+
157+
it "converts any() constructor" do
158+
node = parse_and_adapt(<<~ISC)
159+
system "TEST:eng-Latn:Latn:2026" {
160+
metadata { name "T" }
161+
stage main {
162+
sub any("abc") "x"
163+
}
164+
}
165+
ISC
166+
rule = node.stages[:main].children.first
167+
expect(rule.from).to be_a(Interscript::Node::Item::Any)
168+
end
169+
170+
it "converts boundary primitives" do
171+
node = parse_and_adapt(<<~ISC)
172+
system "TEST:eng-Latn:Latn:2026" {
173+
metadata { name "T" }
174+
stage main {
175+
sub boundary "X"
176+
}
177+
}
178+
ISC
179+
rule = node.stages[:main].children.first
180+
expect(rule.from).to be_a(Interscript::Node::Item::Alias)
181+
end
182+
183+
it "converts constraints" do
184+
node = parse_and_adapt(<<~ISC)
185+
system "TEST:eng-Latn:Latn:2026" {
186+
metadata { name "T" }
187+
stage main {
188+
sub "a" "b"
189+
before "c"
190+
after "d"
191+
}
192+
}
193+
ISC
194+
rule = node.stages[:main].children.first
195+
expect(rule.before).to be_a(Interscript::Node::Item::String)
196+
expect(rule.after).to be_a(Interscript::Node::Item::String)
197+
end
198+
199+
it "converts run directive" do
200+
node = parse_and_adapt(<<~ISC)
201+
system "TEST:eng-Latn:Latn:2026" {
202+
metadata { name "T" }
203+
stage main {
204+
run map.dep.stage.main
205+
}
206+
}
207+
ISC
208+
run_rule = node.stages[:main].children.first
209+
expect(run_rule).to be_a(Interscript::Node::Rule::Run)
210+
end
211+
end
212+
213+
describe "transliteration integration" do
214+
it "produces correct transliteration through the Node pipeline" do
215+
node = parse_and_adapt(<<~ISC)
216+
system "TEST:eng-Latn:Latn:2026" {
217+
metadata { name "T" }
218+
stage main {
219+
parallel {
220+
sub "a" "b"
221+
sub "c" "d"
222+
}
223+
}
224+
}
225+
ISC
226+
interp = Interscript::Interpreter.new
227+
interp.compile(node)
228+
result = interp.call("acd")
229+
expect(result).to eq("bdd")
230+
end
231+
232+
it "handles multi-stage pipelines" do
233+
node = parse_and_adapt(<<~ISC)
234+
system "TEST:eng-Latn:Latn:2026" {
235+
metadata { name "T" }
236+
stage first {
237+
sub "a" "b"
238+
}
239+
stage main {
240+
run stage.first
241+
sub "b" "c"
242+
}
243+
}
244+
ISC
245+
interp = Interscript::Interpreter.new
246+
interp.compile(node)
247+
result = interp.call("a")
248+
expect(result).to eq("c")
249+
end
250+
end
251+
end

0 commit comments

Comments
 (0)