mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-05 23:27:40 +00:00
Merge pull request #7252 from keymanapp/chore/developer/7235-kmc-sort-meta-and-strs
refactor(developer): kmc - encapsulate strs and elem items for sort 🙀
This commit is contained in:
commit
ef86410562
29 changed files with 337 additions and 280 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<T extends TransformCompilerType, TranBase extends Tran,
|
|||
return null;
|
||||
}
|
||||
|
||||
private compileTransform(transform: LKTransform): TranItemBase {
|
||||
private compileTransform(sections: GlobalSections, transform: LKTransform): TranItemBase {
|
||||
let result = this.newTranItem();
|
||||
result.from = new ElementString(transform.from);
|
||||
result.to = transform.to;
|
||||
result.before = new ElementString(transform.before);
|
||||
result.from = sections.elem.allocElementString(sections.strs, transform.from);
|
||||
result.to = sections.strs.allocString(transform.to);
|
||||
result.before = sections.elem.allocElementString(sections.strs, transform.before);
|
||||
result.flags = transform.error == 'fail' ? TranItemFlags.error : TranItemFlags.none;
|
||||
return result;
|
||||
}
|
||||
|
||||
private compileTransforms(transforms: LKTransforms): TranBase {
|
||||
private compileTransforms(sections: GlobalSections, transforms: LKTransforms): TranBase {
|
||||
let result = this.newTran();
|
||||
|
||||
if(transforms?.transform) {
|
||||
for(let transform of transforms.transform) {
|
||||
result.items.push(this.compileTransform(transform));
|
||||
result.items.push(this.compileTransform(sections, transform));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public compile(): TranBase {
|
||||
public compile(sections: GlobalSections): TranBase {
|
||||
for(let t of this.keyboard.transforms) {
|
||||
if(t.type == this.type) {
|
||||
return this.compileTransforms(t);
|
||||
return this.compileTransforms(sections, t);
|
||||
}
|
||||
}
|
||||
return this.newTran();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
||||
import { Strs, StrsItem } from './kmx-plus';
|
||||
|
||||
export enum ElemElementFlags {
|
||||
none = 0,
|
||||
|
|
@ -9,7 +10,7 @@ export enum ElemElementFlags {
|
|||
;
|
||||
|
||||
export class ElemElement {
|
||||
value: string; // UnicodeSet or UCS32LE character
|
||||
value: StrsItem; // UnicodeSet or UCS32LE character
|
||||
order: number; // -128 to +127; used only by reorder element values
|
||||
tertiary: number; // -128 to +127; used only by reorder element values
|
||||
flags: ElemElementFlags;
|
||||
|
|
@ -23,7 +24,7 @@ export class ElemElement {
|
|||
;
|
||||
|
||||
export class ElementString extends Array<ElemElement> {
|
||||
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<ElemElement> {
|
|||
|
||||
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 |
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
96
developer/src/kmc/test/fixtures/basic.txt
vendored
96
developer/src/kmc/test/fixtures/basic.txt
vendored
|
|
@ -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)
|
||||
# <reorder before="ᩫ" from="᩠᩵ᩅ" order="10 55 10" />
|
||||
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'
|
||||
|
||||
# <reorder before="ᩫ" from="᩠᩵ᩅ" order="10 55 10" />
|
||||
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 # 'ថា'
|
||||
# <reorder before="ᩫ" from="᩠᩵ᩅ" order="10 55 10" />
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue