From e6b81ce40ce44e326b15590eeebcb83b9b99d7a4 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Thu, 22 Oct 2026 07:36:10 +0800 Subject: [PATCH] test(WO80/A4): rulings for contexts, maybe, and capture Six more normative rulings pinned as vectors: before/after are zero-width lookbehind/lookahead; maybe is zero-or-one within longest-match; capture/ref round-trips in sequential rules; capture inside parallel raises (the WO80 capture-support gap -- now with two motivating cases: the RTGS compound vowels and the parallel capture crash). --- spec/isc/semantics_spec.rb | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/spec/isc/semantics_spec.rb b/spec/isc/semantics_spec.rb index fc01075d..4d7f2157 100644 --- a/spec/isc/semantics_spec.rb +++ b/spec/isc/semantics_spec.rb @@ -56,3 +56,86 @@ def with_map(name, body) end end end + +RSpec.describe "ISC semantics — contexts, maybe, capture" do + def with_map(name, body) + dir = Dir.mktmpdir + File.write(File.join(dir, name), body) + Interscript.load_path.unshift(dir) + yield + ensure + Interscript.load_path.delete(dir) if dir + end + + def context_map(id, rule) + <<~ISC + system "TST:xx-Latn:Latn:#{id}" { + metadata { + authority_id tst + id #{id} + language iso-639-2:xx + source_script Latn + destination_script Latn + } + stage main { parallel { sub { #{rule} } } } + } + ISC + end + + it "rules: before is a zero-width lookbehind (not consumed)" do + with_map("tst-before-2.isc", context_map(11, 'from "a" to "X" before "b"')) do + expect(Interscript.transliterate("tst-before-2", "ba")).to eq("bX") + expect(Interscript.transliterate("tst-before-2", "ab")).to eq("ab") + end + end + + it "rules: after is a zero-width lookahead (not consumed)" do + with_map("tst-after-2.isc", context_map(12, 'from "b" to "Y" after "a"')) do + expect(Interscript.transliterate("tst-after-2", "ba")).to eq("Ya") + expect(Interscript.transliterate("tst-after-2", "ab")).to eq("ab") + end + end + + it "rules: maybe is zero-or-one and participates in longest-match" do + with_map("tst-maybe-2.isc", <<~ISC) do + system "TST:xx-Latn:Latn:13" { + metadata { + authority_id tst + id 13 + language iso-639-2:xx + source_script Latn + destination_script Latn + } + stage main { parallel { sub { from "k" + maybe("h") to "X" } } } + } + ISC + expect(Interscript.transliterate("tst-maybe-2", "k")).to eq("X") + expect(Interscript.transliterate("tst-maybe-2", "kh")).to eq("X") + expect(Interscript.transliterate("tst-maybe-2", "kk")).to eq("XX") + end + end + + it "rules: capture/ref round-trips in sequential rules" do + with_map("tst-capseq-2.isc", <<~ISC) do + system "TST:xx-Latn:Latn:14" { + metadata { + authority_id tst + id 14 + language iso-639-2:xx + source_script Latn + destination_script Latn + } + stage main { sub { from capture(any("0".."9")) to ref(1) } } + } + ISC + expect(Interscript.transliterate("tst-capseq-2", "77a9")).to eq("77a9") + end + end + + it "rules: capture inside parallel is pending capture support (WO80)" do + with_map("tst-cappar-1.isc", context_map(15, 'from capture(any("0".."9")) to ref(1)')) do + expect { Interscript.transliterate("tst-cappar-1", "5") } + .to raise_error(NoMethodError, /max_length/) + end + end +end