Move around files so the repo will be organized as a monorepo.

This commit is contained in:
be5invis 2023-12-03 02:49:33 -08:00
parent 65d1880a84
commit 08c69f0fd3
365 changed files with 1477 additions and 1262 deletions

View file

@ -0,0 +1,94 @@
extern Map
extern Set
define MarkClasses {
'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos' 'aboveBraceL' 'aboveBraceR'
'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf' 'belowBraceL' 'belowBraceR'
'overlay' 'slash' 'strike'
'cvDecompose' 'enclosureInner' 'enclosureInnerFirstHalf' 'enclosureInnerSecondHalf'
'fracBuildUp'
}
define MarkInteractions : new Map : list
list 'aboveBraceL' {'aboveBraceL' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'}
list 'aboveBraceR' {'aboveBraceR' 'above' 'tieAbove' 'leaningAbove' 'topLeft' 'topRight' 'grekUpperTonos'}
list 'belowBraceL' {'belowBraceL' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'}
list 'belowBraceR' {'belowBraceR' 'below' 'tieBelow' 'leaningBelow' 'bottomLeft' 'bottomRight' 'trailing' 'lf'}
define MkmkStackingLimit : new Set { 'above' 'below' }
export : define [buildMarkMkmk gpos glyphStore markGlyphs] : begin
define validMarkClasses : new Set MarkClasses
define mark : object
feature : gpos.addCommonFeature : gpos.createFeature 'mark'
lookupMap : new Map
createLookup : function [] {.type 'gpos_mark_to_base' .marks {.} .bases {.}}
define mkmk : object
feature : gpos.addCommonFeature : gpos.createFeature 'mkmk'
lookupMap : new Map
createLookup : function [] {.type 'gpos_mark_to_mark' .marks {.} .bases {.}}
# Populate the marks
foreach cls [items-of MarkClasses] : begin
local markLookup : ensureLookup gpos mark cls
local mkmkLookup : ensureLookup gpos mkmk cls
foreach { gn glyph } [glyphStore.namedEntries] : begin
if glyph.markAnchors.(cls) : begin
markGlyphs.all.add gn
markGlyphs.markAttachClassDef.set gn cls
addMarkAnchor markLookup gn cls glyph.markAnchors.(cls)
addMarkAnchor mkmkLookup gn cls glyph.markAnchors.(cls)
# Populate the bases
foreach cls [items-of MarkClasses] : begin
local markLookup : ensureLookup gpos mark cls
local mkmkLookup : ensureLookup gpos mkmk cls
foreach { gn glyph } [glyphStore.namedEntries] : begin
if glyph.baseAnchors.(cls) : begin
if [markGlyphs.all.has gn]
: then : addBaseAnchor mkmkLookup gn cls glyph.baseAnchors.(cls)
: else : addBaseAnchor markLookup gn cls glyph.baseAnchors.(cls)
foreach markLookup [mark.lookupMap.values] : foreach mkmkLookup [mkmk.lookupMap.values]
gpos.setDependency markLookup mkmkLookup
foreach { cls lookup } mkmk.lookupMap : begin
local interactionMarkSet : new Set ([MarkInteractions.get cls] || { cls })
local includeSet : new Set
local ignoreSet : new Set
foreach { gn c } markGlyphs.markAttachClassDef : if [interactionMarkSet.has c]
then : includeSet.add gn
else : ignoreSet.add gn
set lookup.ignoreGlyphs : Array.from ignoreSet
if (interactionMarkSet.size > 1) : begin
markGlyphs.markGlyphSets.push : Array.from includeSet
define [ensureLookup gpos feat cls] : begin
local existing : feat.lookupMap.get cls
if existing : return existing
local lookup : gpos.createLookup : feat.createLookup
feat.feature.addLookup lookup
feat.lookupMap.set cls lookup
return lookup
define [addMarkAnchor lookup gn cls anchor] : begin
local a : object
class cls
x anchor.x
y anchor.y
set lookup.marks.(gn) a
define [addBaseAnchor lookup gn cls anchor] : begin
local a : object
x anchor.x
y anchor.y
if [not lookup.bases.(gn)] : set lookup.bases.(gn) {.}
set lookup.bases.(gn).(cls) a

View file

@ -0,0 +1,333 @@
$$include './meta/macros.ptl'
import [UkMapToLookup UkMap2ToLookup] from "./table-util.mjs"
import [Dotless TieMark TieGlyph OgonekTrY IsSuperscript IsSubscript LeaningMark LeaningMarkSpacer] from "@iosevka/glyph/relation"
import as UnicodeKnowledge from "@iosevka/font-glyphs/unicode-knowledge"
extern Set
define-macro Ccmp-Group : syntax-rules
`[Ccmp-Group @description @body]
dirty `[$ExecCcmpGroup$ [function [export-lookup chain-rule] @[formOf body]]]
export : define [buildCCMP gsub glyphStore markGlyphs] : begin
local anyMark : Array.from markGlyphs.all
local aboveMark : filterMarkByClass markGlyphs 'above'
define ccmp : gsub.addCommonFeature : gsub.createFeature 'ccmp'
define [$ExecCcmpGroup$ fn] : begin
local addedLookups {}
define [export-lookup lookup] : begin
ccmp.addLookup lookup
addedLookups.push lookup
local rec : gsub.beginBlock
define {chain-rule} : gsub.ChainRuleBuilder
fn export-lookup chain-rule
for [local j 1] (j < addedLookups.length) [inc j] : begin
gsub.setDependency addedLookups.(j - 1) addedLookups.(j)
gsub.endBlock rec
Ccmp-Group "Mark transforms" : begin
define groupGrekUpperTonos {}
define groupLF {}
define dotlessFrom {}
define dotlessTo {}
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== "."): begin
if g.baseAnchors.lf : groupLF.push gn
if g.baseAnchors.grekUpperTonos : groupGrekUpperTonos.push gn
if [Dotless.get g] : begin
dotlessFrom.push gn
dotlessTo.push [Dotless.get g]
define [IotaLF] : UkMapToLookup UnicodeKnowledge.iotaBelowToLfTf
define [GrekUpperTonosTf] : UkMapToLookup UnicodeKnowledge.upperGrekMarkToTonosTf
# Dotless transform
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
.rules : list
chain-rule (dotlessFrom ~> dotlessTo) (aboveMark ~> null)
chain-rule groupGrekUpperTonos [GrekUpperTonosTf]
# Iota transform
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
.rules : list
chain-rule groupLF [IotaLF]
export-lookup : gsub.createLookup : object
.type 'gsub_ligature'
.substitutions : UkMap2ToLookup UnicodeKnowledge.markCompositionTf
export-lookup : gsub.createLookup : object
.type 'gsub_multiple'
.substitutions : object
'parenAbove' { 'leftParenAbove' 'rightParenAbove' }
'doubleParenAbove' { 'leftDoubleParenAbove' 'rightDoubleParenAbove' }
'brackAbove' { 'leftBrackAbove' 'rightBrackAbove' }
'parenBelow' { 'leftParenBelow' 'rightParenBelow' }
Ccmp-Group "Tie Mark Transform" : begin
define TieMarkFrom {}
define TieMarkTo {}
define TieGlyphs {}
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
if [TieGlyph.get g] : TieGlyphs.push gid
if [TieMark.get g] : begin
TieMarkFrom.push gid
TieMarkTo.push [TieMark.get g]
define lookupTieMarkLigature : gsub.createLookup : object
.type 'gsub_ligature'
.substitutions : {}.concat
TieMarkFrom.map : lambda [gnFrom idx]
object [from {'cgj' gnFrom}] [to TieMarkTo.(idx)]
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list : object
match {[TieGlyphs.concat TieMarkTo] {'cgj'} TieMarkFrom}
inputBegins 1
inputEnds 3
apply {{.at 1 .lookup lookupTieMarkLigature}}
Ccmp-Group "Special dot-above transformation" : begin
# b-dot, d-dot, h-dot, k-dot
export-lookup : gsub.createLookup : object
.type 'gsub_ligature'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'above']
.substitutions : list
object [from : list "b" "dotAbove"] [to "bDot"]
object [from : list "d" "dotAbove"] [to "dDot"]
object [from : list "h" "dotAbove"] [to "hDot"]
object [from : list "k" "dotAbove"] [to "kDot"]
Ccmp-Group "Leaning Mark Trasnform" : begin
define LeaningAnchorMap : list
list 'above' 'leaningAbove'
list 'below' 'leaningBelow'
foreach { mkCenter mkLeaning } [items-of LeaningAnchorMap] : begin
local basesToConsider {}
local markFrom {}
local markTo {}
local markSpacer {}
local splitMapping {}
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin
if (![markGlyphs.all.has gn] && g.baseAnchors.(mkLeaning)) : basesToConsider.push gn
if (g.markAnchors.(mkCenter) && [LeaningMark.get g] && [LeaningMarkSpacer.get g]) : begin
markFrom.push gn
markTo.push [LeaningMark.get g]
markSpacer.push [LeaningMarkSpacer.get g]
splitMapping.push { gn {[LeaningMarkSpacer.get g] [LeaningMark.get g]} }
define lookupTurnMarkIntoLeaningAndSpacer : gsub.createLookup : object
.type 'gsub_multiple'
.substitutions : Object.fromEntries splitMapping
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs mkCenter]
.rules : list
object
.match { [basesToConsider.concat markSpacer] markFrom }
.inputBegins 1
.inputEnds 2
.apply {{.at 1 .lookup lookupTurnMarkIntoLeaningAndSpacer}}
Ccmp-Group "Rhotic Hook Transform" : begin
define superscripts {}
define subscripts {}
foreach { gn g } [glyphStore.namedEntries] : if (gn.(0) !== ".") : begin
if [IsSuperscript.get g] : superscripts.push gn
if [IsSubscript.get g] : subscripts.push gn
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.ignoreGlyphs anyMark
.rules : list
chain-rule superscripts ({"rhoticHook"} ~> {"rhoticHook/sup"})
chain-rule subscripts ({"rhoticHook"} ~> {"rhoticHook/sub"})
Ccmp-Group "Tone Transform" : begin
define [ToneToToneStart toneEnd] : begin
local f {}
local e {}
foreach toneStart [range 4 downtill 0] : begin
f.push ('tone' + toneStart)
e.push ('toneStart' + toneStart + toneEnd)
return (f ~> e)
define [ToneToToneEnd toneStart] : begin
local f {}
local e {}
foreach toneEnd [range 4 downtill 0] : begin
f.push ('tone' + toneEnd)
e.push ('toneEnd' + toneStart + toneEnd)
return (f ~> e)
define [ToneStartOrMidAt y]: begin
local f {}
foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneStart' + y + toneEnd)
foreach toneStart [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneMid' + toneStart + y + toneEnd)
return f
define [ToneStartToToneMid toneStart] : begin
local f {}
local e {}
foreach toneMid [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneStart' + toneMid + toneEnd)
e.push ('toneMid' + toneStart + toneMid + toneEnd)
return (f ~> e)
define [ToneSandhiToToneStart toneEnd] : begin
local f {}
local e {}
foreach toneStart [range 4 downtill 0] : begin
f.push ('toneSandhi' + toneStart)
e.push ('toneSandhiStart' + toneStart + toneEnd)
return (f ~> e)
define [ToneSandhiToToneEnd toneStart] : begin
local f {}
local e {}
foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneSandhi' + toneEnd)
e.push ('toneSandhiEnd' + toneStart + toneEnd)
return (f ~> e)
define [ToneSandhiStartOrMidAt y]: begin
local f {}
foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneSandhiStart' + y + toneEnd)
foreach toneStart [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneSandhiMid' + toneStart + y + toneEnd)
return f
define [ToneSandhiStartToToneMid toneStart] : begin
local f {}
local e {}
foreach toneMid [range 4 downtill 0] : foreach toneEnd [range 4 downtill 0] : begin
f.push ('toneSandhiStart' + toneMid + toneEnd)
e.push ('toneSandhiMid' + toneStart + toneMid + toneEnd)
return (f ~> e)
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneToToneStart 0] {'tone0'}
chain-rule [ToneToToneStart 1] {'tone1'}
chain-rule [ToneToToneStart 2] {'tone2'}
chain-rule [ToneToToneStart 3] {'tone3'}
chain-rule [ToneToToneStart 4] {'tone4'}
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneStartOrMidAt 0] [ToneStartToToneMid 0]
chain-rule [ToneStartOrMidAt 1] [ToneStartToToneMid 1]
chain-rule [ToneStartOrMidAt 2] [ToneStartToToneMid 2]
chain-rule [ToneStartOrMidAt 3] [ToneStartToToneMid 3]
chain-rule [ToneStartOrMidAt 4] [ToneStartToToneMid 4]
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneStartOrMidAt 0] [ToneToToneEnd 0]
chain-rule [ToneStartOrMidAt 1] [ToneToToneEnd 1]
chain-rule [ToneStartOrMidAt 2] [ToneToToneEnd 2]
chain-rule [ToneStartOrMidAt 3] [ToneToToneEnd 3]
chain-rule [ToneStartOrMidAt 4] [ToneToToneEnd 4]
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneSandhiToToneStart 0] {'toneSandhi0'}
chain-rule [ToneSandhiToToneStart 1] {'toneSandhi1'}
chain-rule [ToneSandhiToToneStart 2] {'toneSandhi2'}
chain-rule [ToneSandhiToToneStart 3] {'toneSandhi3'}
chain-rule [ToneSandhiToToneStart 4] {'toneSandhi4'}
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiStartToToneMid 0]
chain-rule [ToneSandhiStartOrMidAt 1] [ToneSandhiStartToToneMid 1]
chain-rule [ToneSandhiStartOrMidAt 2] [ToneSandhiStartToToneMid 2]
chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiStartToToneMid 3]
chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiStartToToneMid 4]
export-lookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [ToneSandhiStartOrMidAt 0] [ToneSandhiToToneEnd 0]
chain-rule [ToneSandhiStartOrMidAt 1] [ToneSandhiToToneEnd 1]
chain-rule [ToneSandhiStartOrMidAt 2] [ToneSandhiToToneEnd 2]
chain-rule [ToneSandhiStartOrMidAt 3] [ToneSandhiToToneEnd 3]
chain-rule [ToneSandhiStartOrMidAt 4] [ToneSandhiToToneEnd 4]
return ccmp
export : define [buildCCMPPostCvSs gsub ccmp glyphStore markGlyphs] : begin
local anyMark : Array.from markGlyphs.all
local rec : gsub.beginBlock
define triggerGlyphs_Normal { }
define triggerGlyphs_Y { }
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== "."): begin
if g.baseAnchors.trailing : piecewise
[OgonekTrY.get g] : triggerGlyphs_Y.push gid
true : triggerGlyphs_Normal.push gid
define ogonekSpacerNormal : gsub.createLookup : object
.type 'gsub_multiple'
.substitutions UnicodeKnowledge.ogonekBelowToTRTf
define ogonekSpacerY : gsub.createLookup : object
.type 'gsub_single'
.substitutions UnicodeKnowledge.ogonekBelowToTRTf_Y
ccmp.addLookup : gsub.createLookup : object
.type 'gsub_chaining'
.ignoreGlyphs [filterMarkByClassNegated markGlyphs 'below']
.rules : list
object
.match { triggerGlyphs_Y [Object.keys UnicodeKnowledge.ogonekBelowToTRTf_Y] }
.inputBegins 1
.inputEnds 2
.apply {{.at 1 .lookup ogonekSpacerY}}
object
.match { triggerGlyphs_Normal [Object.keys UnicodeKnowledge.ogonekBelowToTRTf] }
.inputBegins 1
.inputEnds 2
.apply {{.at 1 .lookup ogonekSpacerNormal}}
gsub.endBlock rec
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length
define [filterMarkByClass markGlyphs c] : begin
local marks {}
foreach { gn m } markGlyphs.markAttachClassDef : if (m == c) : marks.push gn
return marks
define [filterMarkByClassNegated markGlyphs c] : begin
local marks {}
foreach { gn m } markGlyphs.markAttachClassDef : if (m != c) : marks.push gn
return marks

