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.