diff --git a/developer/src/kmc/src/keyman/compiler/bksp.ts b/developer/src/kmc/src/keyman/compiler/bksp.ts index ae3540529b..865d648f34 100644 --- a/developer/src/kmc/src/keyman/compiler/bksp.ts +++ b/developer/src/kmc/src/keyman/compiler/bksp.ts @@ -1,6 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { Bksp, BkspItem, BkspItemFlags } from "../kmx/kmx-plus"; -import { ElementString } from "../kmx/element-string"; +import { Bksp, BkspItem, BkspItemFlags, GlobalSections } from "../kmx/kmx-plus"; import { LKBackspace, LKBackspaces } from "../ldml-keyboard/ldml-keyboard-xml"; import { SectionCompiler } from "./section-compiler"; @@ -17,28 +16,28 @@ export class BkspCompiler extends SectionCompiler { return valid; } - private compileBackspace(backspace: LKBackspace): BkspItem { + private compileBackspace(sections: GlobalSections, backspace: LKBackspace): BkspItem { let result = new BkspItem(); - result.from = new ElementString(backspace.from); - result.to = backspace.to; - result.before = new ElementString(backspace.before); + result.from = sections.elem.allocElementString(sections.strs, backspace.from); + result.to = sections.strs.allocString(backspace.to); + result.before = sections.elem.allocElementString(sections.strs, backspace.before); result.flags = backspace.error == 'fail' ? BkspItemFlags.error : BkspItemFlags.none; return result; } - private compileBackspaces(backspaces: LKBackspaces): Bksp { + private compileBackspaces(sections: GlobalSections, backspaces: LKBackspaces): Bksp { let result = new Bksp(); if(backspaces?.backspace) { for(let backspace of backspaces.backspace) { - result.items.push(this.compileBackspace(backspace)); + result.items.push(this.compileBackspace(sections, backspace)); } } return result; } - public compile(): Bksp { - return this.compileBackspaces(this.keyboard.backspaces); + public compile(sections: GlobalSections): Bksp { + return this.compileBackspaces(sections, this.keyboard.backspaces); } } diff --git a/developer/src/kmc/src/keyman/compiler/compiler.ts b/developer/src/kmc/src/keyman/compiler/compiler.ts index b793af0b71..56a9e31504 100644 --- a/developer/src/kmc/src/keyman/compiler/compiler.ts +++ b/developer/src/kmc/src/keyman/compiler/compiler.ts @@ -1,4 +1,4 @@ -import KMXPlusFile from '../kmx/kmx-plus'; +import KMXPlusFile, { Elem, Strs } from '../kmx/kmx-plus'; import LDMLKeyboardXMLSourceFile from '../ldml-keyboard/ldml-keyboard-xml'; import LDMLKeyboardXMLSourceFileReader from '../ldml-keyboard/ldml-keyboard-xml-reader'; import { BkspCompiler } from './bksp'; @@ -65,20 +65,26 @@ export default class Compiler { public compile(source: LDMLKeyboardXMLSourceFile): KMXPlusFile { const sections = this.buildSections(source); let passed = true; + const kmx = new KMXPlusFile(); + + // These two sections are required by other sections + kmx.kmxplus.strs = new Strs(); + kmx.kmxplus.elem = new Elem(kmx.kmxplus.strs); + for(let section of sections) { if(!section.validate()) { - //console.log(`failed to validate ${section.id}`); passed = false; // We'll keep validating other sections anyway, so we get a full set of // errors for the keyboard developer. continue; } - const sect = section.compile(); + const sect = section.compile({strs: kmx.kmxplus.strs, elem: kmx.kmxplus.elem}); + + /* istanbul ignore if */ if(!sect) { // This should not really happen -- validate() should be telling us // if something is going to fail to compiler - //console.log(`failed to compile ${section.id}`); passed = false; continue; } diff --git a/developer/src/kmc/src/keyman/compiler/keys.ts b/developer/src/kmc/src/keyman/compiler/keys.ts index 8c6119cc3c..6419fa2b68 100644 --- a/developer/src/kmc/src/keyman/compiler/keys.ts +++ b/developer/src/kmc/src/keyman/compiler/keys.ts @@ -1,5 +1,5 @@ import { constants } from '@keymanapp/ldml-keyboard-constants'; -import { Keys } from '../kmx/kmx-plus'; +import { GlobalSections, Keys } from '../kmx/kmx-plus'; import * as LDMLKeyboard from '../ldml-keyboard/ldml-keyboard-xml'; import { USVirtualKeyMap } from "../ldml-keyboard/virtual-key-constants"; import { CompilerMessages } from './messages'; @@ -12,12 +12,12 @@ export class KeysCompiler extends SectionCompiler { return constants.section.keys; } - public compile(): Keys { + public compile(sections: GlobalSections): Keys { // Use LayerMap + keys to generate compiled keys for hardware if(this.keyboard.layerMaps?.[0]?.form == 'hardware') { for(let layer of this.keyboard.layerMaps[0].layerMap) { - let sect = this.compileHardwareLayer(layer); + let sect = this.compileHardwareLayer(sections, layer); return sect; } } @@ -28,6 +28,7 @@ export class KeysCompiler extends SectionCompiler { } private compileHardwareLayer( + sections: GlobalSections, layer: LDMLKeyboard.LKLayerMap ): Keys { let result = new Keys(); @@ -58,7 +59,7 @@ export class KeysCompiler extends SectionCompiler { result.keys.push({ vkey: USVirtualKeyMap[y][x], mod: mod, - to: keydef.to, + to: sections.strs.allocString(keydef.to), flags: 0 // Note: 'expand' is never set here, only by the .kmx builder }); } diff --git a/developer/src/kmc/src/keyman/compiler/loca.ts b/developer/src/kmc/src/keyman/compiler/loca.ts index cf8141d8ac..c7c4402e1c 100644 --- a/developer/src/kmc/src/keyman/compiler/loca.ts +++ b/developer/src/kmc/src/keyman/compiler/loca.ts @@ -1,5 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { Loca } from "../kmx/kmx-plus"; +import { GlobalSections, Loca } from "../kmx/kmx-plus"; import { SectionCompiler } from "./section-compiler"; import { CompilerMessages } from "./messages"; import { LKKeyboard } from "../ldml-keyboard/ldml-keyboard-xml"; @@ -37,7 +37,7 @@ export class LocaCompiler extends SectionCompiler { return valid; } - public compile(): Loca { + public compile(sections: GlobalSections): Loca { let result = new Loca(); // This also minimizes locales according to Remove Likely Subtags algorithm: @@ -54,7 +54,8 @@ export class LocaCompiler extends SectionCompiler { // TODO: remove `as any` cast: (Intl as any): ts lib version we have doesn't // yet include `getCanonicalLocales` but node 16 does include it so we can // safely use it. Also well supported in modern browsers. - result.locales = (Intl as any).getCanonicalLocales(locales); + const canonicalLocales = (Intl as any).getCanonicalLocales(locales) as string[]; + result.locales = canonicalLocales.map(locale => sections.strs.allocString(locale)); if(result.locales.length < locales.length) { this.callbacks.reportMessage(CompilerMessages.Hint_OneOrMoreRepeatedLocales()); diff --git a/developer/src/kmc/src/keyman/compiler/meta.ts b/developer/src/kmc/src/keyman/compiler/meta.ts index 47849805b0..3f80ea5811 100644 --- a/developer/src/kmc/src/keyman/compiler/meta.ts +++ b/developer/src/kmc/src/keyman/compiler/meta.ts @@ -1,5 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { KeyboardSettings, Meta, Meta_NormalizationForm } from "../kmx/kmx-plus"; +import { GlobalSections, KeyboardSettings, Meta, Meta_NormalizationForm } from "../kmx/kmx-plus"; import { isValidEnumValue } from "../util/util"; import { CompilerMessages } from "./messages"; import { SectionCompiler } from "./section-compiler"; @@ -41,14 +41,14 @@ export class MetaCompiler extends SectionCompiler { return true; } - public compile(): Meta { + public compile(sections: GlobalSections): Meta { let result = new Meta(); - result.author = this.keyboard.info?.author; - result.conform = this.keyboard.conformsTo; - result.layout = this.keyboard.info?.layout; - result.normalization = this.keyboard.info?.normalization as Meta_NormalizationForm; - result.indicator = this.keyboard.info?.indicator; - result.version = this.keyboard.version?.number ?? "0"; + result.author = sections.strs.allocString(this.keyboard.info?.author); + result.conform = sections.strs.allocString(this.keyboard.conformsTo); + result.layout = sections.strs.allocString(this.keyboard.info?.layout); + result.normalization = sections.strs.allocString(this.keyboard.info?.normalization); + result.indicator = sections.strs.allocString(this.keyboard.info?.indicator); + result.version = sections.strs.allocString(this.keyboard.version?.number ?? "0"); result.settings = (this.keyboard.settings?.fallback == "omit" ? KeyboardSettings.fallback : 0) | (this.keyboard.settings?.transformFailure == "omit" ? KeyboardSettings.transformFailure : 0) | diff --git a/developer/src/kmc/src/keyman/compiler/name.ts b/developer/src/kmc/src/keyman/compiler/name.ts index 6a16ac5309..f209aacb7f 100644 --- a/developer/src/kmc/src/keyman/compiler/name.ts +++ b/developer/src/kmc/src/keyman/compiler/name.ts @@ -1,5 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { Name } from "../kmx/kmx-plus"; +import { GlobalSections, Name } from "../kmx/kmx-plus"; import { SectionCompiler } from "./section-compiler"; export class NameCompiler extends SectionCompiler { @@ -14,9 +14,9 @@ export class NameCompiler extends SectionCompiler { return valid; } - public compile(): Name { + public compile(sections: GlobalSections): Name { let result = new Name(); - result.names = this.keyboard.names?.name?.map(v => v.value) ?? []; + result.names = this.keyboard.names?.name?.map(v => sections.strs.allocString(v.value)) ?? []; return result; } } diff --git a/developer/src/kmc/src/keyman/compiler/ordr.ts b/developer/src/kmc/src/keyman/compiler/ordr.ts index 57ff68a4ec..7ea5f19921 100644 --- a/developer/src/kmc/src/keyman/compiler/ordr.ts +++ b/developer/src/kmc/src/keyman/compiler/ordr.ts @@ -1,6 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { Ordr, OrdrItem } from "../kmx/kmx-plus"; -import { ElementString } from "../kmx/element-string"; +import { GlobalSections, Ordr, OrdrItem } from "../kmx/kmx-plus"; import { LKReorder, LKReorders } from "../ldml-keyboard/ldml-keyboard-xml"; import { SectionCompiler } from "./section-compiler"; @@ -17,26 +16,26 @@ export class OrdrCompiler extends SectionCompiler { return valid; } - private compileReorder(reorder: LKReorder): OrdrItem { + private compileReorder(sections: GlobalSections, reorder: LKReorder): OrdrItem { let result = new OrdrItem(); - result.elements = new ElementString(reorder.from, reorder.order, reorder.tertiary, reorder.tertiary_base, reorder.prebase); - result.before = new ElementString(reorder.before); + result.elements = sections.elem.allocElementString(sections.strs, reorder.from, reorder.order, reorder.tertiary, reorder.tertiary_base, reorder.prebase); + result.before = sections.elem.allocElementString(sections.strs, reorder.before); return result; } - private compileReorders(reorders: LKReorders): Ordr { + private compileReorders(sections: GlobalSections, reorders: LKReorders): Ordr { let result = new Ordr(); if(reorders?.reorder) { for(let reorder of reorders.reorder) { - result.items.push(this.compileReorder(reorder)); + result.items.push(this.compileReorder(sections, reorder)); } } return result; } - public compile(): Ordr { - return this.compileReorders(this.keyboard.reorders); + public compile(sections: GlobalSections): Ordr { + return this.compileReorders(sections, this.keyboard.reorders); } } diff --git a/developer/src/kmc/src/keyman/compiler/section-compiler.ts b/developer/src/kmc/src/keyman/compiler/section-compiler.ts index 9400be2fbf..c75409cea7 100644 --- a/developer/src/kmc/src/keyman/compiler/section-compiler.ts +++ b/developer/src/kmc/src/keyman/compiler/section-compiler.ts @@ -1,4 +1,4 @@ -import { Section } from "../kmx/kmx-plus"; +import { GlobalSections, Section } from "../kmx/kmx-plus"; import LDMLKeyboardXMLSourceFile, { LKKeyboard } from "../ldml-keyboard/ldml-keyboard-xml"; import CompilerCallbacks from "./callbacks"; import { SectionIdent } from '@keymanapp/ldml-keyboard-constants'; @@ -12,15 +12,18 @@ export class SectionCompiler { this.callbacks = callbacks; } + /* istanbul ignore next */ public get id(): SectionIdent { return null; } + /* istanbul ignore next */ public get required(): boolean { return true; } - public compile(): Section { + /* istanbul ignore next */ + public compile(sections: GlobalSections): Section { return null; } diff --git a/developer/src/kmc/src/keyman/compiler/tran.ts b/developer/src/kmc/src/keyman/compiler/tran.ts index 0ca2e9d98f..804c549902 100644 --- a/developer/src/kmc/src/keyman/compiler/tran.ts +++ b/developer/src/kmc/src/keyman/compiler/tran.ts @@ -1,6 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; -import { Finl, FinlItem, Tran, TranItem, TranItemFlags } from "../kmx/kmx-plus"; -import { ElementString } from "../kmx/element-string"; +import { Finl, FinlItem, GlobalSections, Tran, TranItem, TranItemFlags } from "../kmx/kmx-plus"; import LDMLKeyboardXMLSourceFile, { LKTransform, LKTransforms } from "../ldml-keyboard/ldml-keyboard-xml"; import CompilerCallbacks from "./callbacks"; import { SectionCompiler } from "./section-compiler"; @@ -30,31 +29,31 @@ class TransformCompiler { - constructor(source: string, order?: string, tertiary?: string, tertiary_base?: string, prebase?: string) { + constructor(strs: Strs, source: string, order?: string, tertiary?: string, tertiary_base?: string, prebase?: string) { super(); //TODO-LDML: full UnicodeSet and parsing if(!source) { @@ -54,7 +55,7 @@ export class ElementString extends Array { for(let i = 0; i < items.length; i++) { let elem = new ElemElement(); - elem.value = items[i]; + elem.value = strs.allocString(items[i]); elem.order = orders.length ? parseInt(orders[i], 10) : 0; elem.tertiary = tertiaries.length ? parseInt(tertiaries[i], 10) : 0; elem.flags = ElemElementFlags.none | diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-elem.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-elem.ts index 4a9127f993..3f7bd0216a 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-elem.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-elem.ts @@ -1,6 +1,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { ElementString } from "../element-string"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { Elem } from "../kmx-plus"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /* ------------------------------------------------------------------ @@ -10,6 +11,7 @@ import { BUILDER_SECTION } from "./builder-section"; interface BUILDER_ELEM_ELEMENT { element: number; // str | UTF-32 char flags: number; + _value: string; }; interface BUILDER_ELEM_STRING { @@ -28,68 +30,71 @@ export interface BUILDER_ELEM extends BUILDER_SECTION { strings: BUILDER_ELEM_STRING[]; }; -export function build_elem(): BUILDER_ELEM { - return { +function binaryElemCompare(a: BUILDER_ELEM_STRING, b: BUILDER_ELEM_STRING): number { + for(let i = 0; i < a.items.length && i < b.items.length; i++) { + if(a.items[i]._value < b.items[i]._value) return -1; + if(a.items[i]._value > b.items[i]._value) return 1; + if(a.items[i].flags < b.items[i].flags) return -1; + if(a.items[i].flags > b.items[i].flags) return 1; + } + if(a.items.length < b.items.length) return -1; + if(a.items.length > b.items.length) return 1; + return 0; +} + +export function build_elem(source_elem: Elem, sect_strs: BUILDER_STRS): BUILDER_ELEM { + let result: BUILDER_ELEM = { ident: constants.hex_section_id(constants.section.elem), - size: 0, // finalized later + size: 0, // finalized below _offset: 0, - count: 0, // finalized later + count: source_elem.strings.length, reserved: 0, - strings: [], // finalized later + strings: [], // finalized below }; -} -/** - * - * @param sect_elem - * @returns false if the section should be omitted from the file - */ -export function finalize_elem(sect_elem: BUILDER_ELEM): boolean { - if(sect_elem.strings.length == 1) { - // If we have only the 'null' element string, then we don't include the - // section at all. - return false; - } - sect_elem.count = sect_elem.strings.length; - let offset = constants.length_elem + constants.length_elem_item * sect_elem.count; // TODO: consider padding - for(let string of sect_elem.strings) { - string.offset = offset; - offset += string.length * constants.length_elem_item_element; - } - sect_elem.size = offset; - return true; -} + result.strings = source_elem.strings.map(item => { + let res: BUILDER_ELEM_STRING = { + offset: 0, // finalized below + length: item.length, + items: [], + _value: item + }; -export function alloc_element_string(sect_strs: BUILDER_STRS, sect_elem: BUILDER_ELEM, value: ElementString) { - if(value === undefined || value === null) { - // the "null" element string - value = new ElementString(''); - } - - const idx = sect_elem.strings.findIndex(v => value.isEqual(v._value)); - if(idx >= 0) { - return idx; - } - - let str: BUILDER_ELEM_STRING = { - _value: value, - items: [], - length: value.length, - offset: 0 // will be filled in later - }; - - for(let v of value) { - str.items.push({ - // TODO: support UTF-32 char (!UnicodeSet) - // TODO: support UnicodeSet properly - element: alloc_string(sect_strs, v.value), - flags: constants.elem_flags_unicode_set | - v.flags | // - ((v.order ?? 0) << constants.elem_flags_order_bitshift) | // -128 to +127; used only by reorder element values - ((v.tertiary ?? 0) << constants.elem_flags_tertiary_bitshift) // -128 to +127; used only by reorder element values + res.items = item.map(v => { + return { + element: build_strs_index(sect_strs, v.value), + flags: constants.elem_flags_unicode_set | + v.flags | // + ((v.order ?? 0) << constants.elem_flags_order_bitshift) | // -128 to +127; used only by reorder element values + ((v.tertiary ?? 0) << constants.elem_flags_tertiary_bitshift), // -128 to +127; used only by reorder element values + _value: v.value.value + }; }); + return res; + }); + result.strings.sort((a,b) => binaryElemCompare(a, b)); + + /* Calculate offsets and total size */ + + let offset = constants.length_elem + constants.length_elem_item * result.count; + for(let item of result.strings) { + item.offset = offset; + offset += item.length * constants.length_elem_item_element; } - return sect_elem.strings.push(str) - 1; + result.size = offset; + return result; +} + +export function build_elem_index(sect_elem: BUILDER_ELEM, value: ElementString) { + if(!(value instanceof ElementString)) { + throw new Error('unexpected value '+value); + } + + const result = sect_elem.strings.findIndex(v => value.isEqual(v._value)); + if(result < 0) { + throw new Error('unexpectedly missing StrsItem '+value); + } + return result; } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-keys.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-keys.ts index 320b2e6540..fc94f6dbaa 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-keys.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-keys.ts @@ -1,7 +1,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KeyFlags, KMXPlusData } from "../kmx-plus"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /* ------------------------------------------------------------------ @@ -45,7 +45,7 @@ export function build_keys(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS): BUILD vkey: item.vkey, mod: item.mod, // todo: support 'extend' - to: alloc_string(sect_strs, item.to), + to: build_strs_index(sect_strs, item.to), flags: KeyFlags.extend // todo: support non-extended }); } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-loca.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-loca.ts index 9e6d99de17..95f060c034 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-loca.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-loca.ts @@ -5,7 +5,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlusData } from "../kmx-plus"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /** @@ -28,7 +28,7 @@ export function build_loca(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS): BUILD }; for(let item of kmxplus.loca.locales) { - loca.items.push(alloc_string(sect_strs, item)); + loca.items.push(build_strs_index(sect_strs, item)); } return loca; diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-meta.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-meta.ts index 05567af132..ac8228814f 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-meta.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-meta.ts @@ -5,7 +5,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlusData } from "../kmx-plus"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /** @@ -26,12 +26,12 @@ export function build_meta(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS): BUILD ident: constants.hex_section_id(constants.section.meta), size: constants.length_meta, _offset: 0, - author: alloc_string(sect_strs, kmxplus.meta.author), - conform: alloc_string(sect_strs, kmxplus.meta.conform), - layout: alloc_string(sect_strs, kmxplus.meta.layout), - normalization: alloc_string(sect_strs, kmxplus.meta.normalization), - indicator: alloc_string(sect_strs, kmxplus.meta.indicator), - version: alloc_string(sect_strs, kmxplus.meta.version), + author: build_strs_index(sect_strs, kmxplus.meta.author), + conform: build_strs_index(sect_strs, kmxplus.meta.conform), + layout: build_strs_index(sect_strs, kmxplus.meta.layout), + normalization: build_strs_index(sect_strs, kmxplus.meta.normalization), + indicator: build_strs_index(sect_strs, kmxplus.meta.indicator), + version: build_strs_index(sect_strs, kmxplus.meta.version), settings: kmxplus.meta.settings ?? 0, }; } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-name.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-name.ts index 79fc28ae65..d24a30c9fa 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-name.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-name.ts @@ -5,7 +5,7 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlusData } from "../kmx-plus"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /** @@ -32,7 +32,7 @@ export function build_name(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS): BUILD }; for(let item of kmxplus.name.names) { - name.items.push(alloc_string(sect_strs, item)); + name.items.push(build_strs_index(sect_strs, item)); } return name; diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-ordr.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-ordr.ts index e8d575faa4..9588ea444a 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-ordr.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-ordr.ts @@ -1,6 +1,6 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { KMXPlusData } from "../kmx-plus"; -import { alloc_element_string, BUILDER_ELEM } from "./build-elem"; +import { build_elem_index, BUILDER_ELEM } from "./build-elem"; import { BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; @@ -38,8 +38,8 @@ export function build_ordr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_e for(let item of kmxplus.ordr.items) { ordr.items.push({ - elements: alloc_element_string(sect_strs, sect_elem, item.elements), - before: alloc_element_string(sect_strs, sect_elem, item.before) + elements: build_elem_index(sect_elem, item.elements), + before: build_elem_index(sect_elem, item.before) }); } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-strs.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-strs.ts index 059f80a333..4b84e6c022 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-strs.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-strs.ts @@ -1,4 +1,5 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; +import { Strs, StrsItem } from "../kmx-plus"; import { BUILDER_SECTION } from "./builder-section"; /* ------------------------------------------------------------------ @@ -22,47 +23,48 @@ export interface BUILDER_STRS extends BUILDER_SECTION { items: BUILDER_STRS_ITEM[]; }; -export function build_strs(): BUILDER_STRS { - return { +function binaryStringCompare(a: string, b: string): number { + // https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islessthan + if(typeof a != 'string' || typeof b != 'string') { + throw new Error('binaryStringCompare: inputs must be strings'); + } + if(a < b) return -1; + if(a > b) return 1; + return 0; +} + +export function build_strs(source_strs: Strs): BUILDER_STRS { + let result: BUILDER_STRS = { ident: constants.hex_section_id(constants.section.strs), size: 0, // finalized later _offset: 0, - count: 0, // finalized later + count: source_strs.strings.length, reserved: 0, - items: [], // finalized later + items: [], // filled below }; -} -export function finalize_strs(sect_strs: BUILDER_STRS): void { - sect_strs.count = sect_strs.items.length; - let offset = constants.length_strs + constants.length_strs_item * sect_strs.count; + result.items = source_strs.strings.map(item => { return {_value: item.value, length: item.value.length, offset: 0}; }); + result.items.sort((a,b) => binaryStringCompare(a._value, b._value)); + + let offset = constants.length_strs + constants.length_strs_item * result.count; // TODO: consider padding - for(let item of sect_strs.items) { + for(let item of result.items) { item.offset = offset; offset += item.length * 2 + 2; /* UTF-16 code units + sizeof null terminator */ } - sect_strs.size = offset; + result.size = offset; + + return result; } -export function alloc_string(sect_strs: BUILDER_STRS, value: string) { - if(typeof value != 'string') { - if(value === undefined || value === null) { - value = ''; - } else { - throw 'unexpected value '+value; - } +export function build_strs_index(sect_strs: BUILDER_STRS, value: StrsItem) { + if(!(value instanceof StrsItem)) { + throw new Error('unexpected value '+ value); } - let idx = sect_strs.items.findIndex(v => v._value === value); - if(idx >= 0) { - return idx; + let result = sect_strs.items.findIndex(v => v._value === value.value); + if(result < 0) { + throw new Error('unexpectedly missing StrsItem '+value.value); } - - let item: BUILDER_STRS_ITEM = { - _value: value, - length: value.length, - offset: 0 // will be filled in later - }; - - return sect_strs.items.push(item) - 1; + return result; } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-tran.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-tran.ts index 78bc14f7de..7af98a2966 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-tran.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/build-tran.ts @@ -1,8 +1,8 @@ import { constants } from "@keymanapp/ldml-keyboard-constants"; import { Bksp, Finl, Tran } from "../kmx-plus"; -import { alloc_element_string, BUILDER_ELEM } from "./build-elem"; -import { alloc_string, BUILDER_STRS } from "./build-strs"; +import { build_elem_index, BUILDER_ELEM } from "./build-elem"; +import { build_strs_index, BUILDER_STRS } from "./build-strs"; import { BUILDER_SECTION } from "./builder-section"; /* ------------------------------------------------------------------ @@ -41,9 +41,9 @@ export function build_tran(source_tran: Tran|Finl|Bksp, sect_strs: BUILDER_STRS, for(let item of source_tran.items) { tran.items.push({ - from: alloc_element_string(sect_strs, sect_elem, item.from), - to: alloc_string(sect_strs, item.to), - before: alloc_element_string(sect_strs, sect_elem, item.before), + from: build_elem_index(sect_elem, item.from), + to: build_strs_index(sect_strs, item.to), + before: build_elem_index(sect_elem, item.before), flags: item.flags }); } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/kmx-plus-builder.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/kmx-plus-builder.ts index d5fabaccd1..73c06e98d7 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/kmx-plus-builder.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus-builder/kmx-plus-builder.ts @@ -7,10 +7,10 @@ import { BUILDER_KEYS, build_keys } from './build-keys'; import { BUILDER_LOCA, build_loca } from './build-loca'; import { BUILDER_META, build_meta } from './build-meta'; import { BUILDER_NAME, build_name } from './build-name'; -import { alloc_string, BUILDER_STRS, build_strs, finalize_strs } from './build-strs'; +import { BUILDER_STRS, build_strs } from './build-strs'; import { BUILDER_VKEY, build_vkey } from './build-vkey'; import { BUILDER_TRAN, build_tran } from './build-tran'; -import { alloc_element_string, BUILDER_ELEM, build_elem, finalize_elem } from './build-elem'; +import { BUILDER_ELEM, build_elem } from './build-elem'; import { BUILDER_ORDR, build_ordr } from './build-ordr'; type BUILDER_BKSP = BUILDER_TRAN; @@ -63,19 +63,10 @@ export default class KMXPlusBuilder { private build() { // Required sections: sect, strs, loca, meta - this.sect_sect = build_sect(); - - // We must prepare the strs section early so that other sections can - // reference it. However, it will be emitted in alpha order. - this.sect_strs = build_strs(); - // per C7043, the first string in sect_strs MUST be the zero-length string. - alloc_string(this.sect_strs, ''); - - // We must prepare the elem section early so that other sections can - // reference it. However, it will be emitted in alpha order. - this.sect_elem = build_elem(); - // per C7043, the first element string in sect_elem MUST be the zero-length string. - alloc_element_string(this.sect_strs, this.sect_elem, null); + // We must prepare the strs and elem sections early so that other sections can + // reference them. However, they will be emitted in alpha order. + this.sect_strs = build_strs(this.file.kmxplus.strs); + this.sect_elem = build_elem(this.file.kmxplus.elem, this.sect_strs); const build_bksp = build_tran; const build_finl = build_tran; @@ -90,16 +81,10 @@ export default class KMXPlusBuilder { this.sect_tran = build_tran(this.file.kmxplus.tran, this.sect_strs, this.sect_elem); this.sect_vkey = build_vkey(this.file.kmxplus); - // Finalize all sections - - if(!finalize_elem(this.sect_elem)) { // must be done after all element strings allocated - this.sect_elem = null; - } - - finalize_strs(this.sect_strs); // must be done after all strings allocated + // Finalize the sect (index) section + this.sect_sect = build_sect(); this.finalize_sect(); // must be done last - return this.sect_sect.total; } diff --git a/developer/src/kmc/src/keyman/kmx/kmx-plus.ts b/developer/src/kmc/src/keyman/kmx/kmx-plus.ts index a673902125..c866ef5f13 100644 --- a/developer/src/kmc/src/keyman/kmx/kmx-plus.ts +++ b/developer/src/kmc/src/keyman/kmx/kmx-plus.ts @@ -11,14 +11,36 @@ import KMXFile from './kmx'; export class Section { } +export class GlobalSections { + // These sections are used by other sections during compilation + strs: Strs; + elem: Elem; +} + // 'sect' export class Sect extends Section {}; // 'bksp' -- see 'tran' + // 'elem' -export class Elem extends Section {}; +export class Elem extends Section { + strings: ElementString[] = []; + constructor(strs: Strs) { + super(); + this.strings.push(new ElementString(strs, '')); // C7043: null element string + } + allocElementString(strs: Strs, source: string, order?: string, tertiary?: string, tertiary_base?: string, prebase?: string): ElementString { + let s = new ElementString(strs, source, order, tertiary, tertiary_base, prebase); + let result = this.strings.find(item => item.isEqual(s)); + if(result === undefined) { + result = s; + this.strings.push(result); + } + return result; + } +}; // 'finl' -- see 'tran' @@ -33,7 +55,7 @@ export enum KeyFlags { export class KeysItem { vkey: number; mod: number; - to: string; + to: StrsItem; flags: KeyFlags; }; @@ -44,7 +66,7 @@ export class Keys extends Section { // 'loca' export class Loca extends Section { - locales: string[] = []; + locales: StrsItem[] = []; }; // 'meta' @@ -59,19 +81,19 @@ export enum KeyboardSettings { export enum Meta_NormalizationForm { NFC='NFC', NFD='NFD', other='other' }; export class Meta extends Section { - author: string; - conform: string; - layout: string; - normalization: Meta_NormalizationForm; - indicator: string; - version: string; // semver version string, defaults to "0" + author: StrsItem; + conform: StrsItem; + layout: StrsItem; + normalization: StrsItem; + indicator: StrsItem; + version: StrsItem; // semver version string, defaults to "0" settings: KeyboardSettings; }; // 'name' export class Name extends Section { - names: string[] = []; + names: StrsItem[] = []; }; // 'ordr' @@ -87,7 +109,34 @@ export class Ordr extends Section { // 'strs' -export type Strs = Section; +export class StrsItem { + readonly value: string; + constructor(value: string) { + this.value = value; + } +} + +export class Strs extends Section { + strings: StrsItem[] = [ new StrsItem('') ]; // C7043: The null string is always requierd + + allocString(s?: string): StrsItem { + if(s === undefined || s === null) { + // undefined or null are always equivalent to empty string, see C7043 + s = ''; + } + + if(typeof s !== 'string') { + throw new Error('alloc_string: s must be a string, undefined, or null.'); + } + + let result = this.strings.find(item => item.value === s); + if(result === undefined) { + result = new StrsItem(s); + this.strings.push(result); + } + return result; + } +}; // 'tran' @@ -98,7 +147,7 @@ export enum TranItemFlags { export class TranItem extends Section { from: ElementString; - to: string; + to: StrsItem; before: ElementString; flags: TranItemFlags; }; diff --git a/developer/src/kmc/test/fixtures/basic.txt b/developer/src/kmc/test/fixtures/basic.txt index 00b9077cd1..f93f3ccea2 100644 --- a/developer/src/kmc/test/fixtures/basic.txt +++ b/developer/src/kmc/test/fixtures/basic.txt @@ -21,7 +21,7 @@ block(kmxheader) # struct COMP_KEYBOARD { 00 10 00 00 # KMX_DWORD dwFileVersion; // 0004 Version of the file - Keyman 4.0 is 0x0400 - 8f 97 57 27 # KMX_DWORD dwCheckSum; // 0008 As stored in keyboard + 26 d2 24 03 # KMX_DWORD dwCheckSum; // 0008 As stored in keyboard 00 00 00 00 # KMX_DWORD KeyboardID; // 000C as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts 01 00 00 00 # KMX_DWORD IsRegistered; // 0010 00 00 00 00 # KMX_DWORD version; // 0014 keyboard version @@ -128,35 +128,42 @@ block(elem) # struct COMP_KMXPLUS_ELEM { diff(elem,elemNull) # KMX_DWORD offset; // 0010+ offset from this blob sizeof(elemNull,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemBkspFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemBkspFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemFinlFrom) # KMX_DWORD offset; // 0010+ offset from this blob sizeof(elemFinlFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + diff(elem,elemTranFrom) # KMX_DWORD offset; // 0010+ offset from this blob + sizeof(elemTranFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + + diff(elem,elemBkspFrom) # KMX_DWORD offset; // 0010+ offset from this blob + sizeof(elemBkspFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) + diff(elem,elemOrdrFrom) # KMX_DWORD offset; // 0010+ offset from this blob sizeof(elemOrdrFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) diff(elem,elemOrdrBefore) # KMX_DWORD offset; // 0010+ offset from this blob sizeof(elemOrdrBefore,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) - diff(elem,elemTranFrom) # KMX_DWORD offset; // 0010+ offset from this blob - sizeof(elemTranFrom,8) # KMX_DWORD length; // 0014+ str length (ELEMENT units) block(elemNull) -block(elemBkspFrom) - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemBkspFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - block(elemFinlFrom) index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset +block(elemTranFrom) + index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint + 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + index(strNull,strElemTranFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint + 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + +block(elemBkspFrom) + index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint + 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + index(strNull,strElemBkspFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint + 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset + block(elemOrdrFrom) # index(strNull,strElemOrdrFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint @@ -170,12 +177,6 @@ block(elemOrdrBefore) index(strNull,strElemOrdrBefore,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset -block(elemTranFrom) - index(strNull,strElemTranFrom1,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - index(strNull,strElemTranFrom2,2) # KMX_DWORD element; // str: output string or UTF-32LE codepoint - 01 00 00 00 # KMX_DWORD flags; // flag and order values - unicodeset - block(endelem) # ---------------------------------------------------------------------------------------------------- @@ -271,54 +272,53 @@ block(strs) # struct COMP_KMXPLUS_STRS { # KMX_DWORD length; // 0014+ str length (UTF-16LE units) diff(strs,strNull) sizeof(strNull,2) + diff(strs,strVersion) sizeof(strVersion,2) + diff(strs,strNorm) sizeof(strNorm,2) + diff(strs,strName) sizeof(strName,2) diff(strs,strElemTranFrom1) sizeof(strElemTranFrom1,2) + diff(strs,strElemTranFrom2) sizeof(strElemTranFrom2,2) diff(strs,strElemBkspFrom2) sizeof(strElemBkspFrom2,2) - diff(strs,strKey1) sizeof(strKey1,2) - diff(strs,strKey2) sizeof(strKey2,2) diff(strs,strLocale) sizeof(strLocale,2) + diff(strs,strLayout) sizeof(strLayout,2) diff(strs,strAuthor) sizeof(strAuthor,2) diff(strs,strConformsTo) sizeof(strConformsTo,2) - diff(strs,strLayout) sizeof(strLayout,2) - diff(strs,strNorm) sizeof(strNorm,2) - diff(strs,strIndicator) sizeof(strIndicator,2) - diff(strs,strVersion) sizeof(strVersion,2) - diff(strs,strName) sizeof(strName,2) - - diff(strs,strElemOrdrFrom1) sizeof(strElemOrdrFrom1,2) - diff(strs,strElemOrdrFrom2) sizeof(strElemOrdrFrom2,2) - diff(strs,strElemOrdrFrom3) sizeof(strElemOrdrFrom3,2) - diff(strs,strElemOrdrBefore) sizeof(strElemOrdrBefore,2) - - diff(strs,strElemTranFrom2) sizeof(strElemTranFrom2,2) diff(strs,strTranTo) sizeof(strTranTo,2) + diff(strs,strKey1) sizeof(strKey1,2) + diff(strs,strKey2) sizeof(strKey2,2) + diff(strs,strElemOrdrFrom3) sizeof(strElemOrdrFrom3,2) + diff(strs,strElemOrdrFrom1) sizeof(strElemOrdrFrom1,2) + diff(strs,strElemOrdrBefore) sizeof(strElemOrdrBefore,2) + diff(strs,strElemOrdrFrom2) sizeof(strElemOrdrFrom2,2) + diff(strs,strIndicator) sizeof(strIndicator,2) + # String table -- block(x) is used to store the null u16char at end of each string # without interfering with sizeof() calculation above block(strNull) block(x) 00 00 # the zero-length string + block(strVersion) 30 00 block(x) 00 00 # '0' + block(strNorm) 4e 00 46 00 43 00 block(x) 00 00 # 'NFC' + block(strName) 54 00 65 00 73 00 74 00 4b 00 62 00 64 00 block(x) 00 00 # 'TestKbd' block(strElemTranFrom1) 5E 00 block(x) 00 00 # '^' + block(strElemTranFrom2) 61 00 block(x) 00 00 # 'a' block(strElemBkspFrom2) 65 00 block(x) 00 00 # 'e' - block(strKey1) 27 01 block(x) 00 00 # 'ħ' - block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' block(strLocale) 6d 00 74 00 block(x) 00 00 # 'mt' + block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty' block(strAuthor) 73 00 72 00 6c 00 32 00 39 00 35 00 block(x) 00 00 # 'srl295' block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 69 00 65 00 77 00 block(x) 00 00 # 'techpreview' - block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 'qwerty' - block(strNorm) 4e 00 46 00 43 00 block(x) 00 00 # 'NFC' - block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀' - block(strVersion) 30 00 block(x) 00 00 # '0' - - block(strName) 54 00 65 00 73 00 74 00 4b 00 62 00 64 00 block(x) 00 00 # 'TestKbd' - - # - block(strElemOrdrFrom1) 60 1a block(x) 00 00 # '᩠' - block(strElemOrdrFrom2) 75 1a block(x) 00 00 # '᩵' - block(strElemOrdrFrom3) 45 1a block(x) 00 00 # 'ᩅ' - block(strElemOrdrBefore) 6b 1a block(x) 00 00 # 'ᩫ' - - block(strElemTranFrom2) 61 00 block(x) 00 00 # 'a' block(strTranTo) E2 00 block(x) 00 00 # 'â' + block(strKey1) 27 01 block(x) 00 00 # 'ħ' + block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា' + # + block(strElemOrdrFrom3) 45 1a block(x) 00 00 # 'ᩅ' + block(strElemOrdrFrom1) 60 1a block(x) 00 00 # '᩠' + block(strElemOrdrBefore) 6b 1a block(x) 00 00 # 'ᩫ' + block(strElemOrdrFrom2) 75 1a block(x) 00 00 # '᩵' + block(strIndicator) 3d d8 40 de block(x) 00 00 # '🙀' + + + block(endstrs) # end of strs block diff --git a/developer/src/kmc/test/helpers/index.ts b/developer/src/kmc/test/helpers/index.ts index 872d89a556..e028caab8b 100644 --- a/developer/src/kmc/test/helpers/index.ts +++ b/developer/src/kmc/test/helpers/index.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { SectionCompiler } from '../../src/keyman/compiler/section-compiler'; -import { Section } from '../../src/keyman/kmx/kmx-plus'; +import { Elem, GlobalSections, Section, Strs } from '../../src/keyman/kmx/kmx-plus'; import LDMLKeyboardXMLSourceFileReader from '../../src/keyman/ldml-keyboard/ldml-keyboard-xml-reader'; import { CompilerEvent } from '../keyman/compiler/callbacks'; @@ -41,5 +41,12 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena if(!compiler.validate()) { return null; } - return compiler.compile(); + + let globalSections: GlobalSections = { + strs: new Strs(), + elem: null + }; + globalSections.elem = new Elem(globalSections.strs); + + return compiler.compile(globalSections); } diff --git a/developer/src/kmc/test/test-bksp.ts b/developer/src/kmc/test/test-bksp.ts index 781933f238..4426341848 100644 --- a/developer/src/kmc/test/test-bksp.ts +++ b/developer/src/kmc/test/test-bksp.ts @@ -15,11 +15,11 @@ describe('bksp', function () { assert.lengthOf(bksp.items, 1); assert.lengthOf(bksp.items[0].from, 2); - assert.strictEqual(bksp.items[0].from[0].value, "្"); - assert.strictEqual(bksp.items[0].from[1].value, "ម"); + assert.strictEqual(bksp.items[0].from[0].value.value, "្"); + assert.strictEqual(bksp.items[0].from[1].value.value, "ម"); assert.strictEqual(bksp.items[0].flags, BkspItemFlags.none); assert.isEmpty(bksp.items[0].before); - assert.strictEqual(bksp.items[0].to, ""); + assert.strictEqual(bksp.items[0].to.value, ""); }); }); diff --git a/developer/src/kmc/test/test-finl.ts b/developer/src/kmc/test/test-finl.ts index d883292f5e..a50cbdad1a 100644 --- a/developer/src/kmc/test/test-finl.ts +++ b/developer/src/kmc/test/test-finl.ts @@ -15,11 +15,11 @@ describe('finl', function () { assert.lengthOf(finl.items, 1); assert.lengthOf(finl.items[0].from, 2); - assert.strictEqual(finl.items[0].from[0].value, "x"); - assert.strictEqual(finl.items[0].from[1].value, "x"); + assert.strictEqual(finl.items[0].from[0].value.value, "x"); + assert.strictEqual(finl.items[0].from[1].value.value, "x"); assert.strictEqual(finl.items[0].flags, FinlItemFlags.error); assert.isEmpty(finl.items[0].before); - assert.strictEqual(finl.items[0].to, "x"); + assert.strictEqual(finl.items[0].to.value, "x"); }); }); diff --git a/developer/src/kmc/test/test-loca.ts b/developer/src/kmc/test/test-loca.ts index f149f43c3f..a099da082b 100644 --- a/developer/src/kmc/test/test-loca.ts +++ b/developer/src/kmc/test/test-loca.ts @@ -14,7 +14,7 @@ describe('loca', function () { assert.equal(callbacks.messages.length, 0); assert.equal(loca.locales.length, 1); - assert.equal(loca.locales[0].toLowerCase(), 'mt'); + assert.equal(loca.locales[0].value.toLowerCase(), 'mt'); }); it('should compile multiple locales', function() { @@ -30,11 +30,11 @@ describe('loca', function () { // Original is 6 locales, now five minimized in the results assert.equal(loca.locales.length, 5); - assert.equal(loca.locales[0], 'mt'); - assert.equal(loca.locales[1], 'fr'); // Original fr-FR - assert.equal(loca.locales[2], 'km'); // Original km-Khmr-kh - assert.equal(loca.locales[3], 'qq-Abcd-ZZ-x-foobar'); - assert.equal(loca.locales[4], 'en-fonipa'); + assert.equal(loca.locales[0].value, 'mt'); + assert.equal(loca.locales[1].value, 'fr'); // Original fr-FR + assert.equal(loca.locales[2].value, 'km'); // Original km-Khmr-kh + assert.equal(loca.locales[3].value, 'qq-Abcd-ZZ-x-foobar'); + assert.equal(loca.locales[4].value, 'en-fonipa'); }); it('should reject structurally invalid invalid locales', function() { diff --git a/developer/src/kmc/test/test-meta.ts b/developer/src/kmc/test/test-meta.ts index 140d9a39c5..da1ae53393 100644 --- a/developer/src/kmc/test/test-meta.ts +++ b/developer/src/kmc/test/test-meta.ts @@ -13,11 +13,11 @@ describe('meta', function () { let meta = loadSectionFixture(MetaCompiler, 'sections/meta/minimal.xml', callbacks) as Meta; assert.equal(callbacks.messages.length, 0); - assert.isUndefined(meta.author); // TODO-LDML: default author string "unknown"? - assert.equal(meta.conform, 'techpreview'); - assert.isUndefined(meta.layout); // TODO-LDML: assumed layout? - assert.isUndefined(meta.normalization); // TODO-LDML: assumed normalization? - assert.isUndefined(meta.indicator); // TODO-LDML: synthesize an indicator? + assert.isEmpty(meta.author.value); // TODO-LDML: default author string "unknown"? + assert.equal(meta.conform.value, 'techpreview'); + assert.isEmpty(meta.layout.value); // TODO-LDML: assumed layout? + assert.isEmpty(meta.normalization.value); // TODO-LDML: assumed normalization? + assert.isEmpty(meta.indicator.value); // TODO-LDML: synthesize an indicator? assert.equal(meta.settings, KeyboardSettings.none); }); @@ -26,11 +26,11 @@ describe('meta', function () { let meta = loadSectionFixture(MetaCompiler, 'sections/meta/maximal.xml', callbacks) as Meta; assert.equal(callbacks.messages.length, 0); - assert.equal(meta.author, 'The Keyman Team'); - assert.equal(meta.conform, 'techpreview'); - assert.equal(meta.layout, 'QWIRKY'); - assert.equal(meta.normalization, 'NFC'); - assert.equal(meta.indicator, 'QW'); + assert.equal(meta.author.value, 'The Keyman Team'); + assert.equal(meta.conform.value, 'techpreview'); + assert.equal(meta.layout.value, 'QWIRKY'); + assert.equal(meta.normalization.value, 'NFC'); + assert.equal(meta.indicator.value, 'QW'); assert.equal(meta.settings, KeyboardSettings.fallback | KeyboardSettings.transformFailure | KeyboardSettings.transformPartial); }); diff --git a/developer/src/kmc/test/test-name.ts b/developer/src/kmc/test/test-name.ts index 65d5729975..edb4770baf 100644 --- a/developer/src/kmc/test/test-name.ts +++ b/developer/src/kmc/test/test-name.ts @@ -14,7 +14,7 @@ describe('name', function () { assert.equal(callbacks.messages.length, 0); assert.equal(name.names.length, 1); - assert.equal(name.names[0], 'My First Keyboard'); + assert.equal(name.names[0].value, 'My First Keyboard'); }); it('should compile multiple names', function() { @@ -23,11 +23,11 @@ describe('name', function () { assert.equal(callbacks.messages.length, 0); assert.equal(name.names.length, 5); - assert.equal(name.names[0], 'My Second Keyboard'); - assert.equal(name.names[1], 'My 2nd Keyboard'); - assert.equal(name.names[2], 'win:kbd2'); - assert.equal(name.names[3], 'mac:keybd_2'); - assert.equal(name.names[4], 'web:keyboard-2'); + assert.equal(name.names[0].value, 'My Second Keyboard'); + assert.equal(name.names[1].value, 'My 2nd Keyboard'); + assert.equal(name.names[2].value, 'win:kbd2'); + assert.equal(name.names[3].value, 'mac:keybd_2'); + assert.equal(name.names[4].value, 'web:keyboard-2'); }); //TODO-LDML: should we be linting on repeated names? diff --git a/developer/src/kmc/test/test-ordr.ts b/developer/src/kmc/test/test-ordr.ts index ffc71950fe..2cc8e4f7ca 100644 --- a/developer/src/kmc/test/test-ordr.ts +++ b/developer/src/kmc/test/test-ordr.ts @@ -15,10 +15,10 @@ describe('ordr', function () { assert.lengthOf(ordr.items, 1); assert.lengthOf(ordr.items[0].elements, 4); - assert.strictEqual(ordr.items[0].elements[0].value, "ខ"); - assert.strictEqual(ordr.items[0].elements[1].value, "ែ"); - assert.strictEqual(ordr.items[0].elements[2].value, "្"); - assert.strictEqual(ordr.items[0].elements[3].value, "ម"); + assert.strictEqual(ordr.items[0].elements[0].value.value, "ខ"); + assert.strictEqual(ordr.items[0].elements[1].value.value, "ែ"); + assert.strictEqual(ordr.items[0].elements[2].value.value, "្"); + assert.strictEqual(ordr.items[0].elements[3].value.value, "ម"); assert.strictEqual(ordr.items[0].elements[0].order, 1); assert.strictEqual(ordr.items[0].elements[1].order, 3); assert.strictEqual(ordr.items[0].elements[2].order, 4); diff --git a/developer/src/kmc/test/test-tran.ts b/developer/src/kmc/test/test-tran.ts index eb922e532b..966c4555ba 100644 --- a/developer/src/kmc/test/test-tran.ts +++ b/developer/src/kmc/test/test-tran.ts @@ -15,11 +15,11 @@ describe('tran', function () { assert.lengthOf(tran.items, 1); assert.lengthOf(tran.items[0].from, 2); - assert.strictEqual(tran.items[0].from[0].value, "x"); - assert.strictEqual(tran.items[0].from[1].value, "x"); + assert.strictEqual(tran.items[0].from[0].value.value, "x"); + assert.strictEqual(tran.items[0].from[1].value.value, "x"); assert.strictEqual(tran.items[0].flags, TranItemFlags.error); assert.isEmpty(tran.items[0].before); - assert.strictEqual(tran.items[0].to, "x"); + assert.strictEqual(tran.items[0].to.value, "x"); }); });