View file

@ -0,0 +1,142 @@
$$include './meta/macros.ptl'
import [AnyCv CvDecompose] from "@iosevka/glyph/relation"
extern Map
extern Set
define look-around null
class CvLookupManager
public [new table tag feature] : begin
set this.table table
set this.tag tag
set this.feature feature
set this.decompositionLookup null
set this.altrenatesLookup null
set this.singleSubstLookups {}
public [addDecompositionLookup lookup] : begin
this.decompositionLookups.push lookup
this.feature.addLookup lookup
public [createDecompositionSubst] : begin
if this.decompositionLookup : return this.decompositionLookup
define lookupName : 'lookup_cv_decompose_' + this.tag
define lookup : this.table.pickLookup lookupName {.type 'gsub_multiple' .substitutions {.}}
this.feature.addLookup lookup
set this.decompositionLookup lookup
return lookup
public [createAlternateSubst] : begin
if this.altrenatesLookup : return this.altrenatesLookup
define lookupName : 'lookup_cv_alternates_' + this.tag
define lookup : this.table.pickLookup lookupName {.type 'gsub_alternate' .substitutions {.}}
this.feature.addLookup lookup
set this.altrenatesLookup lookup
return lookup
public [createSingleSubstFor rank] : begin
if this.singleSubstLookups.(rank) : return this.singleSubstLookups.(rank)
define lookupName : 'lookup_cv_single_' + this.tag + '_' + rank
define lookup : this.table.pickLookup lookupName {.type 'gsub_single' .substitutions {.}}
set this.singleSubstLookups.(rank) lookup
return lookup
public [linkDeps] : begin
if this.decompositionLookup : begin
this.table.setDependency this.decompositionLookup this.altrenatesLookup
foreach lookup [items-of this.singleSubstLookups] : if lookup : begin
this.table.setDependency this.altrenatesLookup lookup
public [linkCrossDeps other] : begin
this.table.setDependency this.altrenatesLookup other.altrenatesLookup
foreach lookup [items-of this.singleSubstLookups] : if lookup : begin
this.table.setDependency lookup other.altrenatesLookup
static [compare a b] : begin
if (a.tag < b.tag) : return (-1)
if (a.tag > b.tag) : return 1
return 0
export : define [buildCVSS gsub para glyphStore] : begin
local rec : gsub.beginBlock
local cvs : new Map
do "Initialize CV feature atlas"
foreach {name prime} para.variants.primes : if prime.tag : begin
define feature : gsub.addCommonFeature : gsub.createFeature prime.tag
local cvLookupManager : new CvLookupManager gsub prime.tag feature
cvs.set prime.tag cvLookupManager
do "Build decomposition lookups"
foreach { gn glyph } [glyphStore.namedEntries] : begin
local parts : CvDecompose.get glyph
if parts : foreach part [items-of parts] : begin
local gPart : glyphStore.queryByNameEnsured part
foreach [gr : items-of : AnyCv.query gPart] : if gr.tag : begin
local lookup : [cvs.get gr.tag].createDecompositionSubst
if [not lookup.substitutions.(gn)] : set lookup.substitutions.(gn) parts
do "cvxx"
local cvGrs {}
foreach {name prime} para.variants.primes : foreach {vn variant} prime.variants : begin
if (variant.tag && variant.rank) : cvGrs.push variant
cvGrs.sort AnyCv.compare
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
foreach gr [items-of cvGrs] : begin
local subst : gr.get glyph
if (subst && subst != gn) : begin
local cvAlt : [cvs.get gr.tag].createAlternateSubst
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { }
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst
do "ssxx" : foreach {name composition} para.variants.composites : if composition.tag : begin
define feature : gsub.addCommonFeature : gsub.createFeature composition.tag
define decomp : composition.decompose para para.variants.selectorTree
local ssGrs {}
foreach { prime pv } [items-of decomp] : if (pv.tag && pv.rank) : begin
ssGrs.push pv
local dl [cvs.get pv.tag].decompositionLookup
if dl : feature.addLookup dl
ssGrs.sort AnyCv.compare
foreach gr [items-of ssGrs] : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
feature.addLookup cvSingle
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
foreach gr [items-of ssGrs] : begin
local subst : gr.get glyph
if (subst && subst != gn) : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst
do "Cleanup and link dependency"
local sortedCvs : Array.from [cvs.values]
sortedCvs.sort CvLookupManager.compare
foreach cv [items-of sortedCvs] : begin
local st cv.altrenatesLookup.substitutions
foreach { k v } [pairs-of st] : foreach idx [range 0 v.length] : if [not v.(idx)]
set v.(idx) k
cv.linkDeps
for [local i 1] (i < sortedCvs.length) [inc i]
sortedCvs.(i - 1).linkCrossDeps sortedCvs.(i)
gsub.endBlock rec
return cvs
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -0,0 +1,46 @@
$$include './meta/macros.ptl'
import [NumeratorForm DenominatorForm] from "@iosevka/glyph/relation"
# Name-driven feature pairs
export : define [buildFrac gsub glyphStore] : begin
local rec : gsub.beginBlock
define frac : gsub.addCommonFeature : gsub.createFeature 'frac'
define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
define subSolidus : gsub.createLookup : object
.type 'gsub_single'
.substitutions : object ['solidus' 'fractionBar'] ['slash' 'fractionBar']
define digitSet { }
define numSet { }
define denSet { }
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
local numForm : NumeratorForm.get g
local denForm : DenominatorForm.get g
if (numForm && denForm) : begin
digitSet.push gid
numSet.push numForm
denSet.push denForm
define subDen : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule [{'fractionBar'}.concat denSet] [digitSet ~> denSet]
define subNum : gsub.createLookup : object
.type 'gsub_reverse'
.rules : list
reverse-rule [digitSet ~> numSet] [{'fractionBar'}.concat numSet]
frac.addLookup subSolidus
frac.addLookup subDen
frac.addLookup subNum
gsub.setDependency subSolidus subDen
gsub.setDependency subSolidus subNum
gsub.endBlock rec
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

