diff --git a/lib/interscript/isc/document_builder.rb b/lib/interscript/isc/document_builder.rb index 232ef42b..f711ece8 100644 --- a/lib/interscript/isc/document_builder.rb +++ b/lib/interscript/isc/document_builder.rb @@ -263,7 +263,10 @@ def extract_stage_items(n) elsif n[:separate] [{kind: :separate, separator: n[:separator] ? materialize(n[:separator]) : nil}] elsif n[:compose] - [{kind: :compose}] + # The grammar captures both `compose` and `decompose` under this + # key — the value decides the directive. Dropping it compiled + # every decompose as NFC compose. + [{kind: n[:compose].to_s.to_sym}] elsif n[:case] item = {kind: :string_case, op: n[:case].to_s} item[:kwargs] = extract_kwargs(n[:case_kwargs]) if n[:case_kwargs] diff --git a/spec/interscript/isc/node_adapter_spec.rb b/spec/interscript/isc/node_adapter_spec.rb index a92944b6..c787ffe9 100644 --- a/spec/interscript/isc/node_adapter_spec.rb +++ b/spec/interscript/isc/node_adapter_spec.rb @@ -373,3 +373,25 @@ def parse_and_adapt(src) expect(node.name).to eq("test-map") end end + +RSpec.describe "NodeAdapter compose vs decompose" do + it "keeps decompose distinct from compose" do + src = <<~ISC + system "t:a-b:C-D:1" { + metadata { name "T" } + stage main { + sub "a" "b" + decompose + compose + } + } + ISC + node = Interscript::Isc::NodeAdapter.to_interscript_node( + Interscript::Isc::DocumentBuilder.build(Interscript::Isc::Parser.parse(src)) + ) + calls = node.stages[:main].children.select { |c| c.is_a?(Interscript::Node::Rule::Funcall) }.map(&:name) + # The grammar captures both directives under :compose; the builder must + # read the value or decompose compiles as NFC compose. + expect(calls).to eq(%i[decompose compose]) + end +end