The ISC Language

ISC (Interscript Specification Code) is the declarative language for script conversion. A .isc file denotes exactly one conversion function; every runtime computes the same object. This is the normative language reference (IS-1 v2 — the executable form is the 22-vector suite in interscript-ruby).

Document structure

system "AUTHORITY:lang-SrcScript:DstScript:year" {
  metadata {
    authority_id odni
    id 2004
    language iso-639-3:prs
    source_script Arab
    destination_script Latn
  }
  stage main {
    parallel {
      sub "ئ" ""
    }
  }
  tests { "input" -> "expected" }
}

Stages

sequence executes rules in order. parallel is single-pass longest-match — the longest applicable rule wins at each position; duplicate rules resolve last-declared-wins.

Rules and contexts

sub { from "a" + any("bc") to "X" before "d" after boundary }
maybe("h")            // zero-or-one, participates in longest-match
capture("a") + "b"    // captured; ref(1) substitutes it in the target
any("0" .. "9")       // inclusive codepoint range
any_character         // any single character

before/after are zero-width lookbehind/lookahead. boundary is a zero-width word edge. not_before/not_after negate. There is no alternation operator — alternatives are separate rules.

Aliases

aliases { vowel = any("aeiou") }
stage main { parallel { sub { from "b" + vowel + "t" to "X" } } }

Aliases bind named item expressions at parse time and are usable in both from and to positions.

Stages calling stages; stdlib directives

run stage.main              // this document
run map.dep.stage.main      // a declared dependency

downcase                    // stage-level stdlib directives run at their
upcase                      // rule position on the whole string
title_case
compose / decompose
separate / unseparate

run takes no other form — stray identifiers are a parse error (a typo'd map fails loudly). The rababa directive invokes the ML adapter with mandatory keyword arguments; an unprovisioned model raises a named error enumerating known ids.

Casing targets

sub { from "ة" to upcase }   // uppercases the matched text only

Reuse and language conditioning (ISC-WF-1)

ruleset romanize_descenders {
  parallel {
    sub "җ" "ž̧"
    sub "қ" "ķ"
  }
}

stage main {
  use romanize_descenders        // splice the ruleset's items here
  use romanize_descenders override {
    sub "қ" "k̦"                  // child-wins on exact from-equality
  }
}

A ruleset never runs on its own: use splices its items at that position (recursively; a reference cycle is a load error, and use alias.<ruleset> reaches into a declared dependency). An override block replaces rows on byte-exact from-equality, adds new rows, and joins the expansion's parallel pass — replacement acts on the source-script pass.

system "ISO:rus-Cyrl:Latn:9-1995" {
  languages kur ckv               // the languages this map serves
  ...
}
stage main {
  parallel {
    sub "қ" "ķ" when kur          // applies only when the caller
    sub "ҫ" "ș" when ckv          // selects that language
  }
}

A map can declare the languages it serves, and rows can be guarded with when. The caller selects a language; guard-applicable rows win over an unguarded row with the same from-string; with no selection every guard is inert. All three constructs are additive: a map that does not use them transliterates identically under runtimes with and without them. The narrative page is Workflow extension; the normative text is maps/docs/isc-wf-1.md.

Embedded tests

tests { "input" -> "expected" }

Normative data on the document — executed by the verification layer (the conformance runner), never by transliterate.

The strict parse

The corpus is strict-parse-clean: stray identifiers and undeclared constructs are parse errors. A typo'd map fails loudly.

Normative reference

The full specification (IS-1 v2) with the 22 frozen rulings and the cross-runtime guarantee lives in interscript-models/docs/IS-1-v2.md.