View file

@ -0,0 +1,19 @@
# Name-driven feature pairs
export : define [buildGrFeature gsub glyphStore gr] : begin
local rec : gsub.beginBlock
local mapping {.}
foreach { gnSrc glyph } [glyphStore.namedEntries] : begin
local gnDst : gr.get glyph
local glyphDst : glyphStore.queryByName gnDst
if (glyphDst && ([glyphStore.queryUnicodeOf glyph] || [glyphStore.queryUnicodeOf glyphDst]))
set mapping.(gnSrc) gnDst
if [objectIsNotEmpty mapping] : begin
define lookup1 : gsub.createLookup {.type 'gsub_single' .substitutions mapping}
define feature1 : gsub.addCommonFeature : gsub.createFeature gr.otlTag
feature1.addLookup lookup1
gsub.endBlock rec
define [objectIsNotEmpty obj] : obj && [Object.keys obj].length

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,99 @@
$$include './meta/macros.ptl'
import [CvDecompose] from "@iosevka/glyph/relation"
extern Set
export : define [buildLOCL gsub para glyphStore] : begin
local rec : gsub.beginBlock
define { chain-rule } : gsub.ChainRuleBuilder
define cyrlSRB : gsub.copyLanguage 'cyrl_SRB ' 'cyrl_DFLT'
define cyrlMKD : gsub.copyLanguage 'cyrl_MKD ' 'cyrl_DFLT'
define cyrlBGR : gsub.copyLanguage 'cyrl_BGR ' 'cyrl_DFLT'
define latnVIT : gsub.copyLanguage 'latn_VIT ' 'latn_DFLT'
# SRB
define loclSRB : gsub.createFeature 'locl'
cyrlSRB.addFeature loclSRB
cyrlMKD.addFeature loclSRB
loclSRB.addLookup : gsub.createLookup
.type 'gsub_single'
.substitutions : if para.isItalic
object
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.SRB'
'cyrl/de' : glyphStore.ensureExists 'cyrl/de.SRB'
'cyrl/gje' : glyphStore.ensureExists 'cyrl/gje.SRB'
'cyrl/pe' : glyphStore.ensureExists 'cyrl/pe.SRB'
'cyrl/te' : glyphStore.ensureExists 'cyrl/te.SRB'
object
'cyrl/be' : glyphStore.ensureExists 'cyrl/be.SRB'
# BGR
define loclBGR : cyrlBGR.addFeature : gsub.createFeature 'locl'
loclBGR.addLookup : gsub.createLookup
.type 'gsub_single'
.substitutions : object
'cyrl/ve' : glyphStore.ensureExists 'cyrl/ve.BGR'
'cyrl/ghe' : glyphStore.ensureExists 'cyrl/ghe.italic'
'cyrl/De' : glyphStore.ensureExists 'cyrl/De.BGR'
'cyrl/de' : glyphStore.ensureExists 'cyrl/de.BGR'
'cyrl/zhe' : glyphStore.ensureExists 'cyrl/zhe.BGR'
'cyrl/ze' : glyphStore.ensureExists 'cyrl/ze.BGR'
'cyrl/i' : glyphStore.ensureExists 'cyrl/i.BGR'
'cyrl/ibreve' : glyphStore.ensureExists 'cyrl/ibreve.BGR'
'cyrl/igrave' : glyphStore.ensureExists 'cyrl/igrave.BGR'
'cyrl/ka' : glyphStore.ensureExists 'cyrl/ka.BGR'
'cyrl/El' : glyphStore.ensureExists 'cyrl/El.BGR'
'cyrl/el' : glyphStore.ensureExists 'cyrl/el.BGR'
'cyrl/en' : glyphStore.ensureExists 'cyrl/en.BGR'
'cyrl/pe' : glyphStore.ensureExists 'cyrl/pe.BGR'
'cyrl/te' : glyphStore.ensureExists 'cyrl/te.BGR'
'cyrl/Ef' : glyphStore.ensureExists 'cyrl/Ef.BGR'
'cyrl/ef' : glyphStore.ensureExists 'cyrl/ef.BGR'
'cyrl/che' : glyphStore.ensureExists 'cyrl/che.BGR'
'cyrl/sha' : glyphStore.ensureExists 'cyrl/sha.BGR'
'cyrl/shcha' : glyphStore.ensureExists 'cyrl/shcha.BGR'
'cyrl/yu' : glyphStore.ensureExists 'cyrl/yu.BGR'
'cyrl/tse' : glyphStore.ensureExists 'cyrl/tse.BGR'
'cyrl/yer' : glyphStore.ensureExists 'cyrl/yer.BGR'
'cyrl/yeri' : glyphStore.ensureExists 'cyrl/yeri.BGR'
# VIT
define loclVIT : latnVIT.addFeature : gsub.createFeature 'locl'
define [sx s] : lambda [t] : t.map : lambda [x] "\(x)/\(s)"
do "Decompose Vietnamese glyphs"
local decompositionSubstitutions {.}
loclVIT.addLookup : gsub.createLookup
.type 'gsub_multiple'
.substitutions decompositionSubstitutions
local viCircumflex 'ẦẤẨỀẾỂỒỐỔầấẩềếểồốổ'
local viBreve 'ẮẰẲằắẳ'
local viShiftableMarks { 'acuteAbove' 'graveAbove' 'hookAbove' }
foreach ch (viCircumflex + viBreve) : begin
local u : ch.codePointAt 0
local gn : glyphStore.queryNameByUnicode u
local g : glyphStore.queryByNameEnsured gn
local decomp : CvDecompose.get g
set decompositionSubstitutions.(gn) decomp
loclVIT.addLookup : gsub.createLookup
.type 'gsub_chaining'
.rules : list
chain-rule
{'A' 'a' 'E' 'e' 'O' 'o'} ~> nothing
{'circumflexAbove'} ~> nothing
viShiftableMarks ~> [sx 'viSide']
chain-rule
{'A' 'a'} ~> nothing
{'breveAbove'} ~> nothing
viShiftableMarks ~> [sx 'viCenter']
gsub.endBlockAtFront rec

