From b45f5bfaec47326df90e035ae1dc3dc1a3d89f46 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Mon, 28 Sep 2026 20:34:53 +0800 Subject: [PATCH] fix(json-ir): serialise to the production corpus shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JsonIR merged every library's aliases into every document and wrapped ISC any()-sets in group nodes. The production corpus carries only the document's own aliases and serialises any()-sets as {kind: "any", "of": [...]} — consumers resolve dependency and library aliases at runtime via the loader, matching interscript-ts's converter. Also renders string_case/compose/decompose stage items as Funcall rules in the NodeAdapter (4fdf15b never reached main): bare Symbols raise 'Cannot serialise rule of type Symbol' in JsonIR. --- lib/interscript/compiler/json_ir.rb | 35 ++++----------------------- lib/interscript/isc/node_adapter.rb | 7 +++--- spec/json_ir_serialisation_spec.rb | 37 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 spec/json_ir_serialisation_spec.rb diff --git a/lib/interscript/compiler/json_ir.rb b/lib/interscript/compiler/json_ir.rb index 24a44730..d1da537f 100644 --- a/lib/interscript/compiler/json_ir.rb +++ b/lib/interscript/compiler/json_ir.rb @@ -38,37 +38,9 @@ def serialise_document(doc) # without re-implementing the Ruby dep_aliases indirection. all_aliases = {} - # Walk dependencies and merge their alias definitions. - # posix defines :upper, :lower; unicode defines :combining marks; etc. - doc.dependencies.each do |dep| - next unless dep.document - dep.document.aliases.each do |aname, defn| - all_aliases[aname.to_s] ||= serialise_item(defn.data) - end - end - - # Also walk dep_aliases (for run-rule dependency resolution paths). - doc.dep_aliases.each_value do |dep| - next unless dep.document - dep.document.aliases.each do |aname, defn| - all_aliases[aname.to_s] ||= serialise_item(defn.data) - end - end - - # Also merge ALL library aliases unconditionally. Libraries (posix, - # unicode, var-Cyrl, var-kor) define character classes that maps - # reference via alias() without listing the library as an explicit - # dependency in the dependency list. - Interscript.maps(libraries: true).each do |lib| - libdoc = Interscript.parse(lib) - libdoc.aliases.each do |aname, defn| - all_aliases[aname.to_s] ||= serialise_item(defn.data) - end - rescue - # skip unparseable libraries - end - - # Document's own aliases override everything. + # Only the document's own aliases are serialised. Library and + # dependency aliases resolve at runtime via the alias `map:` qualifier, + # matching the production corpus shape. doc.aliases.each do |name, defn| all_aliases[name.to_s] = serialise_item(defn.data) end @@ -180,6 +152,7 @@ def serialise_to(to) end def serialise_item(item) + return {kind: "any", of: item.map { |i| i.is_a?(String) ? {kind: "string", value: i} : serialise_item(i) }} if item.is_a?(::Array) case item when Interscript::Node::Item::String {kind: "string", value: item.data} diff --git a/lib/interscript/isc/node_adapter.rb b/lib/interscript/isc/node_adapter.rb index 04c2e4a5..43f66f35 100644 --- a/lib/interscript/isc/node_adapter.rb +++ b/lib/interscript/isc/node_adapter.rb @@ -113,10 +113,11 @@ def build_stage(stage_def) Interscript::Node::Item::String.new(item[:separator]&.value || "-") ) when :string_case - sym = (item[:op] == "title_case") ? :title_case : item[:op].to_sym - stage.children << sym + stage.children << Interscript::Node::Rule::Funcall.new(item[:op].to_sym) when :compose - stage.children << :compose + stage.children << Interscript::Node::Rule::Funcall.new(:compose) + when :decompose + stage.children << Interscript::Node::Rule::Funcall.new(:decompose) when :funcall stage.children << Interscript::Node::Rule::Funcall.new( item[:name].to_sym, diff --git a/spec/json_ir_serialisation_spec.rb b/spec/json_ir_serialisation_spec.rb new file mode 100644 index 00000000..53eff19a --- /dev/null +++ b/spec/json_ir_serialisation_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# JsonIR must serialise to the production corpus shape: a document +# carries only its own aliases (dependency and library aliases resolve +# at runtime via the alias `map:` qualifier), and any()-sets serialise +# as {kind: "any", of: [...]} — never as merged library dumps or +# char-class rewrites. +require "interscript" +require "json" + +MAPS = ENV.fetch("INTERSCRIPT_MAPS_PATH", "../maps/maps") + +RSpec.describe "Interscript::Compiler::JsonIR serialisation form" do + before(:all) do + Interscript.load_path.unshift(MAPS) unless Interscript.load_path.first == MAPS + end + + it "serialises only the document's own aliases" do + skip "maps checkout not present" unless File.file?(File.expand_path("un-tam-Taml-Latn-1972.isc", MAPS)) + + doc = Interscript::Compiler.parse_isc(File.expand_path("un-tam-Taml-Latn-1972.isc", MAPS)) + ir = JSON.parse(Interscript::Compiler::JsonIR.new.compile(doc).code) + + expect(ir["aliases"].keys).to eq(["taml_chars_1"]) + end + + it "serialises any()-sets as any/of string alternatives" do + skip "maps checkout not present" unless File.file?(File.expand_path("un-tam-Taml-Latn-1972.isc", MAPS)) + + doc = Interscript::Compiler.parse_isc(File.expand_path("un-tam-Taml-Latn-1972.isc", MAPS)) + ir = JSON.parse(Interscript::Compiler::JsonIR.new.compile(doc).code) + alias_def = ir["aliases"]["taml_chars_1"] + + expect(alias_def["kind"]).to eq("any") + expect(alias_def["of"]).to all(include("kind" => "string")) + end +end