From 837c97e45d28a7558844d6da65b9b721cdb1e85f Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 22:24:36 +0800 Subject: [PATCH] fix: any_character converts to the Stdlib any-char alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The :any_char fragment had no transform rule and stringified to "any_charany_character" — odni-uzb's five geminate-collapse rules (capture(any_character) + ref(1) -> ref(1)) compiled to garbage and "ччччч" became "chchchchch" instead of "ch". Converts to AliasRef(:any_character), which the runtime resolves to Stdlib's ".". odni-uzb: 18 examples, 0 failures (was 6). Sweep tail: 38 -> 32. --- lib/interscript/isc/transform.rb | 3 +++ spec/interscript/isc/node_adapter_spec.rb | 26 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/interscript/isc/transform.rb b/lib/interscript/isc/transform.rb index 764ce822..757c99c6 100644 --- a/lib/interscript/isc/transform.rb +++ b/lib/interscript/isc/transform.rb @@ -23,6 +23,9 @@ class Transform < Parsanol::Transform rule(identifier: simple(:i)) { i.to_s } rule(none: simple(:_)) { Items::None.new } + # any_character is the Stdlib any-char alias (".") — without this + # rule the fragment stringified to "any_charany_character". + rule(any_char: simple(:_)) { Items::AliasRef.new("any_character") } rule(primitive: simple(:p)) { Items::Primitive.new(p.to_s) } rule(function: simple(:f)) { Items::Function.new(f.to_s) } rule(alias: {identifier: simple(:n), qualified_name: simple(:q)}) { diff --git a/spec/interscript/isc/node_adapter_spec.rb b/spec/interscript/isc/node_adapter_spec.rb index c787ffe9..bbc16731 100644 --- a/spec/interscript/isc/node_adapter_spec.rb +++ b/spec/interscript/isc/node_adapter_spec.rb @@ -395,3 +395,29 @@ def parse_and_adapt(src) expect(calls).to eq(%i[decompose compose]) end end + +RSpec.describe "NodeAdapter any_character" do + it "converts to the Stdlib any-character alias, not a stringified fragment" do + src = <<~ISC + system "t:a-b:C-D:1" { + metadata { name "T" } + stage main { + sub { + from capture(any_character) + ref(1) + to ref(1) + } + } + } + ISC + node = Interscript::Isc::NodeAdapter.to_interscript_node( + Interscript::Isc::DocumentBuilder.build(Interscript::Isc::Parser.parse(src)) + ) + inner = node.stages[:main].children.first.from.children.first.data + expect(inner).to be_a(Interscript::Node::Item::Alias) + expect(inner.name).to eq(:any_character) + interp = Interscript::Interpreter.new + interp.compile(node) + # The geminate collapse: any char followed by itself collapses to one. + expect(interp.call("чч")).to eq("ч") + end +end