View file

@ -0,0 +1,102 @@
$$include './meta/macros.ptl'
extern Set
import [Texture CvDecompose] from "@iosevka/glyph/relation"
# Name-driven feature pairs
export : define [buildGsubTexture gsub glyphStore markGlyphs cvs] : begin
local anyMark : Array.from markGlyphs.all
local rec : gsub.beginBlock
define txtr : gsub.addCommonFeature : gsub.createFeature 'TXTR'
define { chain-rule reverse-rule } : gsub.ChainRuleBuilder
define look-around null
local extLFrom { }
local extRFrom { }
local extLRFrom { }
local extLTo { }
local extRTo { }
local extLRTo { }
local shrLFrom { }
local shrRFrom { }
local shrLRFrom { }
local shrLTo { }
local shrRTo { }
local shrLRTo { }
local anyInfluence : new Set
foreach { gid g } [glyphStore.namedEntries] : if (gid.(0) !== ".") : begin
local extL : Texture.ExtL.get g
local extR : Texture.ExtR.get g
local extLR : Texture.ExtLR.get g
local shrL : Texture.ShrL.get g
local shrR : Texture.ShrR.get g
local shrLR : Texture.ShrLR.get g
if extL : begin [extLFrom.push gid] [extLTo.push extL]
if extR : begin [extRFrom.push gid] [extRTo.push extR]
if extLR : begin [extLRFrom.push gid] [extLRTo.push extLR]
if (extL || extR || extLR) : begin [anyInfluence.add gid]
if shrL : begin [shrLFrom.push gid] [shrLTo.push shrL]
if shrR : begin [shrRFrom.push gid] [shrRTo.push shrR]
if shrLR : begin [shrLRFrom.push gid] [shrLRTo.push shrLR]
# Build the lookup by case-analysis
# Backtracks will be the "target"/"to" glyphs, while look-aheads will be the "from" glyphs
define sub : gsub.createLookup
.type 'gsub_chaining'
.ignoreGlyphs anyMark
.rules : list
chain-rule # Extension to both sides
[{}.concat shrRTo shrLRTo] ~> look-around
extLRFrom ~> extLRTo
[{}.concat shrLFrom shrLRFrom] ~> look-around
chain-rule # Shrink to both sides
[{}.concat extRTo extLRTo] ~> look-around
shrLRFrom ~> shrLRTo
[{}.concat extLFrom extLRFrom] ~> look-around
chain-rule # Extension to the left
[{}.concat shrRTo shrLRTo] ~> look-around
extLFrom ~> extLTo
chain-rule # Shrink to the left
[{}.concat extRTo extLRTo] ~> look-around
shrLFrom ~> shrLTo
chain-rule # Extension to the right
extRFrom ~> extRTo
[{}.concat shrLFrom shrLRFrom] ~> look-around
chain-rule # Shrink to the right
shrRFrom ~> shrRTo
[{}.concat extLFrom extLRFrom] ~> look-around
txtr.addLookup sub
# Decompose everything if we enable TXTR
if cvs
: then : begin
# Reuse existing Cvdecompose data
foreach cv [cvs.values] : begin
if cv.decompositionLookup : begin
txtr.addLookup cv.decompositionLookup
gsub.setDependency cv.decompositionLookup sub
: else : begin
# Built it from scratch
local decompose : gsub.createLookup
.type 'gsub_multiple'
.substitutions {.}
foreach { gn glyph } [glyphStore.namedEntries] : begin
local parts : CvDecompose.get glyph
if parts : begin
local influenced : anyInfluence.has gn
foreach part [items-of parts] : if [anyInfluence.has part] : set influenced true
if influenced : set decompose.substitutions.(gn) parts
txtr.addLookup decompose
gsub.setDependency decompose sub
gsub.endBlock rec

