diff --git a/lib/stupidedi/builder/navigation.rb b/lib/stupidedi/builder/navigation.rb index 7331b5b18..aad925430 100644 --- a/lib/stupidedi/builder/navigation.rb +++ b/lib/stupidedi/builder/navigation.rb @@ -453,8 +453,7 @@ def count!(id, *elements) # @return [Either>] def iterate(id, *elements) a = [] - # m = __find(false, id, elements, true) - m = __find(false, id, elements, false) + m = __find(false, id, elements, true) return m unless m.defined? while m.defined? @@ -487,14 +486,9 @@ def sequence(pattern, *patterns) # @return [Either] def __find(invalid, id, elements, assert_repeatable = false) - reachable = false - matches = [] - - # Note op.segment_use.nil? is true when searching for ISA, - # GS, and ST, because we can't know the SegmentUse until we - # deconstruct the token and looked up the versions numbers - # in the Config. Nonetheless, we know ISA, GS, and ST can repeat - repeatable = [:ISA, :GS, :ST].include?(id) + reachable = false + repeatable = false + matches = [] @active.each do |zipper| matched = false @@ -522,8 +516,6 @@ def __find(invalid, id, elements, assert_repeatable = false) state = zipper value = zipper.node.zipper - repeatable ||= op_.segment_use.try(:repeatable?) - # 1. Move upward (possibly zero times) op_.pop_count.times do value = value.up @@ -534,6 +526,7 @@ def __find(invalid, id, elements, assert_repeatable = false) # nodes to move left, but not exactly how many. Instead, we # know what the InstructionTable is when we get there. target = zipper.node.instructions.pop(op_.pop_count).drop(op_.drop_count) + repeatable ||= target.matches(filter_tok, true).present? # 3. If the segment we're searching for belongs in a new subtree, # but it's not the only segment that might have "opened" that diff --git a/spec/examples/integration/navigating_spec.rb b/spec/examples/integration/navigating_spec.rb index adce80319..187f58768 100644 --- a/spec/examples/integration/navigating_spec.rb +++ b/spec/examples/integration/navigating_spec.rb @@ -5,6 +5,7 @@ let(:payment) { Fixtures.file("X221-HP835/1-good.txt").first } let(:claim) { Fixtures.file("X222-HC837/3b-good.txt").first } + let(:drugs) { Fixtures.file("X222-HC837/10a-good.txt").first } context "unqualified segments" do end @@ -49,11 +50,11 @@ end end - context "repeatable segments" do + context "repeatable loops" do specify "can be iterated" do expect(lambda do - payment.flatmap do |m| - m.iterate(:ISA) do |isa| + payment.flatmap do |isa| + isa.iterate(:ISA) do |_| raise "didn't expect a second ISA segment" end end @@ -62,30 +63,109 @@ specify "can be iterated" do expect(lambda do - payment.flatmap do |m| - m.iterate(:GS) do |gs| - expect(gs.segment.tap do |segment| - expect(segment.node.id).to eq(:GS) + payment.flatmap do |isa| + isa.iterate(:GS) do |gs| + expect(gs.segmentn.tap do |segment| + expect(segment.id).to eq(:GS) end).to be_defined gs.iterate(:ST) do |st| - expect(st.segment.tap do |segment| - expect(segment.node.id).to eq(:ST) + expect(st.segmentn.tap do |segment| + expect(segment.id).to eq(:ST) end).to be_defined end end end end).not_to raise_error end + + specify "can be iterated" do + expect(lambda do + payment.flatmap do |isa| + isa.iterate(:GS) do |gs| + gs.iterate(:ST) do |st| + st.iterate(:LX) do |lx| + expect(lx.segmentn.fetch.id).to be == :LX + end + end + end + end + end).not_to raise_error + end + end + + # N1*PR is not repeatable, but the parser is less strict because there are + # sibling N1 segments (N1*PE); extra segments would be caught in BuilderDsl + context "non-repeatable loops" do + specify "cannot be iterated" do + expect(lambda do + payment.flatmap do |isa| + isa.find(:GS).flatmap do |gs| + gs.find(:ST).flatmap do |st| + st.iterate(:N1, "PR") do |n1| + expect(n1.segmentn.fetch.id).to be == :N1 + end + end + end + end + end).not_to raise_error + end + end + + # REF*EV is not repeatable, but the parser is less strict because there are + # sibling REF segments (REF*PR); extra segments would be caught in BuilderDsl + context "non-repeatable qualified segments" do + specify "can be iterated" do + expect(lambda do + payment.flatmap do |isa| + isa.find(:GS).flatmap do |gs| + gs.find(:ST).flatmap do |st| + st.iterate(:REF) do |ref| + expect(ref.segmentn.fetch.id).to be == :REF + expect(ref.elementn(1).fetch.value).to be == "EV" + end + + st.iterate(:REF, "EV") do |ref| + expect(ref.segmentn.fetch.id).to be == :REF + expect(ref.elementn(1).fetch.value).to be == "EV" + end + end + end + end + end).not_to raise_error + end + end + + context "non-repeatable loops" do + specify "cannot be iterated" do + expect(lambda do + drugs.flatmap do |isa| + isa.find(:GS).flatmap do |gs| + gs.find(:ST).flatmap do |st| + st.find(:HL, nil, nil, "22").flatmap do |hl| + hl.find(:CLM).flatmap do |clm| + clm.find(:LX, 2).flatmap do |lx| + lx.iterate(:LIN) do |lin| + expect(lin.segmentn.fetch.id).to be == :LIN + end + end + end + end + end + end + end + end).to raise_error("LIN segment is not repeatable") + end end context "non-repeatable segments" do specify "cannot be iterated" do expect(lambda do - payment.flatmap do |m| - m.iterate(:GS) do |gs| - gs.iterate(:ST) do |st| - st.iterate(:BHT) + payment.flatmap do |isa| + isa.find(:GS).flatmap do |gs| + gs.find(:ST).flatmap do |st| + st.iterate(:BHT) do |bht| + end end end end