Merge pull request #8711 from keymanapp/feat/typescript-keymanweb-compiler

feat(developer): Rewrite of KeymanWeb compiler in Typescript
This commit is contained in:
Marc Durdin 2023-06-08 17:38:25 +10:00 • committed by GitHub
commit 169ba9a8ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 12565 additions and 19 deletions

View file

@ -78,7 +78,7 @@ fi
if builder_start_action test; then
copy_schemas
npm test
npm test -- "${builder_extra_params[@]}"
builder_finish_action success test
fi

View file

@ -17,12 +17,19 @@ export class TouchLayoutFileWriter {
* @param source TouchLayoutFile
* @returns Uint8Array, the .keyman-touch-layout file
*/
write(source: TouchLayoutFile): Uint8Array {
const output = JSON.stringify(source, null, this.options?.formatted ? 2 : undefined);
public write(source: TouchLayoutFile): Uint8Array {
const output = this.toJSONString(source);
const encoder = new TextEncoder();
return encoder.encode(output);
}
/**
* Gets the output as a JSON string
*/
public toJSONString(source: TouchLayoutFile): string {
return JSON.stringify(source, null, this.options?.formatted ? 2 : undefined);
}
/**
* Compiles the touch layout file into a KeymanWeb-compatible JSON-style
* object string. In the future, this may be optimized to remove unnecessary
@ -31,7 +38,11 @@ export class TouchLayoutFileWriter {
* @param source
* @returns string
*/
compile(source: TouchLayoutFile): string {
public compile(source: TouchLayoutFile): string {
return this.toJSONString(this.fixup(source));
}
public fixup(source: TouchLayoutFile): TouchLayoutFile {
// Deep copy the source
source = JSON.parse(JSON.stringify(source));
@ -40,13 +51,23 @@ export class TouchLayoutFileWriter {
const fixupKey = (key: TouchLayoutKey | TouchLayoutSubKey) => {
if(Object.hasOwn(key, 'pad')) (key.pad as any) = key.pad.toString();
if(Object.hasOwn(key, 'sp')) (key.sp as any) = key.sp.toString();
if(Object.hasOwn(key, 'sp')) {
if(key.sp == 0) {
delete key.sp;
}
else {
(key.sp as any) = key.sp.toString();
}
}
if(Object.hasOwn(key, 'width')) (key.width as any) = key.width.toString();
if(Object.hasOwn(key, 'text') && key.text === '') delete key.text;
};
const fixupPlatform = (platform: TouchLayoutPlatform) => {
for(let layer of platform.layer) {
for(let row of layer.row) {
// this matches the old spec for touch layout files
(row.id as any) = row.id.toString();
for(let key of row.key) {
fixupKey(key);
if(key.sk) {
@ -79,6 +100,6 @@ export class TouchLayoutFileWriter {
fixupPlatform(source.tablet);
}
return JSON.stringify(source, null, this.options?.formatted ? 2 : undefined);
return source;
}
};

View file

@ -1,4 +1,4 @@
import { KMXFile, BUILDER_COMP_KEYBOARD, KEYBOARD, STORE } from "./kmx.js";
import { KMXFile, BUILDER_COMP_KEYBOARD, KEYBOARD, STORE, GROUP, KEY } from "./kmx.js";
import * as r from 'restructure';
export class KmxFileReader {
@ -13,17 +13,60 @@ export class KmxFileReader {
return this.rString.fromBuffer(source.slice(offset));
}
private processSystemStore(store: STORE, result: KEYBOARD) {
switch(store.dwSystemID) {
case KMXFile.TSS_MNEMONIC:
result.isMnemonic = store.dpString == '1';
break;
case KMXFile.TSS_KEYBOARDVERSION:
result.keyboardVersion = store.dpString;
break;
case KMXFile.TSS_BEGIN_NEWCONTEXT:
if(store.dpString.length == 3 && store.dpString.charCodeAt(0) == 0xFFFF && store.dpString.charCodeAt(1) == KMXFile.CODE_USE) {
result.startGroup.newContext = store.dpString.charCodeAt(2) - 1;
}
else {
// TODO: error
return false;
}
break;
case KMXFile.TSS_BEGIN_POSTKEYSTROKE:
if(store.dpString.length == 3 && store.dpString.charCodeAt(0) == 0xFFFF && store.dpString.charCodeAt(1) == KMXFile.CODE_USE) {
result.startGroup.postKeystroke = store.dpString.charCodeAt(2) - 1;
}
else {
// TODO: error
return false;
}
break;
}
return true;
}
public read(source: Uint8Array): KEYBOARD {
let binaryKeyboard: BUILDER_COMP_KEYBOARD;
let kmx = new KMXFile();
binaryKeyboard = kmx.COMP_KEYBOARD.fromBuffer(source);
if(binaryKeyboard.dwIdentifier != KMXFile.FILEID_COMPILED) {
// TODO: error
return null;
}
// TODO: all header fields
let result = new KEYBOARD();
result.fileVersion = binaryKeyboard.dwFileVersion;
result.flags = binaryKeyboard.dwFlags;
result.hotkey = binaryKeyboard.dwHotKey;
result.startGroup = {
ansi: binaryKeyboard.StartGroup_ANSI == 0xFFFFFFFF ? -1 : binaryKeyboard.StartGroup_ANSI,
unicode: binaryKeyboard.StartGroup_Unicode == 0xFFFFFFFF ? -1 : binaryKeyboard.StartGroup_Unicode,
newContext: -1, //TODO
postKeystroke: -1 // TODO
}
// Informative data
result.keyboardVersion = '';
result.isMnemonic = false;
let offset = binaryKeyboard.dpStoreArray;
for(let i = 0; i < binaryKeyboard.cxStoreArray; i++) {
let binaryStore = kmx.COMP_STORE.fromBuffer(source.slice(offset));
@ -32,13 +75,52 @@ export class KmxFileReader {
store.dpName = this.readString(source, binaryStore.dpName);
store.dpString = this.readString(source, binaryStore.dpString);
result.stores.push(store);
if(!this.processSystemStore(store, result)) {
return null;
}
offset += KMXFile.COMP_STORE_SIZE;
}
// TODO: groups
offset = binaryKeyboard.dpGroupArray;
for(let i = 0; i < binaryKeyboard.cxGroupArray; i++) {
let binaryGroup = kmx.COMP_GROUP.fromBuffer(source.slice(offset));
let group = new GROUP();
group.dpMatch = this.readString(source, binaryGroup.dpMatch);
group.dpName = this.readString(source, binaryGroup.dpName);
group.dpNoMatch = this.readString(source, binaryGroup.dpNoMatch);
group.fUsingKeys = binaryGroup.fUsingKeys;
group.keys = [];
let keyOffset = binaryGroup.dpKeyArray;
for(let j = 0; j < binaryGroup.cxKeyArray; j++) {
let binaryKey = kmx.COMP_KEY.fromBuffer(source.slice(keyOffset));
let key = new KEY();
key.Key = binaryKey.Key;
key.Line = binaryKey.Line;
key.ShiftFlags = binaryKey.ShiftFlags;
key.dpContext = this.readString(source, binaryKey.dpContext);
key.dpOutput = this.readString(source, binaryKey.dpOutput);
group.keys.push(key);
keyOffset += KMXFile.COMP_KEY_SIZE;
}
result.groups.push(group);
offset += KMXFile.COMP_GROUP_SIZE;
}
// TODO: KMXPlusFile
// Validate startGroup offsets
let gp: keyof KEYBOARD['startGroup'];
for(gp in result.startGroup) {
if(result.startGroup[gp] < -1 || result.startGroup[gp] >= result.groups.length) {
// TODO: error
return null;
}
}
return result;
}
};

View file

@ -8,9 +8,28 @@ import * as r from 'restructure';
// kmx-builder will transform these to the corresponding COMP_xxxx
export class KEYBOARD {
//TODO: additional header fields
fileVersion?: number; // dwFileVersion (TSS_FILEVERSION)
startGroup: {
ansi: number; // from COMP_KEYBOARD
unicode: number; // from COMP_KEYBOARD
newContext: number; // from TSS_BEGIN_NEWCONTEXT store
postKeystroke: number; // from TSS_BEGIN_POSTKEYSTROKE store
} = {ansi:-1, unicode:-1, newContext:-1, postKeystroke:-1};
flags?: number;
hotkey?: number;
//bitmap:
groups: GROUP[] = [];
stores: STORE[] = [];
// Following values are extracted from stores[] but are
// informative only
keyboardVersion?: string; // version (TSS_KEYBOARDVERSION)
isMnemonic: boolean; // TSS_MNEMONICLAYOUT store
};
export class STORE {
@ -21,7 +40,7 @@ export class STORE {
export class GROUP {
dpName: string;
keys: KEY[];
keys: KEY[] = [];
dpMatch: string;
dpNoMatch: string;
fUsingKeys: boolean;
@ -319,6 +338,21 @@ export class KMXFile {
public static readonly ISVIRTUALKEY = 0x4000; // It is a Virtual Key Sequence
public static readonly VIRTUALCHARKEY = 0x8000; // Keyman 6.0: Virtual Key Cap Sequence NOT YET
public static readonly MASK_MODIFIER_CHIRAL = KMXFile.LCTRLFLAG | KMXFile.RCTRLFLAG | KMXFile.LALTFLAG | KMXFile.RALTFLAG;
public static readonly MASK_MODIFIER_SHIFT = KMXFile.K_SHIFTFLAG;
public static readonly MASK_MODIFIER_NONCHIRAL = KMXFile.K_CTRLFLAG | KMXFile.K_ALTFLAG;
public static readonly MASK_STATEKEY = KMXFile.CAPITALFLAG | KMXFile.NOTCAPITALFLAG |
KMXFile.NUMLOCKFLAG | KMXFile.NOTNUMLOCKFLAG |
KMXFile.SCROLLFLAG | KMXFile.NOTSCROLLFLAG;
public static readonly MASK_KEYTYPE = KMXFile.ISVIRTUALKEY | KMXFile.VIRTUALCHARKEY;
public static readonly MASK_MODIFIER = KMXFile.MASK_MODIFIER_CHIRAL | KMXFile.MASK_MODIFIER_SHIFT | KMXFile.MASK_MODIFIER_NONCHIRAL;
public static readonly MASK_KEYS = KMXFile.MASK_MODIFIER | KMXFile.MASK_STATEKEY;
public static readonly KMX_MASK_VALID = KMXFile.MASK_KEYS | KMXFile.MASK_KEYTYPE;
public static readonly K_MODIFIERFLAG = 0x007F;
public static readonly K_NOTMODIFIERFLAG = 0xFF00; // I4548
@ -328,12 +362,13 @@ export class KMXFile {
public static readonly COMP_GROUP_SIZE = 24;
public static readonly COMP_KEY_SIZE = 20;
public static readonly VERSION_MASK_MINOR = 0x00FF;
public static readonly VERSION_MASK_MAJOR = 0xFF00;
/* In-memory representation of the keyboard */
public keyboard: KEYBOARD = {
groups: [],
stores: []
};
public keyboard: KEYBOARD = new KEYBOARD();
constructor() {

View file

@ -26,6 +26,7 @@ export class VisualKeyboardFont {
name?: string;
size?: number;
color?: number; // unused
style?: string; // TODO: figure out style vs color issues
};
export { BUILDER_KVK_KEY_FLAGS as VisualKeyboardKeyFlags } from "./kvk-file.js";

View file

@ -29,6 +29,7 @@ builder_describe \
test \
":kmcmplib=src/kmcmplib Compiler - .kmn compiler" \
":kmc-kmn=src/kmc-kmn Compiler - .kmn wrapper Keyboard Module" \
":kmc-kmw=src/kmc-kmw Compiler - .kmn Javascript Keyboard Module" \
":kmc-ldml=src/kmc-ldml Compiler - LDML Keyboard Module" \
":kmc-model=src/kmc-model Compiler - Lexical Model Module" \
":kmc-model-info=src/kmc-model-info Compiler - .model_info Module" \

View file

@ -26,13 +26,15 @@ export interface CompilerOptions {
saveDebug?: boolean;
compilerWarningsAsErrors?: boolean;
warnDeprecatedCode?: boolean;
target?: 'kmx' | 'js';
};
const baseOptions: CompilerOptions = {
shouldAddCompilerVersion: true,
saveDebug: true,
compilerWarningsAsErrors: false,
warnDeprecatedCode: true
warnDeprecatedCode: true,
target: 'kmx'
};
/**
@ -66,6 +68,7 @@ export class KmnCompiler {
return false;
}
}
return this.verifyInitialized();
}

View file

@ -0,0 +1,39 @@
#
# Keyman Developer - kmc KMW Keyboard Compiler Makefile
#
!include ..\Defines.mak
# We do configure here because parent Makefile calls this first; other
# kmc and kmc-* makefiles don't do it
build: configure .virtual
$(GIT_BASH_FOR_KEYMAN) build.sh build
configure: .virtual
$(GIT_BASH_FOR_KEYMAN) build.sh configure
clean: .virtual
$(GIT_BASH_FOR_KEYMAN) build.sh clean
test: .virtual
$(GIT_BASH_FOR_KEYMAN) build.sh test
# build.sh bundle must be run from shell as it requires a temp folder to be
# passed in. See inst/download.in.mak for instantiation.
publish: .virtual
$(GIT_BASH_FOR_KEYMAN) build.sh publish
signcode:
@rem nothing to do
wrap-symbols:
@rem nothing to do
test-manifest:
@rem nothing to do
install:
@rem nothing to do
!include ..\Target.mak

68
developer/src/kmc-kmw/build.sh Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/env bash
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
. "${THIS_SCRIPT%/*}/../../../resources/build/build-utils.sh"
## END STANDARD BUILD SCRIPT INCLUDE
cd "$THIS_SCRIPT_PATH"
. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh"
. "$KEYMAN_ROOT/resources/build/build-utils-ci.inc.sh"
builder_describe "Build Keyman kmc KMW Keyboard Compiler module" \
"@/common/web/types" \
"@/developer/src/kmc-kmn" \
"configure" \
"build" \
"clean" \
"test" \
"pack build a local .tgz pack for testing" \
"publish publish to npm" \
"--dry-run,-n don't actually publish, just dry run"
builder_describe_outputs \
configure /node_modules \
build build/src/main.js
builder_parse "$@"
#-------------------------------------------------------------------------------------------------------------------
function copy_schema() {
# We need the schema file at runtime and bundled, so always copy it for all
# actions except `clean` and `configure`
mkdir -p "$THIS_SCRIPT_PATH/build/src/"
cp "$KEYMAN_ROOT/common/schemas/keyman-touch-layout/keyman-touch-layout.spec.json" "$THIS_SCRIPT_PATH/build/src/"
cp "$KEYMAN_ROOT/common/schemas/kvks/kvks.schema.json" "$THIS_SCRIPT_PATH/build/src/"
}
function do_build() {
copy_schema
npm run build
}
function do_test() {
# TODO: add c8 for coverage
copy_schema
npm test
}
function do_publish() {
copy_schema
builder_publish_to_npm
}
function do_pack() {
copy_schema
builder_publish_to_pack
}
#-------------------------------------------------------------------------------------------------------------------
builder_run_action clean rm -rf ./build/ ./tsconfig.tsbuildinfo
builder_run_action configure verify_npm_setup
builder_run_action build do_build
builder_run_action test do_test
builder_run_action pack do_pack
builder_run_action publish do_publish

View file

@ -0,0 +1,56 @@
{
"name": "@keymanapp/kmc-kmw",
"description": "Keyman Developer KMW keyboard compiler",
"keywords": [
"keyboard",
"keyman",
"ldml",
"unicode"
],
"type": "module",
"exports": {
".": "./build/src/main.js"
},
"scripts": {
"build": "tsc -b",
"tsc": "tsc",
"test": "cd test && tsc -b && cd .. && mocha",
"prepublishOnly": "npm run build"
},
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
"license": "MIT",
"bugs": {
"url": "https://github.com/keymanapp/keyman/issues"
},
"dependencies": {
"@keymanapp/common-types": "*",
"@keymanapp/kmc-kmn": "*",
"ajv": "^8.11.0",
"restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50",
"semver": "^7.3.7",
"xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.6",
"@types/semver": "^7.3.12",
"@types/xml2js": "^0.4.5",
"c8": "^7.12.0",
"chai": "^4.3.4",
"chalk": "^2.4.2",
"mocha": "^8.4.0",
"ts-node": "^9.1.1",
"typescript": "^4.9.5"
},
"mocha": {
"spec": "build/test/**/test-*.js",
"require": [
"source-map-support/register"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/keymanapp/keyman.git"
}
}

View file

@ -0,0 +1,14 @@
export interface CompilerEvent {
line: number;
code: number;
message: string;
};
export default interface CompilerCallbacks {
loadFile(baseFilename: string, filename: string): Buffer;
loadLdmlKeyboardSchema(): Buffer;
loadKvksJsonSchema(): Buffer;
reportMessage(event: CompilerEvent): void;
};
// TODO: eliminate this

View file

@ -0,0 +1,30 @@
import { KMX, CompilerCallbacks } from "@keymanapp/common-types";
import CompilerOptions from "./compiler-options.js";
export let FTabStop: string;
export let nl: string;
export let FCompilerWarningsAsErrors = false;
export let fk: KMX.KEYBOARD;
export let FMnemonic: boolean;
export let options: CompilerOptions;
export let callbacks: CompilerCallbacks;
export function setupGlobals(_callbacks: CompilerCallbacks, _options: CompilerOptions, _tab: string, _nl: string, _keyboard: KMX.KEYBOARD) {
callbacks = _callbacks;
options = _options;
FTabStop = _tab;
nl = _nl;
fk = _keyboard;
}
export function IsKeyboardVersion10OrLater(): boolean {
return fk.fileVersion >= KMX.KMXFile.VERSION_100;
}
export function IsKeyboardVersion14OrLater(): boolean {
return fk.fileVersion >= KMX.KMXFile.VERSION_140;
}
export function IsKeyboardVersion15OrLater(): boolean {
return fk.fileVersion >= KMX.KMXFile.VERSION_150;
}

View file

@ -0,0 +1,15 @@
export default interface CompilerOptions {
/**
* Add debug information to the .kmx file when compiling
*/
debug: boolean;
/**
* Add metadata about the compiler version to .kmx file when compiling
*/
addCompilerVersion: boolean;
};
// TODO: we have a shared CompilerOptions intf, eliminate this

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,271 @@
export const
CKeymanWebKeyCodes: number[] = [
0xFF, // L"K_?00", // &H0
0xFF, // L"K_LBUTTON", // &H1
0xFF, // L"K_RBUTTON", // &H2
0xFF, // L"K_CANCEL", // &H3
0xFF, // L"K_MBUTTON", // &H4
0xFF, // L"K_?05", // &H5
0xFF, // L"K_?06", // &H6
0xFF, // L"K_?07", // &H7
0xFF, // L"K_BKSP", // &H8
0xFF, // L"K_TAB", // &H9
0xFF, // L"K_?0A", // &HA
0xFF, // L"K_?0B", // &HB
0xFF, // L"K_KP5", // &HC
0xFF, // L"K_ENTER", // &HD
0xFF, // L"K_?0E", // &HE
0xFF, // L"K_?0F", // &HF
0xFF, // L"K_SHIFT", // &H10
0xFF, // L"K_CONTROL", // &H11
0xFF, // L"K_ALT", // &H12
0xFF, // L"K_PAUSE", // &H13
0xFF, // L"K_CAPS", // &H14
0xFF, // L"K_KANJI?15", // &H15
0xFF, // L"K_KANJI?16", // &H16
0xFF, // L"K_KANJI?17", // &H17
0xFF, // L"K_KANJI?18", // &H18
0xFF, // L"K_KANJI?19", // &H19
0xFF, // L"K_?1A", // &H1A
0xFF, // L"K_ESC", // &H1B
0xFF, // L"K_KANJI?1C", // &H1C
0xFF, // L"K_KANJI?1D", // &H1D
0xFF, // L"K_KANJI?1E", // &H1E
0xFF, // L"K_KANJI?1F", // &H1F
0x40, // L"K_SPACE", // &H20
0xFF, // L"K_PGUP", // &H21
0xFF, // L"K_PGDN", // &H22
0xFF, // L"K_END", // &H23
0xFF, // L"K_HOME", // &H24
0xFF, // L"K_LEFT", // &H25
0xFF, // L"K_UP", // &H26
0xFF, // L"K_RIGHT", // &H27
0xFF, // L"K_DOWN", // &H28
0xFF, // L"K_SEL", // &H29
0xFF, // L"K_PRINT", // &H2A
0xFF, // L"K_EXEC", // &H2B
0xFF, // L"K_PRTSCN", // &H2C
0xFF, // L"K_INS", // &H2D
0xFF, // L"K_DEL", // &H2E
0xFF, // L"K_HELP", // &H2F
0x0A, // L"K_0", // &H30
0x01, // L"K_1", // &H31
0x02, // L"K_2", // &H32
0x03, // L"K_3", // &H33
0x04, // L"K_4", // &H34
0x05, // L"K_5", // &H35
0x06, // L"K_6", // &H36
0x07, // L"K_7", // &H37
0x08, // L"K_8", // &H38
0x09, // L"K_9", // &H39
0xFF, // L"K_?3A", // &H3A
0xFF, // L"K_?3B", // &H3B
0xFF, // L"K_?3C", // &H3C
0xFF, // L"K_?3D", // &H3D
0xFF, // L"K_?3E", // &H3E
0xFF, // L"K_?3F", // &H3F
0xFF, // L"K_?40", // &H40
0x20, // L"K_A", // &H41
0x35, // L"K_B", // &H42
0x33, // L"K_C", // &H43
0x22, // L"K_D", // &H44
0x12, // L"K_E", // &H45
0x23, // L"K_F", // &H46
0x24, // L"K_G", // &H47
0x25, // L"K_H", // &H48
0x17, // L"K_I", // &H49
0x26, // L"K_J", // &H4A
0x27, // L"K_K", // &H4B
0x28, // L"K_L", // &H4C
0x37, // L"K_M", // &H4D
0x36, // L"K_N", // &H4E
0x18, // L"K_O", // &H4F
0x19, // L"K_P", // &H50
0x10, // L"K_Q", // &H51
0x13, // L"K_R", // &H52
0x21, // L"K_S", // &H53
0x14, // L"K_T", // &H54
0x16, // L"K_U", // &H55
0x34, // L"K_V", // &H56
0x11, // L"K_W", // &H57
0x32, // L"K_X", // &H58
0x15, // L"K_Y", // &H59
0x31, // L"K_Z", // &H5A
0xFF, // L"K_?5B", // &H5B
0xFF, // L"K_?5C", // &H5C
0xFF, // L"K_?5D", // &H5D
0xFF, // L"K_?5E", // &H5E
0xFF, // L"K_?5F", // &H5F
0xFF, // L"K_NP0", // &H60
0xFF, // L"K_NP1", // &H61
0xFF, // L"K_NP2", // &H62
0xFF, // L"K_NP3", // &H63
0xFF, // L"K_NP4", // &H64
0xFF, // L"K_NP5", // &H65
0xFF, // L"K_NP6", // &H66
0xFF, // L"K_NP7", // &H67
0xFF, // L"K_NP8", // &H68
0xFF, // L"K_NP9", // &H69
0xFF, // L"K_NPSTAR", // &H6A
0xFF, // L"K_NPPLUS", // &H6B
0xFF, // L"K_SEPARATOR", // &H6C
0xFF, // L"K_NPMINUS", // &H6D
0xFF, // L"K_NPDOT", // &H6E
0xFF, // L"K_NPSLASH", // &H6F
0xFF, // L"K_F1", // &H70
0xFF, // L"K_F2", // &H71
0xFF, // L"K_F3", // &H72
0xFF, // L"K_F4", // &H73
0xFF, // L"K_F5", // &H74
0xFF, // L"K_F6", // &H75
0xFF, // L"K_F7", // &H76
0xFF, // L"K_F8", // &H77
0xFF, // L"K_F9", // &H78
0xFF, // L"K_F10", // &H79
0xFF, // L"K_F11", // &H7A
0xFF, // L"K_F12", // &H7B
0xFF, // L"K_F13", // &H7C
0xFF, // L"K_F14", // &H7D
0xFF, // L"K_F15", // &H7E
0xFF, // L"K_F16", // &H7F
0xFF, // L"K_F17", // &H80
0xFF, // L"K_F18", // &H81
0xFF, // L"K_F19", // &H82
0xFF, // L"K_F20", // &H83
0xFF, // L"K_F21", // &H84
0xFF, // L"K_F22", // &H85
0xFF, // L"K_F23", // &H86
0xFF, // L"K_F24", // &H87
0xFF, // L"K_?88", // &H88
0xFF, // L"K_?89", // &H89
0xFF, // L"K_?8A", // &H8A
0xFF, // L"K_?8B", // &H8B
0xFF, // L"K_?8C", // &H8C
0xFF, // L"K_?8D", // &H8D
0xFF, // L"K_?8E", // &H8E
0xFF, // L"K_?8F", // &H8F
0xFF, // L"K_NUMLOCK", // &H90
0xFF, // L"K_SCROLL", // &H91
0xFF, // L"K_?92", // &H92
0xFF, // L"K_?93", // &H93
0xFF, // L"K_?94", // &H94
0xFF, // L"K_?95", // &H95
0xFF, // L"K_?96", // &H96
0xFF, // L"K_?97", // &H97
0xFF, // L"K_?98", // &H98
0xFF, // L"K_?99", // &H99
0xFF, // L"K_?9A", // &H9A
0xFF, // L"K_?9B", // &H9B
0xFF, // L"K_?9C", // &H9C
0xFF, // L"K_?9D", // &H9D
0xFF, // L"K_?9E", // &H9E
0xFF, // L"K_?9F", // &H9F
0xFF, // L"K_?A0", // &HA0
0xFF, // L"K_?A1", // &HA1
0xFF, // L"K_?A2", // &HA2
0xFF, // L"K_?A3", // &HA3
0xFF, // L"K_?A4", // &HA4
0xFF, // L"K_?A5", // &HA5
0xFF, // L"K_?A6", // &HA6
0xFF, // L"K_?A7", // &HA7
0xFF, // L"K_?A8", // &HA8
0xFF, // L"K_?A9", // &HA9
0xFF, // L"K_?AA", // &HAA
0xFF, // L"K_?AB", // &HAB
0xFF, // L"K_?AC", // &HAC
0xFF, // L"K_?AD", // &HAD
0xFF, // L"K_?AE", // &HAE
0xFF, // L"K_?AF", // &HAF
0xFF, // L"K_?B0", // &HB0
0xFF, // L"K_?B1", // &HB1
0xFF, // L"K_?B2", // &HB2
0xFF, // L"K_?B3", // &HB3
0xFF, // L"K_?B4", // &HB4
0xFF, // L"K_?B5", // &HB5
0xFF, // L"K_?B6", // &HB6
0xFF, // L"K_?B7", // &HB7
0xFF, // L"K_?B8", // &HB8
0xFF, // L"K_?B9", // &HB9
0x29, // L"K_COLON", // &HBA
0x0C, // L"K_EQUAL", // &HBB
0x38, // L"K_COMMA", // &HBC
0x0B, // L"K_HYPHEN", // &HBD
0x39, // L"K_PERIOD", // &HBE
0x3A, // L"K_SLASH", // &HBF
0x00, // L"K_BKQUOTE", // &HC0
0x00, // L"K_?C1", // &HC1
0x00, // L"K_?C2", // &HC2
0x00, // L"K_?C3", // &HC3
0x00, // L"K_?C4", // &HC4
0x00, // L"K_?C5", // &HC5
0x00, // L"K_?C6", // &HC6
0x00, // L"K_?C7", // &HC7
0x00, // L"K_?C8", // &HC8
0x00, // L"K_?C9", // &HC9
0x00, // L"K_?CA", // &HCA
0x00, // L"K_?CB", // &HCB
0x00, // L"K_?CC", // &HCC
0x00, // L"K_?CD", // &HCD
0x00, // L"K_?CE", // &HCE
0x00, // L"K_?CF", // &HCF
0x00, // L"K_?D0", // &HD0
0x00, // L"K_?D1", // &HD1
0x00, // L"K_?D2", // &HD2
0x00, // L"K_?D3", // &HD3
0x00, // L"K_?D4", // &HD4
0x00, // L"K_?D5", // &HD5
0x00, // L"K_?D6", // &HD6
0x00, // L"K_?D7", // &HD7
0x00, // L"K_?D8", // &HD8
0x00, // L"K_?D9", // &HD9
0x00, // L"K_?DA", // &HDA
0x1A, // L"K_LBRKT", // &HDB
0x1C, // L"K_BKSLASH", // &HDC
0x1B, // L"K_RBRKT", // &HDD
0x2A, // L"K_QUOTE", // &HDE
0x00, // L"K_oDF", // &HDF
0x00, // L"K_oE0", // &HE0
0x00, // L"K_oE1", // &HE1
0x30, // L"K_oE2", // &HE2
0x00, // L"K_oE3", // &HE3
0x00, // L"K_oE4", // &HE4
0x00, // L"K_?E5", // &HE5
0x00, // L"K_oE6", // &HE6
0x00, // L"K_?E7", // &HE7
0x00, // L"K_?E8", // &HE8
0x00, // L"K_oE9", // &HE9
0x00, // L"K_oEA", // &HEA
0x00, // L"K_oEB", // &HEB
0x00, // L"K_oEC", // &HEC
0x00, // L"K_oED", // &HED
0x00, // L"K_oEE", // &HEE
0x00, // L"K_oEF", // &HEF
0x00, // L"K_oF0", // &HF0
0x00, // L"K_oF1", // &HF1
0x00, // L"K_oF2", // &HF2
0x00, // L"K_oF3", // &HF3
0x00, // L"K_oF4", // &HF4
0x00, // L"K_oF5", // &HF5
0x00, // L"K_?F6", // &HF6
0x00, // L"K_?F7", // &HF7
0x00, // L"K_?F8", // &HF8
0x00, // L"K_?F9", // &HF9
0x00, // L"K_?FA", // &HFA
0x00, // L"K_?FB", // &HFB
0x00, // L"K_?FC", // &HFC
0x00, // L"K_?FD", // &HFD
0x00, // L"K_?FE", // &HFE
0x00 // L"K_?FF" // &HFF
];

View file

@ -0,0 +1,220 @@
import { callbacks, FCompilerWarningsAsErrors } from "./compiler-globals.js";
export let FError = false;
export function ReportError(line: number, msgcode: number, text: string) { // I1971
let flag = CERR_FLAG | CFATAL_FLAG;
if(FCompilerWarningsAsErrors) {
flag |= CWARN_FLAG;
}
if (msgcode & flag) {
FError = true;
}
// TODO: these Codes Are All Wrong But We'll Fix It Soon
callbacks.reportMessage({code: msgcode, message: text, line}); // I3310
}
export const
CHINT_FLAG = 0x1000,
CWARN_FLAG = 0x2000,
CERR_FLAG = 0x4000,
CFATAL_FLAG = 0x8000,
// Any messages from the lexical model compiler occupy this range:
CERR_LEXICAL_MODEL_MIN = 0x0800,
CERR_LEXICAL_MODEL_MAX = 0x08FF,
CERR_None = 0x0000,
CERR_EndOfFile = 0x0001,
CERR_BadCallParams = 0x8002,
CERR_CannotAllocateMemory = 0x9004,
CERR_InfileNotExist = 0x8005,
CERR_CannotCreateOutfile = 0x8006,
CERR_UnableToWriteFully = 0x8007,
CERR_CannotReadInfile = 0x8008,
CERR_SomewhereIGotItWrong = 0x8009,
CERR_InvalidToken = 0x400A,
CERR_InvalidBegin = 0x400B,
CERR_InvalidName = 0x400C,
CERR_InvalidVersion = 0x400D,
CERR_InvalidLanguageLine = 0x400E,
CERR_LayoutButNoLanguage = 0x400F,
CERR_InvalidLayoutLine = 0x4010,
CERR_NoVersionLine = 0x4011,
CERR_InvalidGroupLine = 0x4012,
CERR_InvalidStoreLine = 0x4013,
CERR_InvalidCodeInKeyPartOfRule = 0x4014,
CERR_InvalidDeadkey = 0x4015,
CERR_InvalidValue = 0x4016,
CERR_ZeroLengthString = 0x4017,
CERR_TooManyIndexToKeyRefs = 0x4018,
CERR_UnterminatedString = 0x4019,
CERR_StringInVirtualKeySection = 0x401A,
CERR_AnyInVirtualKeySection = 0x401B,
CERR_InvalidAny = 0x401C,
CERR_StoreDoesNotExist = 0x401D,
CERR_BeepInVirtualKeySection = 0x401E,
CERR_IndexInVirtualKeySection = 0x401F,
CERR_InvalidIndex = 0x4020,
CERR_OutsInVirtualKeySection = 0x4021,
CERR_InvalidOuts = 0x4022,
CERR_ContextInVirtualKeySection = 0x4024,
CERR_InvalidUse = 0x4025,
CERR_GroupDoesNotExist = 0x4026,
CERR_VirtualKeyNotAllowedHere = 0x4027,
CERR_InvalidSwitch = 0x4028,
CERR_NoTokensFound = 0x4029,
CERR_InvalidLineContinuation = 0x402A,
CERR_LineTooLong = 0x402B,
CERR_InvalidCopyright = 0x402C,
CERR_CodeInvalidInThisSection = 0x402D,
CERR_InvalidMessage = 0x402E,
CERR_InvalidLanguageName = 0x402F,
CERR_InvalidBitmapLine = 0x4030,
CERR_CannotReadBitmapFile = 0x4031,
CERR_IndexDoesNotPointToAny = 0x4032,
CERR_ReservedCharacter = 0x4033,
CERR_InvalidCharacter = 0x4034,
CERR_InvalidCall = 0x4035,
CERR_CallInVirtualKeySection = 0x4036,
CERR_CodeInvalidInKeyStore = 0x4037,
CERR_CannotLoadIncludeFile = 0x4038,
CERR_60FeatureOnly_EthnologueCode = 0x4039,
CERR_60FeatureOnly_MnemonicLayout = 0x403A,
CERR_60FeatureOnly_OldCharPosMatching = 0x403B,
CERR_60FeatureOnly_NamedCodes = 0x403C,
CERR_60FeatureOnly_Contextn = 0x403D,
CERR_501FeatureOnly_Call = 0x403E,
CERR_InvalidNamedCode = 0x403F,
CERR_InvalidSystemStore = 0x4040,
CERR_CallIsProfessionalFeature = 0x4041,
CERR_IncludeCodesIsProfessionalFeature = 0x4042,
CERR_MnemonicLayoutIsProfessionalFeature = 0x4043,
CERR_60FeatureOnly_VirtualCharKey = 0x4044,
CERR_VersionAlreadyIncluded = 0x4045,
CERR_70FeatureOnly = 0x4046,
CERR_80FeatureOnly = 0x4047,
CERR_InvalidInVirtualKeySection = 0x4048,
CERR_InvalidIf = 0x4049,
CERR_InvalidReset = 0x404A,
CERR_InvalidSet = 0x404B,
CERR_InvalidSave = 0x404C,
CERR_InvalidEthnologueCode = 0x404D,
CERR_CannotCreateTempfile = 0x804E,
CERR_90FeatureOnly_IfSystemStores = 0x404F,
CERR_IfSystemStore_NotFound = 0x4050,
CERR_90FeatureOnly_SetSystemStores = 0x4051,
CERR_SetSystemStore_NotFound = 0x4052,
CERR_90FeatureOnlyVirtualKeyDictionary = 0x4053,
CERR_NotSupportedInKeymanWebContext = 0x4054,
CERR_NotSupportedInKeymanWebOutput = 0x4055,
CERR_NotSupportedInKeymanWebStore = 0x4056,
CERR_VirtualCharacterKeysNotSupportedInKeymanWeb = 0x4057,
CERR_VirtualKeysNotValidForMnemonicLayouts = 0x4058,
CERR_InvalidTouchLayoutFile = 0x4059,
CERR_TouchLayoutInvalidIdentifier = 0x405A, // I4142
CERR_InvalidKeyCode = 0x405B, // I4142
CERR_90FeatureOnlyLayoutFile = 0x405C,
CERR_90FeatureOnlyKeyboardVersion = 0x405D,
CERR_KeyboardVersionFormatInvalid = 0x405E,
CERR_ContextExHasInvalidOffset = 0x405F,
CERR_90FeatureOnlyEmbedCSS = 0x4060,
CERR_90FeatureOnlyTargets = 0x4061,
CERR_ContextAndIndexInvalidInMatchNomatch = 0x4062,
CERR_140FeatureOnlyContextAndNotAnyWeb = 0x4063,
CERR_ExpansionMustFollowCharacterOrVKey = 0x4064,
CERR_VKeyExpansionMustBeFollowedByVKey = 0x4065,
CERR_CharacterExpansionMustBeFollowedByCharacter = 0x4066,
CERR_VKeyExpansionMustUseConsistentShift = 0x4067,
CERR_ExpansionMustBePositive = 0x4068,
CERR_CasedKeysMustContainOnlyVirtualKeys = 0x4069,
CERR_CasedKeysMustNotIncludeShiftStates = 0x406A,
CERR_CasedKeysNotSupportedWithMnemonicLayout = 0x406B,
CERR_CannotUseReadWriteGroupFromReadonlyGroup = 0x406C,
CERR_StatementNotPermittedInReadonlyGroup = 0x406D,
CERR_OutputInReadonlyGroup = 0x406E,
CERR_NewContextGroupMustBeReadonly = 0x406F,
CERR_PostKeystrokeGroupMustBeReadonly = 0x4070,
CERR_DuplicateGroup = 0x4071,
CERR_DuplicateStore = 0x4072,
CWARN_TooManyWarnings = 0x2080,
CWARN_OldVersion = 0x2081,
CWARN_BitmapNotUsed = 0x2082,
CWARN_CustomLanguagesNotSupported = 0x2083,
CWARN_KeyBadLength = 0x2084,
CWARN_IndexStoreShort = 0x2085,
CWARN_UnicodeInANSIGroup = 0x2086,
CWARN_ANSIInUnicodeGroup = 0x2087,
CWARN_UnicodeSurrogateUsed = 0x2088,
CWARN_ReservedCharacter = 0x2089,
CWARN_Info = 0x208A,
CWARN_VirtualKeyWithMnemonicLayout = 0x208B,
CWARN_VirtualCharKeyWithPositionalLayout = 0x208C,
CWARN_StoreAlreadyUsedAsOptionOrCall = 0x208D,
CWARN_StoreAlreadyUsedAsStoreOrCall = 0x208E,
CWARN_StoreAlreadyUsedAsStoreOrOption = 0x208F,
CWARN_PunctuationInEthnologueCode = 0x2090,
CWARN_TouchLayoutMissingLayer = 0x2091,
CWARN_TouchLayoutCustomKeyNotDefined = 0x2092,
CWARN_TouchLayoutMissingRequiredKeys = 0x2093,
CWARN_HelpFileMissing = 0x2094,
CWARN_EmbedJsFileMissing = 0x2095,
CWARN_TouchLayoutFileMissing = 0x2096,
CWARN_VisualKeyboardFileMissing = 0x2097,
CWARN_ExtendedShiftFlagsNotSupportedInKeymanWeb = 0x2098, // I4118
CWARN_TouchLayoutUnidentifiedKey = 0x2099, // I4142
CHINT_UnreachableKeyCode = 0x109A, // I4141
CWARN_CouldNotCopyJsonFile = 0x209B, // I4688
CWARN_PlatformNotInTargets = 0x209C,
CWARN_HeaderStatementIsDeprecated = 0x209D,
CWARN_UseNotLastStatementInRule = 0x209E,
CWARN_TouchLayoutFontShouldBeSameForAllPlatforms = 0x209F, // I4872
CWARN_InvalidJSONMetadataFile = 0x20A0, // I4872
CWARN_JSONMetadataOSKFontShouldMatchTouchFont = 0x20A1, // I4872
CWARN_DontMixChiralAndNonChiralModifiers = 0x20A3,
CWARN_MixingLeftAndRightModifiers = 0x20A4,
CWARN_LanguageHeadersDeprecatedInKeyman10 = 0x20A5,
CHINT_NonUnicodeFile = 0x10A6,
CWARN_TooManyErrorsOrWarnings = 0x20A7,
CWARN_HotkeyHasInvalidModifier = 0x20A8,
CWARN_TouchLayoutSpecialLabelOnNormalKey = 0x20A9,
CWARN_OptionStoreNameInvalid = 0x20AA,
CWARN_NulNotFirstStatementInContext = 0x20AB,
CWARN_IfShouldBeAtStartOfContext = 0x20AC,
CWARN_KeyShouldIncludeNCaps = 0x20AD,
CHINT_UnreachableRule = 0x10AE,
CHINT_FilenameHasDifferingCase = 0x10AF,
CWARN_MissingFile = 0x20B0;

View file

@ -0,0 +1,280 @@
import { KMX, TouchLayout, TouchLayoutFileReader, TouchLayoutFileWriter } from "@keymanapp/common-types";
import { callbacks, IsKeyboardVersion14OrLater, IsKeyboardVersion15OrLater } from "./compiler-globals.js";
import { JavaScript_Key, VKeyNames } from "./javascript-strings.js";
interface VLFOutput {
output: string;
result: boolean;
};
function IsValidUnicodeValue(ch: number): boolean { // I4198
return ((ch >= 0x0020) && (ch <= 0x007F)) ||
((ch >= 0x00A0) && (ch <= 0x10FFFF));
}
enum TKeyIdType { Key_Invalid, Key_Constant, Key_Touch, Key_Unicode, Key_Unicode_Multi }; // I4142
function GetKeyIdUnicodeType(value: string): TKeyIdType {
let values = value.split('_');
for(let v of values) {
if(!IsValidUnicodeValue(parseInt(v,16))) {
return TKeyIdType.Key_Invalid;
}
}
if(values.length > 1) {
return TKeyIdType.Key_Unicode_Multi;
}
return TKeyIdType.Key_Unicode;
}
function KeyIdType(FId: string): TKeyIdType { // I4142
FId = FId.toUpperCase();
switch(FId.charAt(0)) {
case 'T':
return TKeyIdType.Key_Touch;
case 'U':
if(FId.startsWith('U_')) {
return GetKeyIdUnicodeType(FId.substring(2));
}
default:
// Note: can't use indexOf because some VKeyNames are mixed case, e.g. K_oE2
if(VKeyNames.find(key => key.toUpperCase() == FId)) {
return TKeyIdType.Key_Constant;
}
}
return TKeyIdType.Key_Invalid;
}
enum TRequiredKey { K_LOPT, K_BKSP, K_ENTER }; // I4447
const
CRequiredKeys: TRequiredKey[] = [TRequiredKey.K_LOPT, TRequiredKey.K_BKSP, TRequiredKey.K_ENTER]; // I4447
// See also builder.js: specialCharacters; web/source/osk/oskKey.ts: specialCharacters
const
CSpecialText10: string =
'*Shift*\0*Enter*\0*Tab*\0*BkSp*\0*Menu*\0*Hide*\0*Alt*\0*Ctrl*\0*Caps*\0'+
'*ABC*\0*abc*\0*123*\0*Symbol*\0*Currency*\0*Shifted*\0*AltGr*\0*TabLeft*',
// these names were added in Keyman 14
CSpecialText14: string =
'*LTREnter*\0*LTRBkSp*\0*RTLEnter*\0*RTLBkSp*\0*ShiftLock*\0*ShiftedLock*\0*ZWNJ*\0*ZWNJiOS*\0*ZWNJAndroid*',
CSpecialText14ZWNJ: string =
'*ZWNJ*\0*ZWNJiOS*\0*ZWNJAndroid*',
CSpecialText14Map: string[][] = [
['*LTREnter*', '*Enter*'],
['*LTRBkSp*', '*BkSp*'],
['*RTLEnter*', '*Enter*'],
['*RTLBkSp*', '*BkSp*'],
['*ShiftLock*', '*Shift*'],
['*ShiftedLock*', '*Shifted*'],
['*ZWNJ*', '<|>'],
['*ZWNJiOS*', '<|>'],
['*ZWNJAndroid*', '<|>']
];
// TODO lifecycle
function CheckKey(FPlatform: TouchLayout.TouchLayoutPlatform,
FId: string, FText: string, FNextLayer: string, FKeyType: TouchLayout.TouchLayoutKeySp,
FRequiredKeys: TRequiredKey[], FDictionary: string[]) { // I4119
//
// Check that each touch layer has K_LOPT, [K_ROPT,] K_BKSP, K_ENTER
//
for(let key of CRequiredKeys) {
if(TRequiredKey[key].toLowerCase() == FId.toLowerCase()) {
FRequiredKeys.push(key);
break;
}
}
//
// Check that each layer referenced exists
//
if(typeof FNextLayer == 'string' && FNextLayer.length > 0) {
if(FPlatform.layer.find(l => l.id.toLowerCase() == FNextLayer.toLowerCase()) == undefined) {
// TODO: callbacks.reportMessage() ReportError(0, CWARN_TouchLayoutMissingLayer, 'Key "'+FId+'" on platform "'+FPlatform.Name+'", layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", references a missing layer "'+FNextLayer+'".');
}
}
//
// Check that the key has a valid id // I4142
//
if(FId.trim() == '') {
if(!(FKeyType in [TouchLayout.TouchLayoutKeySp.blank, TouchLayout.TouchLayoutKeySp.spacer]) && FNextLayer == '') {
// TODO: ReportError(0, CWARN_TouchLayoutUnidentifiedKey, 'A key on layer "'+FLayer.Id+'" has no identifier.');
}
return;
}
let FValid = KeyIdType(FId);
if(FValid == TKeyIdType.Key_Invalid) {
// TODO: ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has an invalid identifier.');
}
else if (FValid == TKeyIdType.Key_Unicode_Multi && !IsKeyboardVersion15OrLater()) {
// TODO: ReportError(0, CERR_TouchLayoutInvalidIdentifier, 'Key "'+FId+'" on "'+FPlatform.Name+'", layer "'+FLayer.Id+'" has a multi-part identifier which requires version 15.0 or newer.');
}
//
// Check that each custom key code has at least *a* rule associated with it
//
if (FValid == TKeyIdType.Key_Touch && FNextLayer == '' && FKeyType in [TouchLayout.TouchLayoutKeySp.normal, TouchLayout.TouchLayoutKeySp.deadkey]) {
// Search for the key in the key dictionary - ignore K_LOPT, K_ROPT...
if(FDictionary.indexOf(FId) < 0) {
// TODO: ReportError(0, CWARN_TouchLayoutCustomKeyNotDefined, 'Key "'+FId+'" on layer "'+FLayer.Id+'", platform "'+FPlatform.Name+'", is a custom key but has no corresponding rule in the source.');
}
}
//
// Check that if the key has a *special* label, it is available in the target version
//
if(FText.startsWith('*') && FText.endsWith('*') && FText.length > 2) {
// Keyman versions before 14 do not support '*special*' labels on non-special keys.
// ZWNJ use, however, is safe because it will be transformed in function
// TransformSpecialKeys14 to '<|>', which does not require the custom OSK font.
if((CSpecialText10.includes(FText) || CSpecialText14.includes(FText)) &&
!CSpecialText14ZWNJ.includes(FText) &&
!IsKeyboardVersion14OrLater() &&
!(FKeyType in [TouchLayout.TouchLayoutKeySp.special, TouchLayout.TouchLayoutKeySp.specialActive])) {
// TODO: ReportError(0, CWARN_TouchLayoutSpecialLabelOnNormalKey,
// Format('Key "%s" on layout "%s", platform "%s" does not have the key type "Special" or "Special (active)" but has the label "%s". This feature is only supported in Keyman 14 or later', [
// FId, FLayer.Id, FPlatform.Name, FText
// ]));
}
}
}
function CheckDictionaryKeyValidity(fk: KMX.KEYBOARD, FDictionary: string[]) { // I4142
// TODO: O(eeek) performance here
for(let i = 0; i < FDictionary.length; i++) {
if(FDictionary[i] == '') {
continue;
}
if(KeyIdType(FDictionary[i]) in [TKeyIdType.Key_Invalid, TKeyIdType.Key_Constant]) {
for(let fgp of fk.groups) {
if(fgp.fUsingKeys) {
for(let fkp of fgp.keys) {
if(JavaScript_Key(fkp, fk.isMnemonic) == i+256) {
// TODO: ReportError(fkp.Line, CERR_InvalidKeyCode, 'Invalid key identifier "'+FDictionary[i]+'"');
}
}
}
}
}
}
}
function TransformSpecialKeys14(FDebug: boolean, sLayoutFile: string): string {
// Rewrite Special key labels that are only supported in Keyman 14+
// This code is a little ugly but effective.
if(!IsKeyboardVersion14OrLater()) {
for(let i = 0; i < CSpecialText14Map.length; i++) {
// Assumes the JSON output format will not change
if(FDebug) {
sLayoutFile = sLayoutFile.replace('"text": "'+CSpecialText14Map[i][0]+'"', '"text": this._v>13 ? "'+CSpecialText14Map[i][0]+'" : "'+CSpecialText14Map[i][1]+'"');
} else {
sLayoutFile = sLayoutFile.replace('"text":"'+CSpecialText14Map[i][0]+'"', '"text":this._v>13?"'+CSpecialText14Map[i][0]+'":"'+CSpecialText14Map[i][1]+'"');
}
}
}
return sLayoutFile;
}
export function ValidateLayoutFile(fk: KMX.KEYBOARD, FDebug: boolean, sLayoutFile: string, sVKDictionary: string): VLFOutput { // I4060 // I4139
/*
var
FPlatform: TTouchLayoutPlatform;
FLayer: TTouchLayoutLayer;
FRow: TTouchLayoutRow;
FKey: TTouchLayoutKey;
FSubKey: TTouchLayoutSubKey;
FRequiredKeys: set of TRequiredKey;
FDictionary: TStringList;
FDirection: TTouchLayoutFlickDirection;
*/
let FDictionary: string[] = sVKDictionary.split(/\s+/);
CheckDictionaryKeyValidity(fk, FDictionary); // I4142
let reader = new TouchLayoutFileReader();
let data = reader.read(callbacks.loadFile(sLayoutFile));
if(!data) {
// TODO: ReportError(0, CERR_InvalidTouchLayoutFile, sMsg);
return {output:null, result: false};
}
let FTouchLayoutFont = ''; // I4872
let pid: keyof TouchLayout.TouchLayoutFile;
for(pid in data) {
let platform = data[pid];
// Test that the font matches on all platforms // I4872
if(FTouchLayoutFont == '') {
FTouchLayoutFont = platform.font;
}
else if(platform.font.toLowerCase() != FTouchLayoutFont) {
// TODO: ReportError(0, CWARN_TouchLayoutFontShouldBeSameForAllPlatforms, 'The touch layout font should be the same for all platforms.');
// TODO: why support multiple font values if it has to be the same across all platforms?!
}
// Test that all required keys are present
for(let layer of platform.layer) {
let FRequiredKeys: TRequiredKey[] = [];
for(let row of layer.row) {
for(let key of row.key) {
CheckKey(platform, key.id, key.text, key.nextlayer, key.sp, FRequiredKeys, FDictionary); // I4119
if(key.sk) {
for(let subkey of key.sk) {
CheckKey(platform, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary);
}
}
let direction: keyof TouchLayout.TouchLayoutFlick;
if(key.flick) {
for(direction in key.flick) {
CheckKey(platform, key.flick[direction].id, key.flick[direction].text,
key.flick[direction].nextlayer, key.flick[direction].sp, FRequiredKeys, FDictionary);
}
}
if(key.multitap) {
for(let subkey of key.multitap) {
CheckKey(platform, subkey.id, subkey.text, subkey.nextlayer, subkey.sp, FRequiredKeys, FDictionary);
}
}
}
}
if(FRequiredKeys.length != CRequiredKeys.length) {
// TODO: ReportError(0, CWARN_TouchLayoutMissingRequiredKeys, 'Layer "'+FLayer.Id+'" on platform "'+FPlatform.Name+'" is missing the required key(s) '+RequiredKeysToString(CRequiredKeys-FRequiredKeys)+'.');
}
}
}
// If not debugging, then this strips out formatting for a big saving in file size
// This also normalises any values such as Pad or Width which should be strings
let writer = new TouchLayoutFileWriter({formatted: FDebug});
sLayoutFile = writer.compile(data);
sLayoutFile = TransformSpecialKeys14(FDebug, sLayoutFile);
return {
output: sLayoutFile,
result: true
}
}

View file

@ -0,0 +1,163 @@
import { KMX, KvkFile, VisualKeyboard } from "@keymanapp/common-types";
import { FTabStop, nl } from "./compiler-globals.js";
import { CKeymanWebKeyCodes } from "./keymanweb-key-codes.js";
import { RequotedString } from "./write-compiled-keyboard.js";
interface VKFFResult {
result: string;
displayUnderling: boolean;
}
export function VisualKeyboardFromFile(visualKeyboard: VisualKeyboard.VisualKeyboard, debug: boolean): VKFFResult {
let fbold = ''; // TODO 'bold ' if visualKeyboard.header.unicodeFont.style
let fitalic = ''; // TODO 'italic ' if visualKeyboard.header.unicodeFont.style
let f102 = visualKeyboard.header.flags & KvkFile.BUILDER_KVK_HEADER_FLAGS.kvkh102 ? '1' : '0';
let result = `{F:'${fbold}${fitalic} 1em "${RequotedString(visualKeyboard.header.unicodeFont.name)}"',K102:${f102}}` +
`;` + VisualKeyboardToKLS(visualKeyboard) +
';' + BuildBKFromKLS(debug);
return {
result: result,
// TODO: this can go in caller, later
displayUnderling: !!(visualKeyboard.header.flags & KvkFile.BUILDER_KVK_HEADER_FLAGS.kvkhDisplayUnderlying)
}
}
function WideQuote(s: string): string {
let result = '';
for(let i = 0; i < s.length; i++) {
if(s[i] == '"' || s[i] == '\\') {
result += '\\' + s[i];
} else {
result += s[i];
}
}
return result;
}
function VkShiftStateToKmxShiftState(ShiftState: number): number {
interface TVKToKMX {
VK: number; KMX: number;
}
const Map: TVKToKMX[] = [
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_SHIFT, KMX: KMX.KMXFile.K_SHIFTFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_CTRL, KMX: KMX.KMXFile.K_CTRLFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_ALT, KMX: KMX.KMXFile.K_ALTFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_LCTRL, KMX: KMX.KMXFile.LCTRLFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_RCTRL, KMX: KMX.KMXFile.RCTRLFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_LALT, KMX: KMX.KMXFile.LALTFLAG},
{VK: KvkFile.BUILDER_KVK_SHIFT_STATE.KVKS_RALT, KMX: KMX.KMXFile.RALTFLAG}
];
let result = 0;
for(let i = 0; i < Map.length; i++) {
if (ShiftState & Map[i].VK) {
result |= Map[i].KMX;
}
}
return result;
}
function VKShiftToLayerName(shift: number): string {
const masks: string[] = [
'leftctrl',
'rightctrl',
'leftalt',
'rightalt',
'shift',
'ctrl',
'alt'
];
shift = VkShiftStateToKmxShiftState(shift);
if(shift == 0) {
return 'default';
}
let result = '';
for(let i = 0; i < masks.length; i++) {
if(shift & (1 << i)) {
result += masks[i] + '-';
}
}
return result.substring(0, result.length - 1);
}
function VisualKeyboardToKLS(FVK: VisualKeyboard.VisualKeyboard): string {
interface TLayer {
shift: number;
name: string;
keys: string[];
};
let layers: TLayer[] = [];
// Discover the layers used in the visual keyboard
for(let key of FVK.keys) {
if(key.flags & KvkFile.BUILDER_KVK_KEY_FLAGS.kvkkUnicode) {
// Find the index of the key in KMW VK arrays
let n = CKeymanWebKeyCodes[key.vkey];
if(n == 0xFF) {
continue;
}
let layer = layers.find(layer => layer.shift == key.shift);
if(!layer) {
// 0-64 covers all possible VirtualKeyCodes in CKemyanWebKeyCodes
layer = { shift: key.shift, name: '', keys: new Array(65)};
layers.push(layer);
}
layer.keys[n] = key.text;
}
}
// Build the layer array
let result = nl+FTabStop+'this.KV.KLS={'+nl;
for(let i = 0; i < layers.length; i++) {
let layer = layers[i];
result += `${FTabStop}${FTabStop}"${VKShiftToLayerName(layer.shift)}": [`;
for(let j = 0; j < layer.keys.length - 1; j++) {
result += '"'+WideQuote(layer.keys[j] ?? '')+'",';
}
result += '"'+WideQuote(layer.keys[layer.keys.length-1] ?? '')+'"]';
if(i < layers.length - 1) {
result += ',' + nl;
}
}
result += nl+FTabStop+'}';
return result;
}
function BuildBKFromKLS(debug: boolean): string {
const func =
'function(x){var e=Array.apply(null,Array(65)).map(String.prototype.valueOf,"")'+
',r=[],v,i,m=[\'default\',\'shift\',\'ctrl\',\'shift-ctrl\',\'alt\',\'shift-alt\','+
'\'ctrl-alt\',\'shift-ctrl-alt\'];for(i=m.length-1;i>=0;i--)if((v=x[m[i]])||r.length)'+
'r=(v?v:e).slice().concat(r);return r}';
const func_debug =
'function(x){'+nl+
' var'+nl+
' empty=Array.apply(null, Array(65)).map(String.prototype.valueOf,""),'+nl+
' result=[], v, i,'+nl+
' modifiers=[\'default\',\'shift\',\'ctrl\',\'shift-ctrl\',\'alt\',\'shift-alt\',\'ctrl-alt\',\'shift-ctrl-alt\'];'+nl+
' for(i=modifiers.length-1;i>=0;i--) {'+nl+
' v = x[modifiers[i]];'+nl+
' if(v || result.length > 0) {'+nl+
' result=(v ? v : empty).slice().concat(result);'+nl+
' }'+nl+
' }'+nl+
' return result;'+nl+
' }';
return nl+FTabStop+'this.KV.BK=('+(debug ? func_debug : func)+')(this.KV.KLS)';
}

View file

@ -0,0 +1,665 @@
import { VisualKeyboard } from "@keymanapp/common-types";
import { KMX, CompilerCallbacks, KvkFileReader, KvksFileReader } from "@keymanapp/common-types";
import { ExpandSentinel, incxstr, xstrlen } from "../util/util.js";
// import { KEY, KEYBOARD, KMX.KMXFile, STORE } from "../../../../../common/web/types/src/kmx/kmx.js";
import { options, nl, FTabStop, setupGlobals, IsKeyboardVersion10OrLater } from "./compiler-globals.js";
import CompilerOptions from "./compiler-options.js";
import { JavaScript_ContextMatch, JavaScript_KeyAsString, JavaScript_Name, JavaScript_OutputString, JavaScript_Rules, JavaScript_Shift, JavaScript_ShiftAsString, JavaScript_Store, zeroPadHex } from './javascript-strings.js';
import { CERR_InvalidBegin, CWARN_DontMixChiralAndNonChiralModifiers, ReportError } from "./messages.js";
import { ValidateLayoutFile } from "./validate-layout-file.js";
import { VisualKeyboardFromFile } from "./visual-keyboard-compiler.js";
export let FFix183_LadderLength: number = 100; // TODO: option
function requote(s: string): string {
return "'" + s.replaceAll(/(['\\])/, "\\$1") + "'";
}
export function RequotedString(s: string, RequoteSingleQuotes: boolean = false): string {
// TODO: use a JSON encode
let i: number = 0;
while(i < s.length) {
if (s.charAt(i) == '"' || s.charAt(i) == '\\') {
s = s.substring(0, i) + '\\' + s.substring(i);
i++;
}
else if (s.charAt(i) == '\'' && RequoteSingleQuotes) {
s = s.substring(0, i) + '\\' + s.substring(i);
i++;
}
else if(s.charAt(i) == '\n') {
s = s.substring(0, i) + '\\n' + s.substring(i + 1);
}
else if(s.charAt(i) == '\r') {
s = s.substring(0, i) + ' ' + s.substring(i + 1);
}
i++;
}
return s;
}
export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: string, kmxfile: string, name: string, keyboard: KMX.KEYBOARD, FDebug: boolean = false): string {
let opts: CompilerOptions = {
addCompilerVersion: false,
debug: FDebug
};
setupGlobals(callbacks, opts, FDebug?' ':'', FDebug?'\r\n':'', keyboard);
// let fgp: GROUP;
// let fsp: STORE;
// let fkp: KEY;
// let j: number;
// let n: number;
let vMnemonic: number = 0;
let /*s: string,*/ sRTL: string = "", sHelp: string = "''", sHelpFile: string = "",
sEmbedJS: string = "", sEmbedCSS: string = "";
let sVisualKeyboard: string = "", sFullName: string = "";
let sBegin_NewContext: string = "", sBegin_PostKeystroke: string = "";
let sLayoutFile: string = "", sVKDictionary: string;
let linecomment: string; // I3438
// let HasRules: boolean;
let sModifierBitmask: string;
let FOptionStores: string;
let FKeyboardVersion = "1.0";
// let rec: TSentinelRecord;
let result = "";
// Locate the name of the keyboard
for(let i = 0; i < keyboard.stores.length; i++) {
const fsp = keyboard.stores[i];
if(fsp.dwSystemID == KMX.KMXFile.TSS_NAME) {
sFullName = fsp.dpString;
}
else if(fsp.dwSystemID == KMX.KMXFile.TSS_KEYBOARDVERSION) { // I4155
FKeyboardVersion = fsp.dpString;
}
else if (fsp.dpName == 'HelpFile' || fsp.dwSystemID == KMX.KMXFile.TSS_KMW_HELPFILE) {
sHelpFile = fsp.dpString;
}
else if (fsp.dpName == 'Help' || fsp.dwSystemID == KMX.KMXFile.TSS_KMW_HELPTEXT) {
sHelp = '"'+RequotedString(fsp.dpString)+'"';
}
else if (fsp.dpName == 'VisualKeyboard' || fsp.dwSystemID == KMX.KMXFile.TSS_VISUALKEYBOARD) {
sVisualKeyboard = fsp.dpString;
}
else if (fsp.dpName == 'EmbedJS' || fsp.dwSystemID == KMX.KMXFile.TSS_KMW_EMBEDJS) {
sEmbedJS = fsp.dpString;
}
else if (fsp.dpName == 'EmbedCSS' || fsp.dwSystemID == KMX.KMXFile.TSS_KMW_EMBEDCSS) { // I4368
sEmbedCSS = fsp.dpString;
}
else if (fsp.dpName == 'RTL' || fsp.dwSystemID == KMX.KMXFile.TSS_KMW_RTL) {
sRTL = fsp.dpString == '1' ? FTabStop+'this.KRTL=1;'+nl : ''; // I3681
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_MNEMONIC) {
vMnemonic = fsp.dpString == '1' ? 1 : 0;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_VKDICTIONARY) { // I3438
sVKDictionary = fsp.dpString;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_LAYOUTFILE) { // I3483
sLayoutFile = fsp.dpString;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_BEGIN_NEWCONTEXT) {
sBegin_NewContext = fsp.dpString;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_BEGIN_POSTKEYSTROKE) {
sBegin_PostKeystroke = fsp.dpString;
}
}
const sName = 'Keyboard_'+name; //TODO: verify --> GetKeymanWebCompiledNameFromFileName(FInFile);
if (sHelpFile != '') {
sHelp = '';
//TODO: load sHelpFile from file
/*with TStringList.Create do
try
try
LoadFromFile(ExtractFilePath(FInFile) + sHelpFile, TEncoding.UTF8); // I3337
for n := 0 to Count - 1 do
sHelp := sHelp + Strings[n] + ' ';
except
on E:EFOpenError do
begin
ReportError(0, CWARN_HelpFileMissing, E.Message); // I1971 // I4061
sHelp := '';
end;
end;
finally
Free;
end;*/
sHelp = requote(sHelp);
}
if (sEmbedJS != '') {
//TODO: load sEmbedJS from file
/*try
with TStringList.Create do
try
LoadFromFile(ExtractFilePath(FInFile) + sEmbedJS, TEncoding.UTF8); // I3337
sEmbedJS := Text;
finally
Free;
end;
except
on E:EFOpenError do // I3683
begin
ReportError(0, CWARN_EmbedJsFileMissing, E.Message); // I4061
sEmbedJS := '';
end;
end;*/
}
if (sEmbedCSS != '') { // I4368
//TODO: load sEmbedCSS from file
/*try
with TStringList.Create do
try
LoadFromFile(ExtractFilePath(FInFile) + sEmbedCSS, TEncoding.UTF8); // I3337
sEmbedCSS := Text;
finally
Free;
end;
except
on E:EFOpenError do // I3683
begin
ReportError(0, CWARN_EmbedJsFileMissing, E.Message); // I4061
sEmbedCSS := '';
end;
end;*/
}
if (sLayoutFile != '') { // I3483
let path = callbacks.resolveFilename(kmnfile, sLayoutFile);
let result = ValidateLayoutFile(keyboard, options.debug, path, sVKDictionary);
if(!result.result) {
sLayoutFile = '';
// TODO: error
// ReportError(0, CWARN_TouchLayoutFileInvalid, 'Touch layout file is not valid');
} else {
// TODO: reusing the same variable here is ugly
sLayoutFile = result.output;
}
}
// Default to hide underlying layout characters. This is overridden by touch
// layout platform.displayUnderlying property or, if that is not present for
// the given platform, by the OSK property.
let fDisplayUnderlying = false;
if (sVisualKeyboard != '') {
// TODO: stop reusing sVisualKeyboard for both filename and content
let path = callbacks.resolveFilename(kmnfile, sVisualKeyboard);
let kvk: VisualKeyboard.VisualKeyboard;
if(path.match(/\.kvks$/i)) {
let reader = new KvksFileReader();
let source = reader.read(callbacks.loadFile(path));
reader.validate(source, callbacks.loadSchema("kvks")); // TODO: handle exceptions
kvk = reader.transform(source);
// TODO: log errors
}
else {
// Note: very old keyboard sources may still have .kvk as an xml
// file, but we'll treat that as an error rather than silently
// falling back to KvksFileReader
let reader = new KvkFileReader();
kvk = reader.read(callbacks.loadFile(path));
}
let result = VisualKeyboardFromFile(kvk, options.debug);
if(!result.result) {
// TODO: error
sVisualKeyboard = 'null';
}
else {
sVisualKeyboard = result.result;
}
}
else {
sVisualKeyboard = 'null';
}
const fMnemonic = vMnemonic == 1;
sModifierBitmask = GetKeyboardModifierBitmask(keyboard, fMnemonic);
result +=
`${JavaScript_SetupProlog()}${nl}` +
`KeymanWeb.KR(new ${sName}());${nl}` +
`${JavaScript_SetupEpilog()}${nl}` +
`function ${sName}()${nl}` +
`{${nl}` +
`${FTabStop}${JavaScript_SetupDebug()}${nl}` +
// Following line caches the Keyman major version
`${FTabStop}this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9;${nl}` +
`${FTabStop}this.KI="${sName}";${nl}` +
`${FTabStop}this.KN="${RequotedString(sFullName)}";${nl}` +
`${FTabStop}this.KMINVER="${(keyboard.fileVersion & KMX.KMXFile.VERSION_MASK_MAJOR) >> 8}.${keyboard.fileVersion & KMX.KMXFile.VERSION_MASK_MINOR}";${nl}` +
`${FTabStop}this.KV=${sVisualKeyboard};${nl}` +
`${FTabStop}this.KDU=${fDisplayUnderlying?'1':'0'};${nl}` +
`${FTabStop}this.KH=${sHelp};${nl}` +
`${FTabStop}this.KM=${vMnemonic};${nl}` +
`${FTabStop}this.KBVER="${FKeyboardVersion}";${nl}` + // I4155
`${FTabStop}this.KMBM=${sModifierBitmask};${nl}` +
`${sRTL}`; // I3681
if (HasSupplementaryPlaneChars()) {
result += `${FTabStop}this.KS=1;${nl}`;
}
if (sVKDictionary != '') { // I3438
result += `${FTabStop}this.KVKD="${RequotedString(sVKDictionary)}";${nl}`; // I3681
}
if (sLayoutFile != '') { // I3483
result += `${FTabStop}this.KVKL=${sLayoutFile};${nl}`; // I3681
}
if (sEmbedCSS != '') { // I4368
result += `${FTabStop}this.KCSS="${RequotedString(sEmbedCSS)}";${nl}`;
}
function isDebugStore(fsp: KMX.STORE) {
return fsp.dwSystemID == KMX.KMXFile.TSS_DEBUG_LINE;
}
function isReservedStore(fsp: KMX.STORE) {
return fsp.dwSystemID != KMX.KMXFile.TSS_NONE;
}
function isOptionStore(fsp: KMX.STORE) {
// TODO: how do we determine this, see CheckStoreUsage()
return false;
}
// Write the stores out
FOptionStores = '';
for(let i = 0; i < keyboard.stores.length; i++) {
let fsp = keyboard.stores[i];
// I3438 - Save all system stores to the keyboard, for now // I3684
if (!isDebugStore(fsp)) { // and not (fsp.dwSystemID in [TSS_BITMAP, TSS_NAME, TSS_VERSION, TSS_CUSTOMKEYMANEDITION, TSS_CUSTOMKEYMANEDITIONNAME, TSS_KEYMANCOPYRIGHT]) then
if (fsp.dwSystemID == KMX.KMXFile.TSS_COMPARISON) {
result += `${FTabStop}this.s${JavaScript_Name(i, fsp.dpName)}=${JavaScript_Store(keyboard, 0/*fsp.line*/, fsp.dpString)};${nl}`;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_COMPILEDVERSION) {
result += `${FTabStop}this.KVER=${JavaScript_Store(keyboard, 0/*fsp.line*/, fsp.dpString)};${nl}`;
}
//else if fsp.dwSystemID = TSS_VKDICTIONARY then // I3438, required for vkdictionary
// Result := Result + Format('%sthis.s%s=%s;%s', [FTabStop, JavaScript_Name(i, fsp.szName), JavaScript_Store(fsp.line, fsp.dpString), nl])
else if (isOptionStore(fsp) && !isReservedStore(fsp)) {
result += `${FTabStop}this.s${JavaScript_Name(i,fsp.dpName)}=KeymanWeb.KLOAD(this.KI,"${JavaScript_Name(i,fsp.dpName,true)}",`+
`${JavaScript_Store(keyboard, 0/*fsp.line*/, fsp.dpString)});${nl}`;
if (FOptionStores != '') {
FOptionStores += ',';
}
FOptionStores += `'s${JavaScript_Name(i, fsp.dpName)}`;
}
else if (fsp.dwSystemID == KMX.KMXFile.TSS_NONE /* aka not fsp.fIsReserved */) {
result += `${FTabStop}this.s${JavaScript_Name(i, fsp.dpName)}=${JavaScript_Store(keyboard, 0/*fsp.line*/, fsp.dpString)};${nl}`; // I3681
}
}
}
result += `${FTabStop}this.KVS=[${FOptionStores}];${nl}`;
// Write the groups out
// I853 - begin unicode missing causes crash
if (keyboard.startGroup.unicode == 0xFFFFFFFF) {
ReportError(0, CERR_InvalidBegin, 'A "begin unicode" statement is required to compile a KeymanWeb keyboard');
return null;
}
result += WriteBeginStatement(keyboard, 'gs', keyboard.startGroup.unicode);
let rec = ExpandSentinel(keyboard, sBegin_NewContext, 0);
if(rec.Code == KMX.KMXFile.CODE_USE) {
result += WriteBeginStatement(keyboard, 'gn', rec.Use.GroupIndex);
}
rec = ExpandSentinel(keyboard, sBegin_PostKeystroke, 0);
if(rec.Code == KMX.KMXFile.CODE_USE) {
result += WriteBeginStatement(keyboard, 'gpk', rec.Use.GroupIndex);
}
let fgp = keyboard.groups[keyboard.startGroup.unicode];
result +=
`${FTabStop}this.gs=function(t,e) {${nl}` +
`${FTabStop+FTabStop}return this.g${JavaScript_Name(keyboard.startGroup.unicode, fgp.dpName)}(t,e);${nl}` +
`${FTabStop}};${nl}`; // I3681
for(let i = 0; i < keyboard.groups.length; i++) { // I1964
let fgp = keyboard.groups[i];
/*
Note on `r` and `m` variables in a group function:
`m` can have one of three values:
0: no rule from this group was matched
1: a rule from this group was matched and did not include a `use`
statement
2: a rule from this group matched and did include a `use` statement
(#5440)
`m` is only used within a rule group to control the firing of the
`match` and `nomatch` rules.
`r` can have one of two values:
0: no rule from the final group matched (even if a rule from an
higher-level group did)
1: a rule from the final group did match;
`r` serves as the rule group's return value and is forwarded
recursively, best serving as a flag for whether or not default
output for a key should be emitted (0 means yes, emit the
default character output for that key).
*/
result +=
`${FTabStop}this.g${JavaScript_Name(i, fgp.dpName)}=function(t,e) {${nl}` +
`${FTabStop+FTabStop}var k=KeymanWeb,r=${fgp.fUsingKeys?0:1},m=0;${nl}`; //I1959
// fkp := fgp.dpKeyArray;
let HasRules = false;
if (FFix183_LadderLength != 0) {
result += JavaScript_Rules(keyboard, fMnemonic, fgp);
}
else {
for (let j = 0; j < fgp.keys.length; j++) { // I1964
let fkp = fgp.keys[j];
if (!RuleIsExcludedByPlatform(keyboard, fkp)) {
result += FTabStop+FTabStop; // I3681
if (HasRules) {
result += 'else ';
}
HasRules = true;
if (fgp.fUsingKeys) {
result += `if(k.KKM(e,${JavaScript_ShiftAsString(fkp, fMnemonic)},${JavaScript_KeyAsString(fkp, fMnemonic)})`;
}
if (xstrlen(fkp.dpContext) > 0) {
result += fgp.fUsingKeys ? '&&' : 'if(';
result += JavaScript_ContextMatch(keyboard, fkp, fkp.dpContext);
}
else if (!fgp.fUsingKeys) {
result += 'if(1';
}
linecomment = (fkp.Line > 0 && FDebug) ? ` // Line ${fkp.Line}` : '';
result +=
`) {${linecomment}${nl}` +
FTabStop+FTabStop+FTabStop;
if (fgp.fUsingKeys) { // I1959
result += 'r=m=1;' + JavaScript_OutputString(keyboard, FTabStop + FTabStop + FTabStop, fkp, fkp.dpOutput, fgp); // I1959 // I3681
}
else {
result += 'm=1;' + JavaScript_OutputString(keyboard, FTabStop + FTabStop + FTabStop, fkp, fkp.dpOutput, fgp); // I1959 // I3681
}
result += `${nl}${FTabStop}${FTabStop}}${nl}`; // I3681
}
}
}
if(fgp.dpMatch) {
result +=
`${FTabStop+FTabStop}if(m==1) {${nl}`+
`${FTabStop+FTabStop}${JavaScript_OutputString(keyboard, FTabStop + FTabStop + FTabStop, null, fgp.dpMatch, fgp)}${nl}`+
`${FTabStop+FTabStop}}${nl}`;
}
if(fgp.dpNoMatch) {
if(fgp.fUsingKeys) { // I1382 - fixup m=1 to m=g()
result +=
`${FTabStop+FTabStop}if(!m&&k.KIK(e)) {${nl}`+
`${FTabStop+FTabStop+FTabStop}r=1;${JavaScript_OutputString(keyboard, FTabStop + FTabStop + FTabStop, null, fgp.dpNoMatch, fgp)}${nl}`+
`${FTabStop+FTabStop}}${nl}`; // I1959. part 2, I2224 // I3681
}
else {
result +=
`${FTabStop+FTabStop}if(!m) {${nl}`+
`${FTabStop+FTabStop+FTabStop}${JavaScript_OutputString(keyboard, FTabStop + FTabStop + FTabStop, null, fgp.dpNoMatch, fgp)}${nl}`+
`${FTabStop+FTabStop}}${nl}`;
}
}
result +=
`${FTabStop+FTabStop}return r;${nl}`+
`${FTabStop}};${nl}`;
}
/* TODO
for(let n = 0 to FCallFunctions.Count - 1 do
begin
s := ExtractFilePath(FInFile) + FCallFunctions[n] + '.call_js';
if FileExists(s) then
with TStringList.Create do
try
LoadFromFile(s, TEncoding.UTF8); // I3337
Result := Result + Format('%sthis.c%d=function(t,e){%s};%s', [FTabstop, n, Trim(Text), nl]); // I3681
finally
Free;
end
else
Result := Result + Format('%sthis.c%d=function(t,e){alert("call(%s) not defined");};%s', [FTabstop, n, FCallFunctions[n], nl]); // I3681
end;
*/
result += sEmbedJS + '}' + nl; // I3681
return result;
}
///
/// Determine the modifiers used in the target keyboard and return a bitmask
/// representing them, or an number value when not in debug mode
///
/// @return string of JavaScript code, e.g. 'modCodes.SHIFT | modCodes.CTRL /* 0x0030 */'
///
function GetKeyboardModifierBitmask(keyboard: KMX.KEYBOARD, fMnemonic: boolean): string {
let bitMask = 0;
for(let gp of keyboard.groups) {
if(gp.fUsingKeys) {
for(let kp of gp.keys) {
if(!RuleIsExcludedByPlatform(keyboard, kp)) {
bitMask |= JavaScript_Shift(kp, fMnemonic);
}
}
}
}
if ((bitMask & KMX.KMXFile.MASK_MODIFIER_CHIRAL) && (bitMask & KMX.KMXFile.MASK_MODIFIER_NONCHIRAL)) {
ReportError(0, CWARN_DontMixChiralAndNonChiralModifiers, 'This keyboard contains Ctrl,Alt and LCtrl,LAlt,RCtrl,RAlt sets of modifiers. Use only one or the other set for web target.');
}
if(options.debug) {
return FormatModifierAsBitflags(bitMask & KMX.KMXFile.MASK_KEYS); // Exclude KMX_ISVIRTUALKEY, KMX_VIRTUALCHARKEY
}
return '0x'+(bitMask & KMX.KMXFile.MASK_KEYS).toString(16).toUpperCase();
}
///
/// If debug mode, then returns Javascript code necessary for
/// accessing constants in the compiled keyboard
///
/// @return string of JavaScript code
///
function JavaScript_SetupDebug() {
if(IsKeyboardVersion10OrLater()) {
if(options.debug) {
return 'var modCodes = keyman.osk.modifierCodes;'+nl+
FTabStop+'var keyCodes = keyman.osk.keyCodes;'+nl;
}
}
return '';
}
function JavaScript_SetupProlog() {
if(IsKeyboardVersion10OrLater()) {
return 'if(typeof keyman === \'undefined\') {'+nl+
FTabStop+'console.log(\'Keyboard requires KeymanWeb 10.0 or later\');'+nl+
FTabStop+'if(typeof tavultesoft !== \'undefined\') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");'+nl+
'} else {';
}
return '';
}
function JavaScript_SetupEpilog() {
if(IsKeyboardVersion10OrLater()) {
return '}';
}
return '';
}
function HasSupplementaryPlaneChars() {
return false; // TODO
/* function StringHasSuppChars(p: PWideChar): Boolean;
begin
if not Assigned(p) then
Exit(False);
while p^ <> #0 do
begin
if Char.IsSurrogate(p, 0) then
Exit(True);
p := incxstr(p);
end;
Result := False;
end;
var
I: number;
fsp: PFILE_STORE;
fgp: PFILE_GROUP;
j: number;
fkp: PFILE_KEY;
begin
fsp := fk.dpStoreArray;
for i := 0 to number(fk.cxStoreArray) - 1 do
begin
if StringHasSuppChars(fsp.dpString) then
Exit(True);
Inc(fsp);
end;
fgp := fk.dpGroupArray;
for i := 0 to number(fk.cxGroupArray) - 1 do
begin
fkp := fgp.dpKeyArray;
for j := 0 to number(fgp.cxKeyArray) - 1 do
begin
if StringHasSuppChars(fkp.dpContext) or
StringHasSuppChars(fkp.dpOutput) then
Exit(True);
Inc(fkp);
end;
if StringHasSuppChars(fgp.dpMatch) or
StringHasSuppChars(fgp.dpNoMatch) then
Exit(True);
Inc(fgp);
end;
Result := False;
end;*/
}
export function RuleIsExcludedByPlatform(keyboard: KMX.KEYBOARD, fkp: KMX.KEY): boolean {
if(fkp.dpContext == null || fkp.dpContext == '') {
return false;
}
let x = 0;
while(x < fkp.dpContext.length) {
let rec = ExpandSentinel(keyboard, fkp.dpContext, x);
if(rec.IsSentinel &&
(rec.Code == KMX.KMXFile.CODE_IFSYSTEMSTORE) &&
(rec.IfSystemStore.dwSystemID == KMX.KMXFile.TSS_PLATFORM) &&
rec.IfSystemStore.Store.dpString.includes('native')) {
if(rec.IfSystemStore.Store.dpString.match(/windows|desktop|macosx|linux/)) {
return true;
}
}
x = incxstr(fkp.dpContext, x);
}
return false;
}
function WriteBeginStatement(keyboard: KMX.KEYBOARD, name: string, groupIndex: number): string {
const fgp = keyboard.groups[groupIndex];
return `${FTabStop}this.${name}=function(t,e) {${nl}`+
`${FTabStop+FTabStop}return this.g${JavaScript_Name(groupIndex, fgp.dpName)}(t,e);${nl}`+
`${FTabStop}};${nl}`;
}
/**
* Converts a modifier bit mask integer into its component bit flags
*
* @param FBitMask A KMX modifier bitmask value
*
* @return string of JavaScript code, e.g. 'modCodes.SHIFT | modCodes.CTRL /* 0x0030 ./'
**/
export function FormatModifierAsBitflags(FBitMask: number): string {
const mask: string[] = [
'LCTRL', // 0X0001
'RCTRL', // 0X0002
'LALT', // 0X0004
'RALT', // 0X0008
'SHIFT', // 0X0010
'CTRL', // 0X0020
'ALT', // 0X0040
'???', // Reserved
'CAPS', // 0X0100
'NO_CAPS', // 0X0200
'NUM_LOCK', // 0X0400
'NO_NUM_LOCK', // 0X0800
'SCROLL_LOCK', // 0X1000
'NO_SCROLL_LOCK', // 0X2000
'VIRTUAL_KEY' // 0X4000
];
let i: number;
let result = '';
//TODO: We need to think about mnemonic layouts which are incompletely supported at present
//tavultesoft.keymanweb.osk.
if(IsKeyboardVersion10OrLater()) {
// This depends on flags defined in KeymanWeb 10.0
result = '';
for(i = 0; i < mask.length; i++) {
if(FBitMask & (1 << i)) {
if(result != '') result += ' | ';
result += 'modCodes.'+mask[i];
}
}
if(result == '') {
result = '0';
}
result += ' /* 0x' + zeroPadHex(FBitMask, 4) + ' */';
}
else {
result = '0x'+zeroPadHex(FBitMask, 4);
}
return result;
}

View file

@ -0,0 +1,2 @@
export { WriteCompiledKeyboard } from './compiler/write-compiled-keyboard.js';

View file

@ -0,0 +1,293 @@
import { KMX } from "@keymanapp/common-types";
/**
* Verifies that value is an item in the enumeration.
*/
export function isValidEnumValue<T extends {[key: number]: string | number}>(enu: T, value: string) {
return (Object.values(enu) as string[]).includes(value);
}
export interface TSentinelRecord {
IsSentinel: boolean;
Code?: number;
Any?: {
StoreIndex: number;
Store: KMX.STORE;
};
Index?: {
StoreIndex: number;
Store: KMX.STORE;
Index: number;
};
DeadKey?: {
DeadKey: number;
};
Use?: {
GroupIndex: number;
Group: KMX.GROUP;
};
Call?: {
StoreIndex: number;
Store: KMX.STORE;
};
ContextEx?: {
Index: number;
};
IfOpt?: {
StoreIndex1: number;
Store1: KMX.STORE;
StoreIndex2: number;
Store2: KMX.STORE;
IsNot: number;
};
IfSystemStore?: {
dwSystemID: number;
SystemStore: KMX.STORE;
StoreIndex: number;
Store: KMX.STORE;
IsNot: number;
};
SetOpt?: {
StoreIndex1: number;
Store1: KMX.STORE;
StoreIndex2: number;
Store2: KMX.STORE;
};
SetSystemStore?: {
dwSystemID: number;
SystemStore: KMX.STORE;
StoreIndex: number;
Store: KMX.STORE;
};
ResetOpt?: {
StoreIndex: number;
Store: KMX.STORE;
};
SaveOpt?: {
StoreIndex: number;
Store: KMX.STORE;
};
ChrVal?: number;
};
export function ExpandSentinel(fk: KMX.KEYBOARD, pwsz: string, x: number): TSentinelRecord {
let result: TSentinelRecord = {
IsSentinel: false
};
if(pwsz.charCodeAt(x) == KMX.KMXFile.UC_SENTINEL) {
result.IsSentinel = true;
x++;
result.Code = pwsz.charCodeAt(x);
x++;
switch(result.Code) {
case KMX.KMXFile.CODE_ANY:
case KMX.KMXFile.CODE_NOTANY: // I3981
let anyIdx = pwsz.charCodeAt(x) - 1;
result.Any = {
StoreIndex: anyIdx,
Store: fk.stores[anyIdx]
}
break;
case KMX.KMXFile.CODE_INDEX:
let indexIdx = pwsz.charCodeAt(x) - 1;
x++;
result.Index = {
StoreIndex: indexIdx,
Store: fk.stores[indexIdx],
Index: pwsz.charCodeAt(x) - 1
}
break;
case KMX.KMXFile.CODE_DEADKEY:
result.DeadKey = { DeadKey: pwsz.charCodeAt(x) - 1 };
break;
case KMX.KMXFile.CODE_USE:
let useIdx = pwsz.charCodeAt(x) - 1;
result.Use = {
GroupIndex: useIdx,
Group: fk.groups[useIdx]
};
break;
case KMX.KMXFile.CODE_CALL:
let callIdx = pwsz.charCodeAt(x) - 1;
result.Call = {
StoreIndex: callIdx,
Store: fk.stores[callIdx]
}
break;
case KMX.KMXFile.CODE_CONTEXTEX:
result.ContextEx = { Index: pwsz.charCodeAt(x) - 1 };
break;
case KMX.KMXFile.CODE_SETOPT: // I3429
let setIdx1 = pwsz.charCodeAt(x) - 1;
x++;
let setIdx2 = pwsz.charCodeAt(x) - 1;
result.SetOpt = {
StoreIndex1: setIdx1,
Store1: fk.stores[setIdx1],
StoreIndex2: setIdx2,
Store2: fk.stores[setIdx2]
};
break;
case KMX.KMXFile.CODE_SETSYSTEMSTORE: // I3437
let setsIdx1 = pwsz.charCodeAt(x) - 1;
x++;
let setsIdx2 = pwsz.charCodeAt(x) - 1;
result.SetSystemStore = {
dwSystemID: setsIdx1,
SystemStore: fk.stores.find(s => s.dwSystemID == setsIdx1) || null,
StoreIndex: setsIdx2,
Store: fk.stores[setsIdx2]
};
break;
case KMX.KMXFile.CODE_RESETOPT: // I3429
result.ResetOpt = {
StoreIndex: pwsz.charCodeAt(x) - 1,
Store: fk.stores[result.ResetOpt.StoreIndex]
};
break;
case KMX.KMXFile.CODE_SAVEOPT: // I3429
result.SaveOpt = {
StoreIndex: pwsz.charCodeAt(x) - 1,
Store: fk.stores[result.SaveOpt.StoreIndex]
};
break;
case KMX.KMXFile.CODE_IFOPT: // I3429
let ifIdx1 = pwsz.charCodeAt(x) - 1;
x++;
let ifNot = pwsz.charCodeAt(x) - 1;
x++;
let ifIdx2 = pwsz.charCodeAt(x) - 1;
result.IfOpt = {
StoreIndex1: ifIdx1,
Store1: fk.stores[ifIdx1],
IsNot: ifNot, // I3429
StoreIndex2: ifIdx2,
Store2: fk.stores[ifIdx2]
};
break;
case KMX.KMXFile.CODE_IFSYSTEMSTORE: // I3430
let ifsSystemID = pwsz.charCodeAt(x) - 1;
x++;
let ifsNot = pwsz.charCodeAt(x) - 1;
x++;
let ifsIdx2 = pwsz.charCodeAt(x) - 1;
result.IfSystemStore = {
dwSystemID: ifsSystemID,
SystemStore: fk.stores.find(s => s.dwSystemID == ifsSystemID) || null,
IsNot: ifsNot,
StoreIndex: ifsIdx2,
Store: fk.stores[ifsIdx2]
};
break;
case KMX.KMXFile.CODE_NUL:
case KMX.KMXFile.CODE_CONTEXT:
case KMX.KMXFile.CODE_RETURN:
case KMX.KMXFile.CODE_BEEP:
case KMX.KMXFile.CODE_EXTENDED:
// No additional detail needed, or outside scope
break;
default:
throw new Error(`Unrecognized system store value ${result.Code}`);
}
}
else {
result.ChrVal = GetSuppChar(pwsz, x);
}
return result;
}
export function GetSuppChar(p: string, x: number): number {
return p.codePointAt(x);
}
export function incxstr(p: string, x: number): number {
if(x >= p.length) {
return p.length;
}
let ch = p.charCodeAt(x);
if(ch != KMX.KMXFile.UC_SENTINEL) {
if(ch >= 0xD800 && ch <= 0xDBFF) {
x++;
if(x == p.length) {
return x;
}
ch = p.charCodeAt(x);
if (ch >= 0xDC00 && ch <= 0xDFFF) {
x++;
}
}
else {
x++;
}
return x;
}
x++;
if(x == p.length) {
return x;
}
switch(p.charCodeAt(x)) {
case KMX.KMXFile.CODE_ANY: x += 2; break;
case KMX.KMXFile.CODE_INDEX: x += 3; break;
case KMX.KMXFile.CODE_USE: x += 2; break;
case KMX.KMXFile.CODE_DEADKEY: x += 2; break;
case KMX.KMXFile.CODE_EXTENDED:
x += 3;
while(x <= p.length && p.charCodeAt(x) != KMX.KMXFile.UC_SENTINEL_EXTENDEDEND) {
x++;
}
x++;
break;
case KMX.KMXFile.CODE_CALL: x += 2; break;
case KMX.KMXFile.CODE_CONTEXTEX: x += 2; break;
case KMX.KMXFile.CODE_NOTANY: x += 2; break;
case KMX.KMXFile.CODE_CLEARCONTEXT: x += 2; break;
case KMX.KMXFile.CODE_IFOPT: x += 4; break;
case KMX.KMXFile.CODE_IFSYSTEMSTORE: x += 4; break;
case KMX.KMXFile.CODE_SETOPT: x += 3; break;
case KMX.KMXFile.CODE_SETSYSTEMSTORE: x += 3; break;
case KMX.KMXFile.CODE_RESETOPT: x += 2; break;
case KMX.KMXFile.CODE_SAVEOPT: x += 2; break;
default: x++;
}
if(x >= p.length) {
return p.length;
}
return x;
}
export function xstrlen(p: string): number {
let result = 0, x = 0;
while(x < p.length) {
x = incxstr(p, x);
result++;
}
return result;
}
export function xstrlen_printing(p: string): number {
let result = 0, x = 0;
while(x < p.length) {
if(x + 1 < p.length && p.charCodeAt(x) == KMX.KMXFile.UC_SENTINEL) {
switch(p.charCodeAt(x+1)) {
case KMX.KMXFile.CODE_DEADKEY:
case KMX.KMXFile.CODE_NUL:
case KMX.KMXFile.CODE_IFOPT:
case KMX.KMXFile.CODE_IFSYSTEMSTORE:
result--;
break;
}
}
x = incxstr(p, x);
result++;
}
return result;
}

View file

@ -0,0 +1,7 @@
Keyman kmn kmw Keyboard Compiler Tests
======================================
Test
----
../build.sh test

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,642 @@
store(&VERSION) '10.0'
store(&NAME) "Khmer Angkor"
store(&COPYRIGHT) '© 2015-2022 SIL International'
store(&MESSAGE) "More than just a Khmer Unicode keyboard."
store(&TARGETS) 'any'
store(&LAYOUTFILE) 'khmer_angkor.keyman-touch-layout'
store(&KEYBOARDVERSION) '1.3'
store(&BITMAP) 'khmer_angkor.ico'
store(&VISUALKEYBOARD) 'khmer_angkor.kvks'
begin Unicode > use(main)
c ==============================================STORES==============================================
c 33 consonants and two deprecated consonants
store(c_key) [K_K] [K_X] [SHIFT K_K] [SHIFT K_X] [K_G] \
[K_C] [K_Q] [SHIFT K_C] [SHIFT K_Q] [SHIFT K_J] \
[K_D] [K_Z] [SHIFT K_D] [SHIFT K_Z] [SHIFT K_N] \
[K_T] [K_F] [SHIFT K_T] [SHIFT K_F] [K_N] \
[K_B] [K_P] [SHIFT K_B] [SHIFT K_P] [K_M] \
[K_Y] [K_R] [K_L] [K_V] [K_S] [K_H] [SHIFT K_L] [SHIFT K_G] \
[RALT K_K] [RALT K_B]
store(c_out) U+1780 U+1781 U+1782 U+1783 U+1784 \
U+1785 U+1786 U+1787 U+1788 U+1789 \
U+178A U+178B U+178C U+178D U+178E \
U+178F U+1790 U+1791 U+1792 U+1793 \
U+1794 U+1795 U+1796 U+1797 U+1798 \
U+1799 U+179A U+179B U+179C U+179F U+17A0 U+17A1 U+17A2 \
U+179D U+179E c deprecated, but they are used in minority languages
c All genuine dependent vowels
store(v_gen_key) [K_A] [K_I] [SHIFT K_I] [K_W] [SHIFT K_W] [K_U] [SHIFT K_U] [SHIFT K_Y] \
[K_COLON] [SHIFT K_LBRKT] [K_LBRKT] [K_E] [SHIFT K_E] [SHIFT K_S] [K_O] [SHIFT K_O]
store(v_gen) U+17B6 U+17B7 U+17B8 U+17B9 U+17BA U+17BB U+17BC U+17BD \
U+17BE U+17BF U+17C0 U+17C1 U+17C2 U+17C3 U+17C4 U+17C5
c All pseudo dependent vowels
store(v_pseudo_key) [SHIFT K_M] [SHIFT K_H] [RALT K_QUOTE]
store(v_pseudo) U+17C6 U+17C7 U+17C8
c Both genuine and pseudo vowels
store(v_key) outs(v_gen_key) outs(v_pseudo_key)
store(v_out) outs(v_gen) outs(v_pseudo)
store(v_any) outs(v_out)
c These seven vowels can be concatenated with Reahmuk (U+17C7) to make េះ ោះ ុះ ិះ ីះ ឹះ and ែះ.
store(v_combo_R) U+17C1 U+17C4 U+17BB U+17B7 U+17B8 U+17B9 U+17C2
c these two vowels can be concatenated with Nikahit (U+17C6) to make ាំ and ុំ.
store(v_combo_N) U+17B6 U+17BB
store(v_combo) outs(v_combo_R) outs(v_combo_N)
c Independent vowels
store(ind_v_key) [K_HYPHEN] [RALT K_I] [SHIFT K_RBRKT] [RALT K_T] [RALT K_LBRKT] [K_RBRKT] [RALT K_R] [SHIFT K_R] \
[SHIFT K_BKSLASH] [K_BKSLASH] [RALT K_E] [RALT K_P] [RALT K_O] [K_EQUAL] [RALT K_RBRKT]
store(ind_v_out) U+17A5 U+17A6 U+17A7 U+17A8 U+17A9 U+17AA U+17AB U+17AC \
U+17AD U+17AE U+17AF U+17B0 U+17B1 U+17B2 U+17B3
c Diacritics
store(diacritic_key) [K_QUOTE] [SHIFT K_7] [SHIFT K_HYPHEN] [SHIFT K_8] [SHIFT K_6] [RALT K_QUOTE] \
[RALT K_EQUAL] [RALT K_3] [RALT K_W] [RALT K_Q] [K_J]
store(diacritic_out) U+17CB U+17D0 U+17CC U+17CF U+17CD U+17C8 \
U+17CE U+17D1 U+17DD U+17DC U+17D2
c Consonant shifter--Muusikatoan(U+17C9) and Triisap(U+17CA)
store(c_shifter_key) [SHIFT K_QUOTE] [K_SLASH]
store(c_shifter) U+17C9 U+17CA
c punctuations
store(punct_key) [K_PERIOD] [SHIFT K_PERIOD] [RALT K_COLON] [SHIFT K_2] [RALT K_L] [RALT K_6] [RALT K_7] [RALT K_M]
store(punct_out) U+17D4 U+17D5 U+17D6 U+17D7 U+17D8 U+17D9 U+17DA U+17D3
store(latin_punct_key) [K_BKQUOTE] [SHIFT K_BKQUOTE] [SHIFT K_9] [SHIFT K_0] [SHIFT K_1] [SHIFT K_3] \
[SHIFT K_5] [SHIFT K_EQUAL] [SHIFT K_SLASH] [RALT K_9] [RALT K_0] [RALT K_BKSLASH] \
[RALT K_2] [RALT K_8] [RALT K_COMMA] [RALT K_D] [RALT K_PERIOD] [RALT K_SLASH] \
[RALT K_Y] [RALT K_U] [RALT K_BKQUOTE] [RALT K_1] [RALT K_A] [RALT K_S] \
[RALT K_F] [RALT K_G] [RALT K_HYPHEN] [RALT K_H] [RALT K_J] [RALT K_N] \
[RALT K_Z] [RALT K_X] [RALT K_C] [RALT K_V]
store(latin_punct_out) U+00AB U+00BB U+0028 U+0029 U+0021 U+0022 \
U+0025 U+003D U+003F U+007B U+007D U+005C \
U+0040 U+002A U+002C U+00D7 U+002E U+002F \
U+005B U+005D U+200D U+200C U+002B U+002D \
U+00F7 U+003A U+2248 U+2018 U+2019 U+003B \
U+003C U+003E U+0023 U+0026
c white space, ZWSP, ZWJ
store(spaces_key) [K_SPACE] [SHIFT K_SPACE] [RALT K_SPACE]
store(spaces_out) U+200B U+0020 U+00A0
c Currency: Riel, Dollar and Euro
store(currency_key) [SHIFT K_4] [RALT K_4] [RALT K_5]
store(currency_out) U+17DB U+0024 U+20AC
c Khmer digits 0 through 9
store(digit_key) [K_0] [K_1] [K_2] [K_3] [K_4] [K_5] [K_6] [K_7] [K_8] [K_9]
store(digit_out) U+17E0 U+17E1 U+17E2 U+17E3 U+17E4 U+17E5 U+17E6 U+17E7 U+17E8 U+17E9
c Khmer Lek Attak (a.k.a. divination lore)
store(lek_attak_key) [SHIFT RALT K_0] [SHIFT RALT K_1] [SHIFT RALT K_2] [SHIFT RALT K_3] [SHIFT RALT K_4] \
[SHIFT RALT K_5] [SHIFT RALT K_6] [SHIFT RALT K_7] [SHIFT RALT K_8] [SHIFT RALT K_9]
store(lek_attak_out) U+17F0 U+17F1 U+17F2 U+17F3 U+17F4 \
U+17F5 U+17F6 U+17F7 U+17F8 U+17F9
c Lunar date format (Khmer style)
store(lunar_date_key) [SHIFT RALT K_A] [SHIFT RALT K_B] [SHIFT RALT K_C] [SHIFT RALT K_D] [SHIFT RALT K_E] \
[SHIFT RALT K_F] [SHIFT RALT K_G] [SHIFT RALT K_H] [SHIFT RALT K_I] [SHIFT RALT K_J] \
[SHIFT RALT K_K] [SHIFT RALT K_L] [SHIFT RALT K_M] [SHIFT RALT K_N] [SHIFT RALT K_O] \
[SHIFT RALT K_P] [SHIFT RALT K_Q] [SHIFT RALT K_R] [SHIFT RALT K_S] [SHIFT RALT K_T] \
[SHIFT RALT K_U] [SHIFT RALT K_V] [SHIFT RALT K_W] [SHIFT RALT K_X] [SHIFT RALT K_Y] \
[SHIFT RALT K_Z] [SHIFT RALT K_COLON] [SHIFT RALT K_COMMA] [SHIFT RALT K_PERIOD] [SHIFT RALT K_LBRKT] \
[SHIFT RALT K_RBRKT] [SHIFT RALT K_QUOTE]
store(lunar_date_out) U+19EC U+19FB U+19F9 U+19EE U+19E2 \
U+19EF U+19F0 U+19F1 U+19E7 U+19F2 \
U+19F3 U+19F4 U+19FD U+19FC U+19E8 \
U+19E9 U+19E0 U+19E3 U+19ED U+19E4 \
U+19E6 U+19FA U+19E1 U+19F8 U+19E5 \
U+19F7 U+19F5 U+19FE U+19FF U+19EA \
U+19EB U+19F6
c Subscripts​ for mobile/table layouts
store(input_subcons) \
[T_17D2_1780] [T_17D2_1781] [T_17D2_1782] [T_17D2_1783] [T_17D2_1784] [T_17D2_1785] [T_17D2_1786] [T_17D2_1787] \
[T_17D2_1788] [T_17D2_1789] [T_17D2_178A] [T_17D2_178B] [T_17D2_178C] [T_17D2_178D] [T_17D2_178E] [T_17D2_178F] \
[T_17D2_1790] [T_17D2_1791] [T_17D2_1792] [T_17D2_1793] [T_17D2_1794] [T_17D2_1795] [T_17D2_1796] [T_17D2_1797] \
[T_17D2_1798] [T_17D2_1799] [T_17D2_179A] [T_17D2_179B] [T_17D2_179C] [T_17D2_179D] [T_17D2_179E] [T_17D2_179F] \
[T_17D2_17A0] [T_17D2_17A1] [T_17D2_17A2]
store(subcons) \
U+1780 U+1781 U+1782 U+1783 U+1784 U+1785 U+1786 U+1787 \
U+1788 U+1789 U+178A U+178B U+178C U+178D U+178E U+178F \
U+1790 U+1791 U+1792 U+1793 U+1794 U+1795 U+1796 U+1797 \
U+1798 U+1799 U+179A U+179B U+179C U+179D U+179E U+179F \
U+17A0 U+17A1 U+17A2
c Arabic digits 0 through 9
store(arabic_digit_key) [U_0030] [U_0031] [U_0032] [U_0033] [U_0034] [U_0035] [U_0036] [U_0037] [U_0038] [U_0039]
store(arabic_digit_out) U+0030 U+0031 U+0032 U+0033 U+0034 U+0035 U+0036 U+0037 U+0038 U+0039
c These stores are for consonant (cluster) and consonant shifter configuration
store(v_above) U+17B7 U+17B8 U+17B9 U+17BA \
U+17BE U+17D0 c These vowels are rendered above the base consonant.
store(shiftable_c_1st) U+179F U+17A0 U+17A2 c These consonants shift to the 2nd series when adding U+17CA to them.
store(shiftable_BA) U+1794 c BA is unique because it can be used with both U+17CA (shift to 2nd) and and U+179C (shift to PA, still in 1st series).
store(shiftable_c_2nd) U+1784 U+1789 U+1798 U+1799 \
U+179A U+179C U+1793 U+179B c These consonants shift to the 1st series when adding U+17C9 to them.
store(shiftable_c_2nd_with_BA) outs(shiftable_c_2nd) outs(shiftable_BA)
store(c_2nd_combo_LO) U+1799 U+1798 U+1784 U+1794 \
U+179C c These can be combined with U+179B to make a 2nd series consonant cluster.
store(c_2nd_combo_MO) U+1799 U+179B U+1784 U+179A c These can be combined with U+1798 to make a 2nd series consonant cluster.
store(c_1st_combo_LO) U+1794 U+17A0 U+17A2 c they can be combined with U+179B to make a 1st series consonant cluster.
store(c_1st_combo_MO) U+17A0 U+179F U+17A2 c they can be combined with U+1798 to make a 1st series consonant cluster.
store(c_combo_SA) U+1794 U+1799 U+179B U+1798 \
U+1793 U+1789 U+1784 U+179A \
U+179C U+17A2 c they can be combined with U+179F to make a 1st series consonant cluster.
store(c_combo_QA) U+1786 U+1788 U+1794 U+1795 \
U+178F U+1791 c they can be combined with U+17A2 to make a 1st series consonant cluster.
store(c_combo_HA) U+179C U+17A3 c they can be combined with U+17A0 to make a 1st series consonant cluster.
c ==============================================BASIC RULES & CONSTRAINTS/ROTATIONS==============================================
group(main) using keys
c Basic rules with RALT key
+ any(c_key) > index(c_out,1)
+ any(v_key) > index(v_out,1)
+ any(ind_v_key) > index(ind_v_out,1)
+ any(diacritic_key) > index(diacritic_out,1)
+ any(c_shifter_key) > index(c_shifter,1)
+ any(punct_key) > index(punct_out,1)
+ any(latin_punct_key) > index(latin_punct_out,1)
+ any(currency_key) > index(currency_out,1)
+ any(digit_key) > index(digit_out,1)
+ any(lek_attak_key) > index(lek_attak_out,1)
+ any(lunar_date_key) > index(lunar_date_out,1)
+ any(spaces_key) > index(spaces_out,1)
c two-unicode-code-point vowels
+ [SHIFT K_A] > U+17B6 U+17C6
+ [SHIFT K_V] > U+17C1 U+17C7
+ [SHIFT K_COLON] > U+17C4 U+17C7
+ [K_COMMA] > U+17BB U+17C6
+ [SHIFT K_COMMA] > U+17BB U+17C7
c Constraints
any(v_gen) + [K_QUOTE] > context beep c no Bantoc after any genuine vowel
any(v_pseudo) + [K_QUOTE] > context beep c no Bantoc after any pseudo vowel
any(c_shifter) + [K_QUOTE] > context beep c no Bantoc after Triisap/Muusikatoan
U+17D2 any(c_out) + [K_QUOTE] > context beep c no Bantoc on a subscript
any(c_shifter) + any(c_shifter_key) > context beep c no two Triisap/Muusikatoan
c Rotation
U+17C7 + [SHIFT K_H] > U+17C8 c U+17C7 is changed to U+17C8 when pressing [SHIFT K_H].
U+17C8 + [SHIFT K_H] > U+17C7 c U+17C8 is changed to U+17C7 when K_H is pressed.
c any(v_gen) U+17C7 + [SHIFT K_H] > context beep c Don't rotate between U+17C7 and U+17C8.
c Press Space twice to get a White Space
U+200B + [K_SPACE] > U+0020
c One backspace deletes a two-part vowels (i.e. psuedoVowel + U+17C7)
any(v_combo_N) U+17C6 + [K_BKSP] > nul
any(v_combo_R) U+17C7 + [K_BKSP] > nul
c When pressing [K_U] after ាំ and ុំ, the U+17BB is converted to a respective consonant shifter placing before them (ាំ and ុំ).
any(shiftable_c_1st) U+17B6 U+17C6 + [K_U] > context(1) U+17CA context(2) context(3) c fix ស ាំ ុ > ស៊ាំ
any(shiftable_c_2nd_with_BA) U+17B6 U+17C6 + [K_U] > context(1) U+17C9 context(2) context(3) c fix ម ាំ ុ > ម៉ាំ
c Consonant shifter constraints (beep when the consonant shifter is used in inappropriate environment)
any(shiftable_c_1st) + [SHIFT K_QUOTE] > context U+17C9 beep
any(shiftable_c_2nd_with_BA) + [K_SLASH] > context U+17CA beep
c 1st series consonant clusters
any(c_combo_QA) U+17D2 U+17A2 + [SHIFT K_QUOTE] > context U+17C9 beep
U+179B U+17D2 any(c_1st_combo_LO) + [SHIFT K_QUOTE] > context U+17C9 beep
U+1798 U+17D2 any(c_1st_combo_MO) + [SHIFT K_QUOTE] > context U+17C9 beep
U+179F U+17D2 any(c_combo_SA) + [SHIFT K_QUOTE] > context U+17C9 beep
any(c_combo_HA) U+17D2 U+17A0 + [SHIFT K_QUOTE] > context U+17C9 beep
U+17A2 U+17D2 U+1784 + [SHIFT K_QUOTE] > context U+17C9 beep
U+17A2 U+17D2 U+179C + [SHIFT K_QUOTE] > context U+17C9 beep
c 2nd series consonant clusters
U+179B U+17D2 any(c_2nd_combo_LO) + [K_SLASH] > context U+17CA beep
U+1798 U+17D2 any(c_2nd_combo_MO) + [K_SLASH] > context U+17CA beep
c ==============================================MOBILE/TABLET TOUCH LAYOUT==============================================
+ any(input_subcons) > U+17D2 index(subcons, 1)
c delete a subscript (a subscript sign + a consonant) upon a backspace
platform('touch') U+17D2 any(c_out) + [K_BKSP] > nul c working OK
+ [K_NPSTAR] > U+002A
+ [SHIFT K_NPSTAR] > U+002A
+ [K_NPPLUS] > U+002B
+ [SHIFT K_NPPLUS] > U+002B
+ [K_NPMINUS] > U+002D
+ [SHIFT K_NPMINUS] > U+002D
+ [K_NPDOT] > U+002E
+ [SHIFT K_NPDOT] > U+002E
+ [K_NPSLASH] > U+002F
+ [SHIFT K_NPSLASH] > U+002F
c ==============================================NORMALIZATION==============================================
match > use(normalise)
group(normalise)
c Illegitimate vowel combinations should be transformed to the legitimate ones (Case #6 and #7).
U+17C1 U+17B6 > U+17C4 c when type េ and ា, tranform them to ោ
U+17B6 U+17C1 > U+17C4 c when type ា and េ, tranform them to ោ
U+17C1 U+17B8 > U+17BE c when type េ and ី , tranform them to ើ
U+17B8 U+17C1 > U+17BE c when type ី and េ, tranform them to ើ
c Illegitimate vowel combinations should be transformed to the legitimate ones (Case #5).
U+17C6 U+17BB > U+17BB U+17C6 c when type ំ and ុ , tranform them to ុំ
U+17C6 U+17B6 > U+17B6 U+17C6 c when type ំ and ា, tranform them to ាំ
c Rotate vowels
any(v_gen) any(v_gen) > context(2) c the last vowel stroke replaces the previous one.
c Rotation of two-part vowels
any(v_gen) any(v_pseudo) any(v_gen) > context(3) c a two-part vowel rotates to a genuine vowel.
any(v_gen) any(v_pseudo) any(v_pseudo) > context(3) c a two-part vowel rotates to a pseudo vowel.
any(v_gen) any(v_gen) any(v_pseudo) > context(2) context(3) c a genuine vowel rotates to a two-part vowel.
any(v_pseudo) any(v_gen) any(v_pseudo) > context(2) context(3) c a pseudo vowel roates to a two-part vowel.
any(v_gen) any(v_pseudo) any(v_gen) any(v_pseudo) > context(3) context(4) c a two-part vowel rotates to another two-part vowel.
any(v_pseudo) any(v_pseudo) > context(2) c a pseudo vowel rotates to another pseudo vowel.
c any(v_pseudo) any(v_gen) > context(2) c a pseudo vowel roates to a genuine vowel.
c Delete extraneous coeng markers
U+17D2 U+17D2 > context(1) c no two coengs are allowed.
U+17D2 any(v_any) > context(2) c The coeng is deleted when a vowel is put after it.
U+17D2 any(v_gen) any(v_pseudo) > context(2) context(3) c The coeng is deleted when a pseudo vowel is put after it.
c Case #1: when a subscript is typed after a vowel, the subscript should be automatically moved to before the vowel.
any(v_any) U+17D2 any(subcons) > context(2) context(3) context(1) c a vowel has to come after a subscript.
any(v_combo_N) U+17C6 U+17D2 any(subcons) > context(3) context(4) context(1) context(2) c these two-part vowels have to come after a subscript.
any(v_combo_R) U+17C7 U+17D2 any(subcons) > context(3) context(4) context(1) context(2) c these two-part vowels have to come after a subscript.
c a subscript placed after a shifter and a vowel (a genuine/pseudo vowel or a two-part vowel) has to move to before them.
any(c_shifter) any(v_any) U+17D2 any(subcons) > context(3) context(4) context(1) context(2) c after a genuine/pseudo vowel
any(c_shifter) any(v_combo_N) U+17C6 U+17D2 any(subcons) > context(4) context(5) context(1) context(2) context(3) c after a two-part vowel
any(c_shifter) any(v_combo_R) U+17C7 U+17D2 any(subcons) > context(4) context(5) context(1) context(2) context(3) c after a two-part vowel
c Case #2: when [D2+9A] is typed before a subscript, it should be moved to after it.
U+17D2 U+178A U+17D2 U+179A > context(1) U+178F context(3) context(4) c change subscript ្ដ to ្ត after ្រ
U+17D2 U+179A U+17D2 U+178A > context(3) U+178F context(1) context(2) c ្ដ placed after ្រ has to be changed to ្ត and placed before ្រ
U+17D2 U+178A any(v_any) U+17D2 U+179A > context(1) U+178F context(4) context(5) context(3) c fixed 6e
U+17D2 U+178A any(v_combo_N) U+17C6 U+17D2 U+179A > context(1) U+178F context(5) context(6) context(3) context(4)
U+17D2 U+178A any(v_combo_R) U+17C7 U+17D2 U+179A > context(1) U+178F context(5) context(6) context(3) context(4)
U+17D2 U+179A any(v_any) U+17D2 U+178A > context(1) U+178F context(4) context(2) context(3) c fixed 6g
U+17D2 U+179A any(v_combo_N) U+17C6 U+17D2 U+178A > context(1) U+178F context(5) context(2) context(3) context(4)
U+17D2 U+179A any(v_combo_R) U+17C7 U+17D2 U+178A > context(1) U+178F context(5) context(2) context(3) context(4)
U+17D2 U+179A any(c_shifter) U+17D2 any(subcons) > context(4) context(5) context(1) context(2) context(3) c when a shifter intervenes, move it to the end.
U+17D2 U+179A U+17D2 any(subcons) > context(3) context(4) context(1) context(2) c move any subscript placed after [D2+DA] to before them
U+17D2 U+179A any(v_any) U+17D2 any(subcons) > context(4) context(5) context(1) context(2) context(3) c when a vowel intervenes, move it to the end
U+17D2 U+179A any(v_combo_N) U+17C6 U+17D2 any(subcons) > context(5) context(6) context(1) context(2) context(3) context(4) c when a two-part vowel intervenes, move it to the end too.
U+17D2 U+179A any(v_combo_R) U+17C7 U+17D2 any(subcons) > context(5) context(6) context(1) context(2) context(3) context(4) c (same as stated right above)
c when a subscript is placed after a consonant shifter, they should be reversed. (Case #3)
any(c_shifter) U+17D2 any(subcons) > context(2) context(3) context(1)
c Reordering - Triisap and Muusikatoan store before a vowel and a vowelcomboR and pseudo vowel.
any(v_any) any(c_shifter) > context(2) context(1) c place either a enguine or pseudo vowel after a consonant shifter
any(v_gen) any(v_pseudo) any(c_shifter) > context(3) context(1) context(2) c place a two-part vowel after a consonant shifter(i.e. មោះ៉ )
any(c_shifter) any(v_any) any(c_shifter) > context(3) context(2) c when either a genuine or pseudo vowel is in between two consonant shifters, replace the one on the left with the one on the right.
any(c_shifter) any(v_gen) any(v_pseudo) any(c_shifter) > context(2) context(3) context(4) c when a two-part vowel is in between two consonants shifter, replace the one on the left with the one the right.
c +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
c + when [BB] is place either before or after an above vowel (ិ ី ឹ ឺ ើ and ាំ) and a Samyok Sannya (Case #4) +
c + the [BB] should transform to a respective consonant shifter placed before the vowel. +
c + the consonant shifter is a Trisap if the base consonant is ស ហ អ +
c + the consonant shifter is a Muusikatoan if the base consonant is ង ញ ម យ រ វ ប and ន ល +
c +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
c consonant clusters --1st > 2nd series (with U+17BB)
any(c_combo_QA) U+17D2 U+17A2 U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
any(c_combo_QA) U+17D2 U+17A2 U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
any(c_combo_QA) U+17D2 U+17A2 any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
any(c_combo_QA) U+17D2 U+17A2 U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+179B U+17D2 any(c_1st_combo_LO) U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+179B U+17D2 any(c_1st_combo_LO) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+179B U+17D2 any(c_1st_combo_LO) any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
U+179B U+17D2 any(c_1st_combo_LO) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+1798 U+17D2 any(c_1st_combo_MO) U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+1798 U+17D2 any(c_1st_combo_MO) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+1798 U+17D2 any(c_1st_combo_MO) any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
U+1798 U+17D2 any(c_1st_combo_MO) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+179F U+17D2 U+1794 U+17BB any(v_above) > context(1) context(2) context(3) U+17C9 context(5) c prevent ស្បុិ from transforming to ស្ប៊ិ​​​, but produce ស្ប៉ិ instead because ប never have Triisap shifted down.
U+179F U+17D2 U+1794 U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(5) context(6)
U+179F U+17D2 U+1794 any(v_above) U+17BB > context(1) context(2) context(3) U+17C9 context(4)
U+179F U+17D2 U+1794 U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(6) context(7)
U+179F U+17D2 any(c_combo_SA) U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+179F U+17D2 any(c_combo_SA) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+179F U+17D2 any(c_combo_SA) any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
U+179F U+17D2 any(c_combo_SA) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
any(c_combo_HA) U+17D2 U+17A0 U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
any(c_combo_HA) U+17D2 U+17A0 U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
any(c_combo_HA) U+17D2 U+17A0 any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
any(c_combo_HA) U+17D2 U+17A0 U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+17A2 U+17D2 U+1784 U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+17A2 U+17D2 U+1784 U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+17A2 U+17D2 U+1784 any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
U+17A2 U+17D2 U+1784 U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+17A2 U+17D2 U+179C U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+17A2 U+17D2 U+179C U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+17A2 U+17D2 U+179C any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4)
U+17A2 U+17D2 U+179C U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7)
U+17A0 U+17D2 U+1794 U+17BB any(v_above) > context(1) context(2) context(3) U+17C9 context(5) c change U+17BB preceded by HA and coeng BA to U+17C9 before the above vowel
U+17A0 U+17D2 U+1794 U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(3) context(6) c change U+17BB preceded by HA and coeng BA to U+17C9 before ាំ
U+17A0 U+17D2 U+1794 any(v_above) U+17BB > context(1) context(2) context(3) U+17C9 context(4) c change U+17BB preceded by HA and coeng BA to U+17C9 before the above vowel
U+17A0 U+17D2 U+1794 U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(6) context(7) c change U+17BB preceded by HA and coeng BA to U+17C9 before U+17B6 U+17C6
U+17A0 U+17D2 any(shiftable_c_2nd_with_BA) U+17BB any(v_above) > context(1) context(2) context(3) U+17CA context(5) c change U+17BB preceded by HA and coeng shiftable_c_2nd to U+17CA before the above vowel
U+17A0 U+17D2 any(shiftable_c_2nd_with_BA) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(3) context(6) c change U+17BB preceded by HA and coeng shiftable_c_2nd to U+17CA before ាំ
U+17A0 U+17D2 any(shiftable_c_2nd_with_BA) any(v_above) U+17BB > context(1) context(2) context(3) U+17CA context(4) c change U+17BB preceded by HA and coeng shiftable_c_2nd to U+17CA before the above vowel
U+17A0 U+17D2 any(shiftable_c_2nd_with_BA) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(6) context(7) c change U+17BB preceded by HA and coeng shiftable_c_2nd to U+17CA before U+17B6 U+17C6
c consonant clusters --2nd > 1st series (with U+17BB)
U+179B U+17D2 any(c_2nd_combo_LO) U+17BB any(v_above) > context(1) context(2) context(3) U+17C9 context(5)
U+179B U+17D2 any(c_2nd_combo_LO) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(5) context(6)
U+179B U+17D2 any(c_2nd_combo_LO) any(v_above) U+17BB > context(1) context(2) context(3) U+17C9 context(4)
U+179B U+17D2 any(c_2nd_combo_LO) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(6) context(7)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17BB any(v_above) > context(1) context(2) context(3) U+17C9 context(5)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17BB U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(5) context(6)
U+1798 U+17D2 any(c_2nd_combo_MO) any(v_above) U+17BB > context(1) context(2) context(3) U+17C9 context(4)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17BB U+17C6 U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(6) context(7)
c single consonants --1st > 2nd series (with U+17BB)
any(shiftable_c_1st) U+17BB any(v_above) > context(1) U+17CA context(3) c change U+17BB to U+17CA before the above vowel
any(shiftable_c_1st) U+17BB U+17B6 U+17C6 > context(1) U+17CA context(3) context(4) c change U+17BB to U+17CA before ាំ
any(shiftable_c_1st) any(v_above) U+17BB > context(1) U+17CA context(2) c change U+17BB to U+17CA and place it before the above vowel
any(shiftable_c_1st) U+17BB U+17C6 U+17B6 U+17C6 > context(1) U+17CA context(4) context(5) c change U+17BB U+17C6 to U+17CA
c single consonants --2nd > 1st series (with U+17BB)
any(shiftable_c_2nd_with_BA) U+17BB any(v_above) > context(1) U+17C9 context(3) c change U+17BB to U+17C9 before the above vowel
any(shiftable_c_2nd_with_BA) U+17BB U+17B6 U+17C6 > context(1) U+17C9 context(3) context(4) c change U+17BB to U+17C9 before ាំ
any(shiftable_c_2nd_with_BA) any(v_above) U+17BB > context(1) U+17C9 context(2) c change U+17BB to U+17C9 before the above vowel
any(shiftable_c_2nd_with_BA) U+17BB U+17C6 U+17B6 U+17C6 > context(1) U+17C9 context(4) context(5) c change U+17BB U+17C6 to U+17C9
c prevent incorrect use of consonant shifter with 2nd series consonant clusters (without U+17BB)
U+179B U+17D2 any(c_2nd_combo_LO) U+17CA any(v_above) > context(1) context(2) context(3) U+17C9 context(5)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17CA any(v_above) > context(1) context(2) context(3) U+17C9 context(5)
U+179B U+17D2 any(c_2nd_combo_LO) U+17CA any(v_gen) any(v_pseudo) > context(1) context(2) context(3) U+17C9 context(5) context(6)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17CA any(v_gen) any(v_pseudo) > context(1) context(2) context(3) U+17C9 context(5) context(6)
c single consonants --2nd > 1st series (without U+17BB)
any(shiftable_c_2nd) U+17CA any(v_above) > context(1) U+17C9 context(3) c change U+17CA to U+17C9 before the above vowel
any(shiftable_c_2nd) U+17CA any(v_gen) any(v_pseudo) > context(1) U+17C9 context(3) context(4) c change U+17CA to U+17C9 before ាំ
U+17D2 any(shiftable_c_2nd) U+17CA any(v_above) > context c stop ស្រ៊ី from becoming ស្រ៉ី
U+17D2 any(shiftable_c_2nd) U+17CA any(v_gen) any(v_pseudo) > context
c SPECIAL CASE -- the following clusters may accept either of the two consonant shifters.
U+1794 U+17D2 U+1799 any(c_shifter) > context
U+179F U+17D2 U+1794 any(c_shifter) > context
U+1786 U+17D2 U+1794 any(c_shifter) > context
U+1794 U+17D2 U+1799 any(c_shifter) > context
U+179F U+17D2 U+1794 any(c_shifter) > context
U+1786 U+17D2 U+1794 any(c_shifter) > context
c single --1st > 2nd series (without U+17BB)
any(shiftable_c_1st) U+17C9 any(v_above) > context(1) U+17CA context(3) c change U+17C9 to U+17CA before the above vowel
any(shiftable_c_1st) U+17C9 any(v_gen) any(v_pseudo) > context(1) U+17CA context(3) context(4) c change U+17C9 to U+17CA before ាំ
c consonant clusters --1st > 2nd series (without U+17BB)
any(c_combo_QA) U+17D2 U+17A2 U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+179B U+17D2 any(c_1st_combo_LO) U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+1798 U+17D2 any(c_1st_combo_MO) U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+179F U+17D2 U+1794 U+17C9 any(v_above) > context(1) context(2) context(3) U+17C9 context(5) c prevent ស្ប៉ី from transforming to ស្ប៊ី
U+179F U+17D2 any(c_combo_SA) U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
any(c_combo_HA) U+17D2 U+17A0 U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+17A2 U+17D2 U+1784 U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
U+17A2 U+17D2 U+179C U+17C9 any(v_above) > context(1) context(2) context(3) U+17CA context(5)
any(c_combo_QA) U+17D2 U+17A2 U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+179B U+17D2 any(c_1st_combo_LO) U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+1798 U+17D2 any(c_1st_combo_MO) U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+179F U+17D2 U+1794 U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17C9 context(5) context(6) c prevent ស្ប៉ាំ from transforming to ស្ប៊ាំ
U+179F U+17D2 any(c_combo_SA) U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
any(c_combo_HA) U+17D2 U+17A0 U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+17A2 U+17D2 U+1784 U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
U+17A2 U+17D2 U+179C U+17C9 U+17B6 U+17C6 > context(1) context(2) context(3) U+17CA context(5) context(6)
c U+17B6 U+17BB and U+17C6 in that order preceeded by any first series consonant (cluster) are transformed to U+17CA U+17B6 and U+17C6
any(shiftable_c_1st) U+17B6 U+17BB U+17C6 > context(1) U+17CA context(2) context(4)
any(shiftable_c_1st) U+17BB U+17C6 U+17B6 > context(1) U+17CA context(4) context(3)
any(c_combo_QA) U+17D2 U+17A2 U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
any(c_combo_QA) U+17D2 U+17A2 U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
U+179B U+17D2 any(c_1st_combo_LO) U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
U+179B U+17D2 any(c_1st_combo_LO) U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
U+1798 U+17D2 any(c_1st_combo_MO) U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
U+1798 U+17D2 any(c_1st_combo_MO) U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
U+179F U+17D2 any(c_combo_SA) U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
U+179F U+17D2 any(c_combo_SA) U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
any(c_combo_HA) U+17D2 U+17A0 U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
any(c_combo_HA) U+17D2 U+17A0 U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
U+17A2 U+17D2 U+1784 U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
U+17A2 U+17D2 U+1784 U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(6) context(5)
U+17A2 U+17D2 U+179C U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17CA context(4) context(6)
U+17A2 U+17D2 U+179C U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17CA context(4) context(6)
c U+17B6 U+17BB and U+17C6 in that order preceeded by any second series consonant (cluster) are transformed to U+17C9 U+17B6 and U+17C6
any(shiftable_c_2nd_with_BA) U+17B6 U+17BB U+17C6 > context(1) U+17C9 context(2) context(4)
any(shiftable_c_2nd_with_BA) U+17BB U+17C6 U+17B6 > context(1) U+17C9 context(4) context(3)
U+179B U+17D2 any(c_2nd_combo_LO) U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17C9 context(4) context(6)
U+179B U+17D2 any(c_2nd_combo_LO) U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17C9 context(6) context(5)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17B6 U+17BB U+17C6 > context(1) context(2) context(3) U+17C9 context(4) context(6)
U+1798 U+17D2 any(c_2nd_combo_MO) U+17BB U+17C6 U+17B6 > context(1) context(2) context(3) U+17C9 context(6) context(5)
c េុី/ុេី/៉េី > ៊ើ
any(shiftable_c_1st) U+17C1 U+17BB U+17B8 > context(1) U+17CA U+17BE c cancel out by vowel rotation
any(shiftable_c_1st) U+17BB U+17C1 U+17B8 > context(1) U+17CA U+17BE c cancel out by vowel rotation
any(shiftable_c_1st) U+17C9 U+17C1 U+17B8 > context(1) U+17CA U+17BE
any(c_combo_QA) U+17D2 U+17A2 U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
any(c_combo_QA) U+17D2 U+17A2 U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
any(c_combo_QA) U+17D2 U+17A2 U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
U+179B U+17D2 any(c_1st_combo_LO) U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+179B U+17D2 any(c_1st_combo_LO) U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+179B U+17D2 any(c_1st_combo_LO) U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
U+1798 U+17D2 any(c_1st_combo_MO) U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+1798 U+17D2 any(c_1st_combo_MO) U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+1798 U+17D2 any(c_1st_combo_MO) U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
U+179F U+17D2 any(c_combo_SA) U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+179F U+17D2 any(c_combo_SA) U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+179F U+17D2 any(c_combo_SA) U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
any(c_combo_HA) U+17D2 U+17A0 U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
any(c_combo_HA) U+17D2 U+17A0 U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
any(c_combo_HA) U+17D2 U+17A0 U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
U+17A2 U+17D2 U+1784 U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+17A2 U+17D2 U+1784 U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+17A2 U+17D2 U+1784 U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
U+17A2 U+17D2 U+179C U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+17A2 U+17D2 U+179C U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE c cancel out by vowel rotation
U+17A2 U+17D2 U+179C U+17C9 U+17C1 U+17B8 > context(1) context(2) context(3) U+17CA U+17BE
c េុី/ុេី/៊េី > ៉ើ
any(shiftable_c_2nd) U+17C1 U+17BB U+17B8 > context(1) U+17C9 U+17BE c cancel out by vowel rotation
any(shiftable_c_2nd) U+17BB U+17C1 U+17B8 > context(1) U+17C9 U+17BE c cancel out by vowel rotation
any(shiftable_c_2nd) U+17CA U+17C1 U+17B8 > context(1) U+17C9 U+17BE
U+179B U+17D2 any(c_2nd_combo_LO) U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE c cancel out by vowel rotation
U+179B U+17D2 any(c_2nd_combo_LO) U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE c cancel out by vowel rotation
U+179B U+17D2 any(c_2nd_combo_LO) U+17CA U+17C1 U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE
U+1798 U+17D2 any(c_2nd_combo_MO) U+17C1 U+17BB U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE c cancel out by vowel rotation
U+1798 U+17D2 any(c_2nd_combo_MO) U+17BB U+17C1 U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE c cancel out by vowel rotation
U+1798 U+17D2 any(c_2nd_combo_MO) U+17CA U+17C1 U+17B8 > context(1) context(2) context(3) U+17C9 U+17BE
c place Robat after a base before any vowel
any(c_out) any(v_gen) U+17CC > context(1) context(3) context(2)
c Use appropriate identical subscript in two separate contexts. (Case #8)
U+178E U+17D2 U+178F > context(1) context(2) U+178A
U+1793 U+17D2 U+178A > context(1) context(2) U+178F
c BONUSES​(note that these combinations are invalid and they can't be found in the CN dictionary 1967 nor RH 1997, but users think they are)
U+1791 U+17D2 U+1794 > U+17A1 c ទ្ប transforms to ឡ
U+1794 U+17D2 U+1789 > U+17AB c ប្ញ transforms to ឫ
U+17AB U+17BB > U+17AC c ឫុ transforms to ឬ
U+17AD U+17B6 > U+1789 c ឭា transforms to ញ
U+17AE U+17B6 > U+1789 c ឮា transforms to ញ
U+1796 U+17D2 U+1789 > U+17AD c ព្ញ transforms to ឭ
U+17AD U+17BB > U+17AE c ឭុ transforms to ឮ
U+1796 U+17D2 U+178B > U+17B0 c ព្ឋ transforms to ឰ
U+17A7 U+17B7 > U+17B1 c ឧិ transforms to ឱ
U+17A7 U+17CC > U+17B1 c ឧ៌ transforms to ឱ
U+17A7 U+17CD > U+17B1 c ឧ៍ transforms to ឱ
c U+17D4 U+179B U+17D4 > U+17D8 c ។ល។ transforms to U+17D8
U+178A U+17D2 U+1792 > U+178A U+17D2 U+178B c ដ្ធ > ដ្ឋ
U+1791 U+17D2 U+178B > U+1791 U+17D2 U+1792 c ទ្ឋ > ទ្ធ
c additional rules
U+1796 U+1793 U+17D2 U+178B > context(1) context(2) context(3) U+1792 c ពន្ឋ > ពន្ធ
U+1796 U+17D0 U+1793 U+17D2 U+178B > context(1) context(2) context(3) context(4) U+1792 c ព័ន្ឋ > ព័ន្ធ
U+17AA U+17D2 U+1799 > U+17B1 context(2) context(3) c ឪ្យ > ឱ្យ
U+17B3 U+17D2 U+1799 > U+17B1 context(2) context(3) c ឳ្យ > ឱ្យ
U+1789 U+17D2 U+179C > U+1796 context(2) context(3) U+17B6 c ញ្វ (as in សញ្វវុធ) > ព្វា as in សញ្វវុធ
U+17D2 U+1799 U+17C1 U+17BA > U+17BF c េ្យឺ > ឿ
U+17D2 U+1799 U+17C1 U+17B9 > U+17BF c េ្យឹ > ឿ
U+17D2 U+1799 U+17C1 U+17B8 > U+17BF c េ្យី > ឿ
U+17D2 U+1799 U+17D2 any(c_out) U+17C1 U+17BA > context(3) context(4) U+17BF c គ្រេ្យឺង > គ្រឿង
U+17D2 U+1799 U+17D2 any(c_out) U+17C1 U+17B9 > context(3) context(4) U+17BF c គ្រេ្យឹង > គ្រឿង
U+17D2 U+1799 U+17D2 any(c_out) U+17C1 U+17B8 > context(3) context(4) U+17BF c គ្រេ្យីង > គ្រឿង
c In everyday practice, both 'regular space' and 'no-break space' are not used before punctuation, i.e. ។ ! etc.
c An annoying issue of having line broken up at the end of the line dropping the punctuation (។, ! and also ៗ) to the next line.
c It may be helpful to include a rule where a 'Zero Width No-Break Space' should be output together with those punct.
c ref: https://www.unicode.org/L2/L2020/20008-core-text.pdf
c ref: https://www.unicode.org/versions/Unicode12.0.0/ch23.pdf#G12985
c ref: https://www.compart.com/en/unicode/U+FEFF
c EOF

View file

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<visualkeyboard>
<header>
<version>10.0</version>
<kbdname>khmer_angkor</kbdname>
<flags>
<usealtgr/>
</flags>
</header>
<encoding name="unicode" fontname="Khmer Busra Kbd" fontsize="16">
<layer shift="RA">
<key vkey="K_B">ឞ</key>
<key vkey="K_K">ឝ</key>
<key vkey="K_QUOTE">ៈ</key>
<key vkey="K_RBRKT">ឳ</key>
<key vkey="K_T">ឨ</key>
<key vkey="K_LBRKT">ឩ</key>
<key vkey="K_P">ឰ</key>
<key vkey="K_R">ឫ</key>
<key vkey="K_I">ឦ</key>
<key vkey="K_O">ឱ</key>
<key vkey="K_E">ឯ</key>
<key vkey="K_3"></key>
<key vkey="K_W"></key>
<key vkey="K_Q">ៜ</key>
<key vkey="K_EQUAL"></key>
<key vkey="K_COLON">៖</key>
<key vkey="K_6">៙</key>
<key vkey="K_7">៚</key>
<key vkey="K_M"></key>
<key vkey="K_L">៘</key>
<key vkey="K_1">‌</key>
<key vkey="K_BKQUOTE">‍</key>
<key vkey="K_U">]</key>
<key vkey="K_Y">[</key>
<key vkey="K_SLASH">/</key>
<key vkey="K_PERIOD">.</key>
<key vkey="K_H">‘</key>
<key vkey="K_A">+</key>
<key vkey="K_V">&amp;</key>
<key vkey="K_J">’</key>
<key vkey="K_8">*</key>
<key vkey="K_2">@</key>
<key vkey="K_BKSLASH">\</key>
<key vkey="K_0">}</key>
<key vkey="K_9">{</key>
<key vkey="K_S">-</key>
<key vkey="K_F">÷</key>
<key vkey="K_G">:</key>
<key vkey="K_COMMA">,</key>
<key vkey="K_HYPHEN">≈</key>
<key vkey="K_N">;</key>
<key vkey="K_Z">&lt;</key>
<key vkey="K_C">#</key>
<key vkey="K_X">&gt;</key>
<key vkey="K_D">×</key>
<key vkey="K_4">$</key>
<key vkey="K_5">€</key>
<key vkey="K_SPACE"> </key>
</layer>
<layer shift="SRA">
<key vkey="K_8">៸</key>
<key vkey="K_0">៰</key>
<key vkey="K_1">៱</key>
<key vkey="K_2">៲</key>
<key vkey="K_3">៳</key>
<key vkey="K_4">៴</key>
<key vkey="K_5">៵</key>
<key vkey="K_6">៶</key>
<key vkey="K_7">៷</key>
<key vkey="K_9">៹</key>
<key vkey="K_PERIOD">᧿</key>
<key vkey="K_COMMA">᧾</key>
<key vkey="K_LBRKT">᧪</key>
<key vkey="K_RBRKT">᧫</key>
<key vkey="K_QUOTE">᧶</key>
<key vkey="K_COLON">᧵</key>
<key vkey="K_A">᧬</key>
<key vkey="K_Z">᧷</key>
<key vkey="K_Y">᧥</key>
<key vkey="K_X">᧸</key>
<key vkey="K_W">᧡</key>
<key vkey="K_B">᧻</key>
<key vkey="K_C">᧹</key>
<key vkey="K_D">᧮</key>
<key vkey="K_E">᧢</key>
<key vkey="K_F">᧯</key>
<key vkey="K_G">᧰</key>
<key vkey="K_U">᧦</key>
<key vkey="K_H">᧱</key>
<key vkey="K_T">᧤</key>
<key vkey="K_S">᧭</key>
<key vkey="K_R">᧣</key>
<key vkey="K_I">᧧</key>
<key vkey="K_Q">᧠</key>
<key vkey="K_V">᧺</key>
<key vkey="K_J">᧲</key>
<key vkey="K_K">᧳</key>
<key vkey="K_L">᧴</key>
<key vkey="K_P">᧩</key>
<key vkey="K_O">᧨</key>
<key vkey="K_N">᧼</key>
<key vkey="K_M">᧽</key>
</layer>
<layer shift="">
<key vkey="K_SPACE">​</key>
<key vkey="K_QUOTE"></key>
<key vkey="K_COMMA"></key>
<key vkey="K_HYPHEN">ឥ</key>
<key vkey="K_PERIOD">។</key>
<key vkey="K_SLASH"></key>
<key vkey="K_0">០</key>
<key vkey="K_1">១</key>
<key vkey="K_2">២</key>
<key vkey="K_3">៣</key>
<key vkey="K_4">៤</key>
<key vkey="K_5">៥</key>
<key vkey="K_6">៦</key>
<key vkey="K_7">៧</key>
<key vkey="K_8">៨</key>
<key vkey="K_9">៩</key>
<key vkey="K_COLON"></key>
<key vkey="K_EQUAL">ឲ</key>
<key vkey="K_LBRKT"></key>
<key vkey="K_BKSLASH">ឮ</key>
<key vkey="K_RBRKT">ឪ</key>
<key vkey="K_BKQUOTE">«</key>
<key vkey="K_A"></key>
<key vkey="K_B">ប</key>
<key vkey="K_C">ច</key>
<key vkey="K_D">ដ</key>
<key vkey="K_E"></key>
<key vkey="K_F">ថ</key>
<key vkey="K_G">ង</key>
<key vkey="K_H">ហ</key>
<key vkey="K_I"></key>
<key vkey="K_J"></key>
<key vkey="K_K">ក</key>
<key vkey="K_L">ល</key>
<key vkey="K_M">ម</key>
<key vkey="K_N">ន</key>
<key vkey="K_O"></key>
<key vkey="K_P">ផ</key>
<key vkey="K_Q">ឆ</key>
<key vkey="K_R">រ</key>
<key vkey="K_S">ស</key>
<key vkey="K_T">ត</key>
<key vkey="K_U"></key>
<key vkey="K_V">វ</key>
<key vkey="K_W"></key>
<key vkey="K_X">ខ</key>
<key vkey="K_Y">យ</key>
<key vkey="K_Z">ឋ</key>
</layer>
<layer shift="S">
<key vkey="K_SPACE"></key>
<key vkey="K_1">!</key>
<key vkey="K_QUOTE"></key>
<key vkey="K_3">"</key>
<key vkey="K_4">៛</key>
<key vkey="K_5">%</key>
<key vkey="K_7"></key>
<key vkey="K_9">(</key>
<key vkey="K_0">)</key>
<key vkey="K_8"></key>
<key vkey="K_EQUAL">=</key>
<key vkey="K_COLON"></key>
<key vkey="K_PERIOD">៕</key>
<key vkey="K_SLASH">?</key>
<key vkey="K_2">ៗ</key>
<key vkey="K_A"></key>
<key vkey="K_B">ព</key>
<key vkey="K_C">ជ</key>
<key vkey="K_D">ឌ</key>
<key vkey="K_E"></key>
<key vkey="K_F">ធ</key>
<key vkey="K_G">អ</key>
<key vkey="K_H">ះ</key>
<key vkey="K_I"></key>
<key vkey="K_J">ញ</key>
<key vkey="K_K">គ</key>
<key vkey="K_L">ឡ</key>
<key vkey="K_M"></key>
<key vkey="K_N">ណ</key>
<key vkey="K_O"></key>
<key vkey="K_P">ភ</key>
<key vkey="K_Q">ឈ</key>
<key vkey="K_R">ឬ</key>
<key vkey="K_S"></key>
<key vkey="K_T">ទ</key>
<key vkey="K_U"></key>
<key vkey="K_V"></key>
<key vkey="K_W"></key>
<key vkey="K_X">ឃ</key>
<key vkey="K_Y"></key>
<key vkey="K_Z">ឍ</key>
<key vkey="K_6"></key>
<key vkey="K_HYPHEN"></key>
<key vkey="K_LBRKT"></key>
<key vkey="K_BKSLASH">ឭ</key>
<key vkey="K_RBRKT">ឧ</key>
<key vkey="K_BKQUOTE">»</key>
<key vkey="K_COMMA"></key>
</layer>
</encoding>
</visualkeyboard>

View file

@ -0,0 +1,55 @@
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { KmnCompiler } from '@keymanapp/kmc-kmn';
import { KMX, KmxFileReader } from '@keymanapp/common-types';
import { WriteCompiledKeyboard } from '../src/compiler/write-compiled-keyboard.js';
import { extractTouchLayout } from './util.js';
const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/');
const fixturesDir = __dirname + '/../../test/fixtures/';
const fixtureName = fixturesDir + 'khmer_angkor.js';
const infile = fixturesDir + 'khmer_angkor.kmn';
const outfile = fixturesDir + 'khmer_angkor.kmx'; // intermediate outfile
const testOutfile = fixturesDir + 'khmer_angkor.test.js';
if(fs.existsSync(testOutfile)) {
fs.unlinkSync(testOutfile);
}
const callbacks = new TestCompilerCallbacks();
const kmnCompiler = new KmnCompiler();
if(!await kmnCompiler.init(callbacks)) {
console.error('kmx compiler failed to init');
process.exit(1);
}
// TODO: runToMemory, add option to kmxCompiler to store debug-data for conversion to .js (e.g. store metadata, group readonly metadata, etc)
if(!kmnCompiler.run(infile, outfile, {
shouldAddCompilerVersion: false,
saveDebug: true,
target: 'js'
})) {
callbacks.printMessages();
process.exit(1);
}
const reader = new KmxFileReader();
const keyboard: KMX.KEYBOARD = reader.read(callbacks.loadFile(outfile));
const js = WriteCompiledKeyboard(callbacks, infile, outfile, 'khmer_angkor', keyboard, true);
callbacks.printMessages();
const fjs = fs.readFileSync(fixtureName, 'utf8');
const expected = extractTouchLayout(fjs);
const actual = extractTouchLayout(js);
if(expected.js !== actual.js) {
fs.writeFileSync(testOutfile, js);
console.error('JS not equal');
process.exit(1);
}

View file

@ -0,0 +1,65 @@
import 'mocha';
import { assert } from 'chai';
// import sinonChai from 'sinon-chai';
import { WriteCompiledKeyboard } from '../src/compiler/write-compiled-keyboard.js';
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { KmnCompiler } from '@keymanapp/kmc-kmn';
import { KMX, KmxFileReader } from '@keymanapp/common-types';
import { extractTouchLayout } from './util.js';
const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/');
const fixturesDir = __dirname + '/../../test/fixtures/';
//const baselineDir = __dirname + '/../../../../../common/test/keyboards/baseline/';
// chai.use(sinonChai);
describe('Compiler class', function() {
const callbacks = new TestCompilerCallbacks();
this.afterEach(function() {
callbacks.printMessages();
callbacks.clear();
});
it('should compile a basic keyboard', async function() {
// const compiler = new KeymanWebCompiler(callbacks, {addCompilerVersion: false, debug: true});
const fixtureName = fixturesDir + 'khmer_angkor.js';
const infile = fixturesDir + 'khmer_angkor.kmn';
const outfile = fixturesDir + 'khmer_angkor.kmx'; // intermediate outfile
const testOutfile = fixturesDir + 'khmer_angkor.test.js';
if(fs.existsSync(testOutfile)) {
fs.unlinkSync(testOutfile);
}
const kmnCompiler = new KmnCompiler();
assert.isTrue(await kmnCompiler.init(callbacks));
// TODO: runToMemory, add option to kmxCompiler to store debug-data for conversion to .js (e.g. store metadata, group readonly metadata, visual keyboard source filename, etc)
assert.isTrue(kmnCompiler.run(infile, outfile, {
shouldAddCompilerVersion: false,
saveDebug: true,
target: 'js'
}));
const reader = new KmxFileReader();
const keyboard: KMX.KEYBOARD = reader.read(callbacks.loadFile(outfile));
const js = WriteCompiledKeyboard(callbacks, infile, outfile, 'khmer_angkor', keyboard, true);
const fjs = fs.readFileSync(fixtureName, 'utf8');
const expected = extractTouchLayout(fjs);
const actual = extractTouchLayout(js);
fs.writeFileSync(testOutfile + '.strip.js', actual.js);
fs.writeFileSync(fixtureName + '.strip.js', expected.js);
fs.writeFileSync(testOutfile, js);
assert.deepEqual(actual.js, expected.js);
assert.deepEqual(JSON.parse(actual.touchLayout), JSON.parse(expected.touchLayout));
});
});

View file

@ -0,0 +1,26 @@
{
"extends": "../../kmc/tsconfig.kmc-base.json",
"compilerOptions": {
"rootDir": ".",
"rootDirs": ["./", "../src/"],
"outDir": "../build/test",
"baseUrl": ".",
"allowSyntheticDefaultImports": true, // for chai
"paths": {
"@keymanapp/common-types": ["../../../../common/web/types/src/main"],
"@keymanapp/developer-test-helpers": ["../../common/web/test-helpers/index"],
"@keymanapp/kmc-kmn": ["../../kmc-kmn/src/main"],
},
},
"include": [
"**/test-*.ts",
"util.ts"
],
"references": [
{ "path": "../../../../common/web/types/" },
{ "path": "../../kmc-kmn/" },
{ "path": "../../common/web/test-helpers/" },
{ "path": "../" }
]
}

View file

@ -0,0 +1,19 @@
interface ETLResult {
js: string;
touchLayout: string;
}
export function extractTouchLayout(js: string): ETLResult|null {
let m = /KVKL=(?<kvkl>.+?);[\r\n]/ds.exec(js);
if(!m) {
return null;
}
let kvkl = (<any>m).indices.groups.kvkl;
return {
js: js.substring(0, kvkl[0]) + 'null' + js.substring(kvkl[1]),
touchLayout: m.groups?.['kvkl'] ?? ''
};
}

View file

@ -0,0 +1,22 @@
{
"extends": "../../../tsconfig.esm-base.json",
"compilerOptions": {
"outDir": "build/src/",
"rootDir": "src/",
"baseUrl": ".",
"paths": {
"@keymanapp/common-types": ["../../../common/web/types/src/main"],
"@keymanapp/kmc-kmn": ["../kmc-kmn/src/main"],
},
},
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/types/" },
{ "path": "../kmc-kmn/" },
{ "path": "../../../core/include/ldml/"},
]
}

View file

@ -1 +1 @@
if(typeof keyman === 'undefined') {console.error('Keyboard requires KeymanWeb 16.0 or later');} else {KeymanWeb.KR(new Keyboard_basic());}function Keyboard_basic() {this.KI="Keyboard_basic";this.KN={"value":"TestKbd"};this.KMINVER="16.0";this.KV={F: '10pt "Arial"', K102: 0};this.KV.KLS={TODO_LDML: 2};this.KDU=1;this.KH="";this.KM=0;this.KBVER="1.0.0";this.KMBM=0;this.KVKL={"desktop":{"defaultHint":"none","layer":[{"id":"base","row":[{"id":0,"key":[{"id":"T_hmaqtugha","text":"ħ"},{"id":"T_that","text":"ថា"}]}]}]}};this.gs=function(t,e){return 0;};}
if(typeof keyman === 'undefined') {console.error('Keyboard requires KeymanWeb 16.0 or later');} else {KeymanWeb.KR(new Keyboard_basic());}function Keyboard_basic() {this.KI="Keyboard_basic";this.KN={"value":"TestKbd"};this.KMINVER="16.0";this.KV={F: '10pt "Arial"', K102: 0};this.KV.KLS={TODO_LDML: 2};this.KDU=1;this.KH="";this.KM=0;this.KBVER="1.0.0";this.KMBM=0;this.KVKL={"desktop":{"defaultHint":"none","layer":[{"id":"base","row":[{"id":"0","key":[{"id":"T_hmaqtugha","text":"ħ"},{"id":"T_that","text":"ថា"}]}]}]}};this.gs=function(t,e){return 0;};}

View file

@ -24,7 +24,7 @@ function Keyboard_basic() {
"id": "base",
"row": [
{
"id": 0,
"id": "0",
"key": [
{
"id": "T_hmaqtugha",

View file

@ -21,6 +21,7 @@ builder_describe "Build Keyman Keyboard Compiler kmc" \
"@/common/web/keyman-version" \
"@/common/web/types" \
"@/developer/src/kmc-kmn" \
"@/developer/src/kmc-kmw" \
"@/developer/src/kmc-ldml" \
"@/developer/src/kmc-model" \
"@/developer/src/kmc-model-info" \
@ -113,6 +114,7 @@ readonly PACKAGES=(
common/models/types
core/include/ldml
developer/src/kmc-kmn
developer/src/kmc-kmw
developer/src/kmc-ldml
developer/src/kmc-model
developer/src/kmc-model-info

View file

@ -40,6 +40,7 @@
"@keymanapp/keyman-version": "*",
"@keymanapp/common-types": "*",
"@keymanapp/kmc-kmn": "*",
"@keymanapp/kmc-kmw": "*",
"@keymanapp/kmc-ldml": "*",
"@keymanapp/kmc-model": "*",
"@keymanapp/kmc-model-info": "*",

View file

@ -8,6 +8,7 @@
"paths": {
"@keymanapp/common-types": [ "../../../common/web/types/src/main" ],
"@keymanapp/kmc-kmn": [ "../kmc-kmn/src/main" ],
"@keymanapp/kmc-kmw": [ "../kmc-kmw/src/main" ],
"@keymanapp/kmc-ldml": [ "../kmc-ldml/src/main" ],
"@keymanapp/kmc-model": [ "../kmc-model/src/main" ],
"@keymanapp/kmc-model-info": [ "../kmc-model-info/src/model-info-compiler" ],
@ -21,6 +22,7 @@
{ "path": "../../../common/web/keyman-version/tsconfig.esm.json" },
{ "path": "../../../common/web/types" },
{ "path": "../kmc-kmn" },
{ "path": "../kmc-kmw" },
{ "path": "../kmc-ldml" },
{ "path": "../kmc-model" },
{ "path": "../kmc-model-info" },

261
package-lock.json generated
View file

@ -11,6 +11,7 @@
"core/include/ldml",
"developer/src/common/web/test-helpers",
"developer/src/kmc-kmn",
"developer/src/kmc-kmw",
"developer/src/kmc-ldml",
"developer/src/kmc-model",
"developer/src/kmc-model-info",
@ -635,6 +636,7 @@
"@keymanapp/common-types": "*",
"@keymanapp/keyman-version": "*",
"@keymanapp/kmc-kmn": "*",
"@keymanapp/kmc-kmw": "*",
"@keymanapp/kmc-ldml": "*",
"@keymanapp/kmc-model": "*",
"@keymanapp/kmc-model-info": "*",
@ -1029,6 +1031,261 @@
"node": ">=0.3.1"
}
},
"developer/src/kmc-kmw": {
"name": "@keymanapp/kmc-kmw",
"license": "MIT",
"dependencies": {
"@keymanapp/common-types": "*",
"@keymanapp/kmc-kmn": "*",
"ajv": "^8.11.0",
"restructure": "git+https://github.com/keymanapp/dependency-restructure.git#49d129cf0916d082a7278bb09296fb89cecfcc50",
"semver": "^7.3.7",
"xml2js": "git+https://github.com/keymanapp/dependency-node-xml2js#535fe732dc408d697e0f847c944cc45f0baf0829"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.6",
"@types/semver": "^7.3.12",
"@types/xml2js": "^0.4.5",
"c8": "^7.12.0",
"chai": "^4.3.4",
"chalk": "^2.4.2",
"mocha": "^8.4.0",
"ts-node": "^9.1.1",
"typescript": "^4.9.5"
}
},
"developer/src/kmc-kmw/node_modules/@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
"integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
"dev": true
},
"developer/src/kmc-kmw/node_modules/@types/node": {
"version": "10.17.60",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
"dev": true
},
"developer/src/kmc-kmw/node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"developer/src/kmc-kmw/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"developer/src/kmc-kmw/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"developer/src/kmc-kmw/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
},
"developer/src/kmc-kmw/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"developer/src/kmc-kmw/node_modules/js-yaml": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"developer/src/kmc-kmw/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"developer/src/kmc-kmw/node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"developer/src/kmc-kmw/node_modules/mocha": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz",
"integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==",
"dev": true,
"dependencies": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.5.1",
"debug": "4.3.1",
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.1.6",
"growl": "1.10.5",
"he": "1.2.0",
"js-yaml": "4.0.0",
"log-symbols": "4.0.0",
"minimatch": "3.0.4",
"ms": "2.1.3",
"nanoid": "3.1.20",
"serialize-javascript": "5.0.1",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
"which": "2.0.2",
"wide-align": "1.1.3",
"workerpool": "6.1.0",
"yargs": "16.2.0",
"yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0"
},
"bin": {
"_mocha": "bin/_mocha",
"mocha": "bin/mocha"
},
"engines": {
"node": ">= 10.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mochajs"
}
},
"developer/src/kmc-kmw/node_modules/mocha/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"developer/src/kmc-kmw/node_modules/mocha/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"developer/src/kmc-kmw/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"developer/src/kmc-kmw/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"developer/src/kmc-kmw/node_modules/supports-color/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"developer/src/kmc-kmw/node_modules/ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"dev": true,
"dependencies": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"typescript": ">=2.7"
}
},
"developer/src/kmc-kmw/node_modules/ts-node/node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"developer/src/kmc-ldml": {
"name": "@keymanapp/kmc-ldml",
"license": "MIT",
@ -2322,6 +2579,10 @@
"resolved": "developer/src/kmc-kmn",
"link": true
},
"node_modules/@keymanapp/kmc-kmw": {
"resolved": "developer/src/kmc-kmw",
"link": true
},
"node_modules/@keymanapp/kmc-ldml": {
"resolved": "developer/src/kmc-ldml",
"link": true

View file

@ -21,6 +21,7 @@
"core/include/ldml",
"developer/src/common/web/test-helpers",
"developer/src/kmc-kmn",
"developer/src/kmc-kmw",
"developer/src/kmc-ldml",
"developer/src/kmc-model",
"developer/src/kmc-model-info",

View file

@ -8,10 +8,14 @@
{ "path": "./core/include/ldml/tsconfig.json" },
//{ "path": "./developer/src/kmc/test/tsconfig.json" },
{ "path": "./developer/src/common/web/test-helpers/tsconfig.json" },
{ "path": "./developer/src/kmc/tsconfig.json" },
{ "path": "./developer/src/kmc-kmn/test/tsconfig.json" },
{ "path": "./developer/src/kmc-kmn/tsconfig.json" },
{ "path": "./developer/src/kmc-kmw/test/tsconfig.json" },
{ "path": "./developer/src/kmc-kmw/tsconfig.json" },
{ "path": "./developer/src/kmc-ldml/test/tsconfig.json" },
{ "path": "./developer/src/kmc-ldml/tsconfig.json" },
{ "path": "./developer/src/kmc-model/test/tsconfig.json" },