View file

@ -0,0 +1,48 @@
$$include './meta/macros.ptl'
import [QueryRelatedGlyphs] from "./table-util.mjs"
export : define [buildGsubThousands gsub para] : begin
local rec : gsub.beginBlock
define Thousand : gsub.addCommonFeature : gsub.createFeature 'THND'
define {chain-rule reverse-rule} : gsub.ChainRuleBuilder
define numberGlyphIDs {
'zero.lnum' 'one.lnum' 'two.lnum' 'three.lnum' 'four.lnum'
'five.lnum' 'six.lnum' 'seven.lnum' 'eight.lnum' 'nine.lnum'
}
define [nd s] : numberGlyphIDs.map : lambda [x] "\(x).nd\(s)"
define a : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule ({'period'} ~> null) (numberGlyphIDs ~> [nd 2]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
chain-rule ([nd 2] ~> null) (numberGlyphIDs ~> [nd 1])
chain-rule ([nd 1] ~> null) (numberGlyphIDs ~> [nd 6])
chain-rule ([nd 6] ~> null) (numberGlyphIDs ~> [nd 5])
chain-rule ([nd 5] ~> null) (numberGlyphIDs ~> [nd 4])
chain-rule ([nd 4] ~> null) (numberGlyphIDs ~> [nd 3])
chain-rule ([nd 3] ~> null) (numberGlyphIDs ~> [nd 2])
define b : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_chaining'
.rules : list
chain-rule (numberGlyphIDs ~> [nd 0]) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null) (numberGlyphIDs ~> null)
chain-rule ([nd 0] ~> null) (numberGlyphIDs ~> [nd 0])
define c : Thousand.addLookup : gsub.createLookup : object
.type 'gsub_reverse'
.rules : list
reverse-rule ([nd 0] ~> [nd 1]) ([nd 0] ~> null)
reverse-rule ([nd 0] ~> [nd 2]) ([nd 1] ~> null)
reverse-rule ([nd 0] ~> [nd 3]) ([nd 2] ~> null)
reverse-rule ([nd 0] ~> [nd 4]) ([nd 3] ~> null)
reverse-rule ([nd 0] ~> [nd 5]) ([nd 4] ~> null)
reverse-rule ([nd 0] ~> [nd 6]) ([nd 5] ~> null)
reverse-rule ([nd 0] ~> [nd 1]) ([nd 6] ~> null)
gsub.setDependency a b
gsub.setDependency b c
gsub.endBlock rec

View file

@ -0,0 +1,122 @@
import as Gr from "@iosevka/glyph/relation"
import [CreateEmptyTable] from "./table-util.mjs"
import [buildLigations] from "./gsub-ligation.mjs"
import [buildCCMP buildCCMPPostCvSs] from "./gsub-ccmp.mjs"
import [buildGrFeature] from "./gsub-gr.mjs"
import [buildFrac] from "./gsub-frac.mjs"
import [buildCVSS] from "./gsub-cv-ss.mjs"
import [buildLOCL] from "./gsub-locl.mjs"
import [buildGsubThousands] from "./gsub-thousands.mjs"
import [buildGsubTexture] from "./gsub-texture.mjs"
import [buildMarkMkmk] from "./gpos-mark-mkmk.mjs"
define GDEF_SIMPLE 1
define GDEF_LIGATURE 2
define GDEF_MARK 3
extern Set
extern Map
# GSUB
define [buildGSUB para glyphStore markGlyphs] : begin
define gsub : CreateEmptyTable
# NWID / WWID
if para.enableNwidWwid : begin
buildGrFeature gsub glyphStore Gr.Nwid
buildGrFeature gsub glyphStore Gr.Wwid
# lnum / onum
buildGrFeature gsub glyphStore Gr.Lnum
buildGrFeature gsub glyphStore Gr.Onum
# zero
buildGrFeature gsub glyphStore Gr.Zero
# APLF
if para.enableCvSs : begin
buildGrFeature gsub glyphStore Gr.AplForm
# numr / dnom
buildGrFeature gsub glyphStore Gr.NumeratorForm
buildGrFeature gsub glyphStore Gr.DenominatorForm
# ccmp
local ccmp : buildCCMP gsub glyphStore markGlyphs
# frac
buildFrac gsub glyphStore
# Ligation
if para.enableLigation : do
define plm : Object.assign {.} para.ligation.defaultBuildup
if (para.ligation.caltBuildup && para.ligation.caltBuildup.length) : begin
set plm.calt para.ligation.caltBuildup
buildLigations gsub para plm
# THND
buildGsubThousands gsub para glyphStore
# cv##, ss##
local cvs nothing
if para.enableCvSs : begin
set cvs : buildCVSS gsub para glyphStore
# ccmp post cv/ss (for Ogonek shape transform)
buildCCMPPostCvSs gsub ccmp glyphStore markGlyphs
# locl
# Builds last, but the lookups are added into the beginning of the lookup list
buildLOCL gsub para glyphStore
# TXTR, "texture" feature
if (para.buildTextureFeature && !para.isQuasiProportional) : begin
buildGsubTexture gsub glyphStore markGlyphs cvs
gsub.finalize
return gsub
# GPOS
define [buildGPOS para glyphStore markGlyphs] : begin
define gpos : CreateEmptyTable
buildMarkMkmk gpos glyphStore markGlyphs
gpos.finalize
return gpos
# GDEF
define [buildGDEF para glyphStore markGlyphs] : begin
local GDEF { .glyphClassDef {.} .markAttachClassDef {.} .markGlyphSets {} }
foreach { gn glyph } [glyphStore.namedEntries] : piecewise
[markGlyphs.all.has gn] : set GDEF.glyphClassDef.(gn) GDEF_MARK
[[regex '_'].test gn] : set GDEF.glyphClassDef.(gn) GDEF_LIGATURE
true : set GDEF.glyphClassDef.(gn) GDEF_SIMPLE
local n 1
local m : new Map
foreach { gn clsStr } markGlyphs.markAttachClassDef : begin
local clsNum : m.get clsStr
if [not clsNum] : begin
set clsNum n
set n (n + 1)
m.set clsStr clsNum
set GDEF.markAttachClassDef.(gn) clsNum
set GDEF.markGlyphSets markGlyphs.markGlyphSets
return GDEF
export : define [buildOtl para glyphStore] : begin
local markGlyphs { .all [new Set] .markAttachClassDef [new Map] .markGlyphSets {} }
local GPOS : buildGPOS para glyphStore markGlyphs
local GDEF : buildGDEF para glyphStore markGlyphs
local GSUB : buildGSUB para glyphStore markGlyphs
# Make all diacritics join-l (Kitty #3716)
foreach gnMark markGlyphs.all : begin
Gr.Joining.or [glyphStore.queryByName gnMark] Gr.Joining.Classes.Left
return [object GSUB GPOS GDEF]

View file

@ -0,0 +1,8 @@
### Autoarg macro
define-operator "--" 890 'right' : syntax-rules
`(@l -- @r) [atom l] : dirty `[new $NamedParameterPair$ @{".quote" [formOf l]} @r]
`(@{".quote" l} -- @r) : dirty `[new $NamedParameterPair$ @l @r]
### Arbitrary pair operator
define-operator "~>" 880 'right' : syntax-rules
`(@l ~> @r) `{.left @l .right @r}

View file

@ -0,0 +1,237 @@
import toposort from 'toposort'
import [AnyCv] from "@iosevka/glyph/relation"
extern Map
extern Set
export : define [CreateEmptyTable] : new LayoutTable
class LayoutTable
public [new] : begin
set this.languages {.}
set this.features {.}
set this.lookups {.}
set this.lookupDep {}
set this.lookupOrder {}
public [pickLanguage tag] : begin
if this.languages.(tag) : return this.languages.(tag)
define lang : new LayoutLanguage tag
set this.languages.(tag) lang
return lang
public [copyLanguage tag tagFrom] : begin
define langFrom : this.pickLanguage tagFrom
define langTo : this.pickLanguage tag
foreach [feat : items-of langFrom.features] : langTo.features.push feat
return langTo
public [createFeature tag] : begin
local feature : new LayoutFeature tag
local n 0
while true : begin
if [not this.features.(tag + '_' + n)] : begin
set feature.name : tag + '_' + n
set this.features.(tag + '_' + n) feature
return feature
set n : n + 1
public [addCommonFeature fea] : begin
define dfltDflt : this.pickLanguage 'DFLT_DFLT'
define latnDflt : this.pickLanguage 'latn_DFLT'
define grekDflt : this.pickLanguage 'grek_DFLT'
define cyrlDflt : this.pickLanguage 'cyrl_DFLT'
dfltDflt.addFeature fea
latnDflt.addFeature fea
grekDflt.addFeature fea
cyrlDflt.addFeature fea
return fea
public [createLookup data _prefix] : begin
local prefix : _prefix || 'lookup/'
local n 0
while true : begin
if [not this.lookups.(prefix + n)] : begin
return : this.addLookupNoCheck (prefix + n) data
set n : n + 1
public [pickLookup name fallback] : begin
if this.lookups.(name) : return this.lookups.(name)
return : this.addLookupNoCheck name fallback
public [addLookupNoCheck name data] : begin
local lookup : Object.fromEntries : Object.entries data
set lookup.name name
set this.lookups.(name) lookup
return lookup
public [setDependency a b] : begin
if [not a.name] : throw : new Error "Invalid lookup"
if [not b.name] : throw : new Error "Invalid lookup"
this.lookupDep.push { a.name b.name }
public [beginBlock] : begin
return : object
existingLookupNames : new Set : Object.keys this.lookups
public [endBlock rec] : begin
local currentLookupNames : new Set : Object.keys this.lookups
foreach existing rec.existingLookupNames : foreach current currentLookupNames
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
this.lookupDep.push { existing current }
public [endBlockAtFront rec] : begin
local currentLookupNames : new Set : Object.keys this.lookups
foreach existing rec.existingLookupNames : foreach current currentLookupNames
if (![IsUtilityLookupId existing] && ![IsUtilityLookupId current] && ![rec.existingLookupNames.has current])
this.lookupDep.push { current existing }
public [finalize] : begin
local lns : new Set : Object.keys this.lookups
foreach [front lns] : foreach [rear lns]
if (![IsUtilityLookupId front] && [IsUtilityLookupId rear])
this.lookupDep.push { front rear }
set this.lookupOrder : toposort this.lookupDep
foreach [{key lang} : pairs-of this.languages] : begin
if lang.features : lang.features.sort
public [ChainRuleBuilder] : begin
local table this
define [createNewLookup f t] : begin
local subst {.}
foreach [j : range 0 f.length] : set subst.(f.(j)) t.(j)
return : table.createLookup {.type 'gsub_single' .substitutions subst} UtilityLookupPrefix
define [getSubLookup left right] : piecewise
[not right] null
([typeof right] === "string") : throw : new Error "Invalid substitution"
(right <@ Function) : getSubLookup left [right left]
true : begin
local found null
local maxMatch 0
local lookupKeys : [Object.keys table.lookups].reverse
foreach [name : items-of lookupKeys] : if [IsUtilityLookupId name] : begin
local lookup table.lookups.(name)
local st lookup.substitutions
local compatible true
local matchCount 0
foreach [j : range 0 left.length] : begin
if (st.(left.(j)) && st.(left.(j)) !== right.(j)) : set compatible false
if (st.(left.(j)) === right.(j)) : inc matchCount
if (compatible && (!found || matchCount > maxMatch)) : begin
set found lookup
set maxMatch matchCount
if found : begin
local st found.substitutions
foreach [j : range 0 left.length] : set st.(left.(j)) right.(j)
return found
return : createNewLookup left right
define [chain-rule] : begin
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x {.left x .right null}])
local rule {.match {} .apply {} .inputBegins 0 .inputEnds 0}
local foundi false
local founde false
foreach [j : range 0 terms.length] : if (!foundi && terms.(j).right) : begin
set rule.inputBegins j
set foundi true
foreach [j : range (terms.length - 1) downtill 0] : if (!founde && terms.(j).right) : begin
set rule.inputEnds (j + 1)
set founde true
foreach [j : range 0 terms.length] : begin
local term terms.(j)
rule.match.push : Array.from : new Set term.left
local lookup : getSubLookup term.left term.right
if lookup : rule.apply.push {.at j .lookup {.name lookup.name}}
return rule
define [reverse-rule] : begin
local terms : [{}.slice.call arguments 0].map (x -> [if x.left x {.left x .right null}])
local rule {.match {} .to {} .inputIndex 0}
local foundi false
foreach [j : range 0 terms.length] : begin
local term terms.(j)
local subst : new Map
foreach [g : items-of term.left] : subst.set g g
if term.right : begin
if foundi : throw : new Error "Duplicate substitutions in one reverse rule"
set foundi true
set rule.inputIndex j
local toGlyphs : piecewise
(term.right <@ Function) [term.right term.left]
true term.right
foreach [k : range 0 term.left.length]
subst.set term.left.(k) (toGlyphs.(k) || term.left.(k))
set rule.to : Array.from [subst.values]
set rule.match.(j) : Array.from [subst.keys]
return rule
return {chain-rule reverse-rule}
class LayoutFeature
public [new tag] : begin
set this.tag tag
set this.name tag
set this.lookups {}
public [addLookup lookup] : begin
if [not lookup.name] : throw : new Error "Invalid lookup"
define index : this.lookups.indexOf lookup.name
if (index < 0) : this.lookups.push lookup.name
return lookup
class LayoutLanguage
public [new tag] : begin
set this.tag tag
set this.features {}
public [addFeature feature] : begin
if [not feature.name] : throw : new Error "Invalid feature"
define index : this.features.indexOf feature.name
if (index < 0) : this.features.push feature.name
return feature
define UtilityLookupPrefix '.utility-single/'
define [IsUtilityLookupId name] : [name.slice 0 UtilityLookupPrefix.length] === UtilityLookupPrefix
export : define [QueryRelatedGlyphs glyphs para entries] : begin
define sink {}
foreach [gid : items-of entries] : if glyphs.(gid) : begin
sink.push gid
if para.enableCvSs : foreach [gr : items-of : AnyCv.query glyphs.(gid)] : begin
sink.push : gr.get glyphs.(gid)
return sink
export : define [FinalizeTable table] : begin
set table.lookupOrder : toposort table.lookupDep
foreach [{key lang} : pairs-of table.languages] : begin
if lang.features : lang.features.sort
export : define [UkMapToLookup m] : begin
local l {}
local r {}
foreach { k v } [Object.entries m] : begin
l.push k
r.push v
return { .left l .right r }
export : define [UkMap2ToLookup mm] : begin
local res {}
foreach { g1 second } [Object.entries mm] : foreach { g2 gTo } [Object.entries second] : begin
res.push {.from {g1 g2} .to gTo}
return res