diff --git a/developer/src/kmc-kmn/src/compiler/compiler.ts b/developer/src/kmc-kmn/src/compiler/compiler.ts index 86e371f532..97d27bbfa1 100644 --- a/developer/src/kmc-kmn/src/compiler/compiler.ts +++ b/developer/src/kmc-kmn/src/compiler/compiler.ts @@ -17,6 +17,22 @@ export interface CompilerResultFile { data: Uint8Array; }; +// +// Matches kmcmplibapi.h definitions +// + +export const STORETYPE_STORE = 0x01; +export const STORETYPE_RESERVED = 0x02; +export const STORETYPE_OPTION = 0x04; +export const STORETYPE_DEBUG = 0x08; +export const STORETYPE_CALL = 0x10; +export const STORETYPE__MASK = 0x1F; + +export interface CompilerResultExtraStore { + storeType: number; // STORETYPE__MASK + name: string; // when debug=false, the .kmx will not have store names +}; + export const COMPILETARGETS_KMX = 0x01; export const COMPILETARGETS_JS = 0x02; export const COMPILETARGETS__MASK = 0x03; @@ -31,8 +47,13 @@ export interface CompilerResultExtra { targets: number; kvksFilename?: string; displayMapFilename?: string; + stores?: CompilerResultExtraStore[]; }; +// +// Internal in-memory result from a successful compilation +// + export interface CompilerResult { kmx?: CompilerResultFile; kvk?: CompilerResultFile; @@ -163,6 +184,24 @@ export class KmnCompiler implements UnicodeSetParser { return 1; } + private copyWasmResult(wasm_result: any): CompilerResult { + let result: CompilerResult = { + // We cannot Object.assign or {...} on a wasm-defined object, so... + extra: { + targets: wasm_result.extra.targets, + displayMapFilename: wasm_result.extra.displayMapFilename, + kvksFilename: wasm_result.extra.kvksFilename, + stores: [] + }, + displayMap: null + }; + for(let store of wasm_result.extra.stores) { + result.extra.stores.push({storeType: store.storeType, name: store.name}); + } + + return result; + } + public runCompiler(infile: string, outfile: string, options: KmnCompilerOptions): CompilerResult { if(!this.verifyInitialized()) { /* c8 ignore next 2 */ @@ -191,15 +230,7 @@ export class KmnCompiler implements UnicodeSetParser { return null; } - const result: CompilerResult = { - // We cannot Object.assign or {...} on a wasm-defined object, so... - extra: { - targets: wasm_result.extra.targets, - displayMapFilename: wasm_result.extra.displayMapFilename, - kvksFilename: wasm_result.extra.kvksFilename, - }, - displayMap: null - }; + const result: CompilerResult = this.copyWasmResult(wasm_result); if(result.extra.targets & COMPILETARGETS_KMX) { result.kmx = { @@ -234,15 +265,8 @@ export class KmnCompiler implements UnicodeSetParser { if(!wasm_result.result) { return null; } - const kmw_result: CompilerResult = { - extra: { - // We cannot Object.assign or {...} on a wasm-defined object, so... - targets: wasm_result.extra.targets, - displayMapFilename: wasm_result.extra.displayMapFilename, - kvksFilename: wasm_result.extra.kvksFilename, - }, - displayMap: result.displayMap // we can safely re-use the kmx compile displayMap - }; + const kmw_result: CompilerResult = this.copyWasmResult(wasm_result); + kmw_result.displayMap = result.displayMap; // we can safely re-use the kmx compile displayMap let web_kmx = new Uint8Array(Module.HEAP8.buffer, wasm_result.kmx, wasm_result.kmxSize) result.js = this.runWebCompiler(infile, outfile, web_kmx, result.kvk?.data, kmw_result, options); diff --git a/developer/src/kmc-kmn/src/kmw-compiler/constants.ts b/developer/src/kmc-kmn/src/kmw-compiler/constants.ts index 87a59e796c..511ca785a3 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/constants.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/constants.ts @@ -39,8 +39,8 @@ KMXCodeNames: string[] = [ export const USEnglishUnshift: string = ' `' + '1234567890' + '-' + '=' + 'qwertyuiop' + '[' + ']' + '\\' + 'asdfghjkl' + ';' + '\'' + 'zxcvbnm' + ',' + '.' + '/', - USEnglishShift: string = 0xFF + '~' + '!@#$%^&*()' + '_' + '+' + 'QWERTYUIOP' + '{' + '}' + '|' + 'ASDFGHJKL' + ':' + '"' + 'ZXCVBNM' + '<' + '>' + '?', - USEnglishValues: string = 0x20 + 0xc0 + '1234567890' + 0xbd + 0xbb + 'QWERTYUIOP' + 0xdb + 0xdd + 0xdc + 'ASDFGHJKL' + 0xba + 0xde + 'ZXCVBNM' + 0xbc + 0xbe + 0xbf, + USEnglishShift: string = '\u00FF' + '~' + '!@#$%^&*()' + '_' + '+' + 'QWERTYUIOP' + '{' + '}' + '|' + 'ASDFGHJKL' + ':' + '"' + 'ZXCVBNM' + '<' + '>' + '?', + USEnglishValues: string = '\u0020' + '\u00c0' + '1234567890' + '\u00bd' + '\u00bb' + 'QWERTYUIOP' + '\u00db' + '\u00dd' + '\u00dc' + 'ASDFGHJKL' + '\u00ba' + '\u00de' + 'ZXCVBNM' + '\u00bc' + '\u00be' + '\u00bf', UnreachableKeyCodes: number[] = [ // I4141 0x00, // &H0 0x01, //VK_LBUTTON, // &H1 diff --git a/developer/src/kmc-kmn/src/kmw-compiler/write-compiled-keyboard.ts b/developer/src/kmc-kmn/src/kmw-compiler/write-compiled-keyboard.ts index b0665f5c58..a62083c178 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/write-compiled-keyboard.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/write-compiled-keyboard.ts @@ -5,7 +5,7 @@ import { JavaScript_ContextMatch, JavaScript_KeyAsString, JavaScript_Name, JavaS import { KmwCompilerMessages } from "./messages.js"; import { ValidateLayoutFile } from "./validate-layout-file.js"; import { VisualKeyboardFromFile } from "./visual-keyboard-compiler.js"; -import { CompilerResult } from "src/compiler/compiler.js"; +import { CompilerResult, STORETYPE_DEBUG, STORETYPE_OPTION, STORETYPE_RESERVED } from "../compiler/compiler.js"; function requote(s: string): string { return "'" + s.replaceAll(/(['\\])/, "\\$1") + "'"; @@ -34,7 +34,14 @@ export function RequotedString(s: string, RequoteSingleQuotes: boolean = false): return s; } -export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: string, keyboardData: Uint8Array, kvkData: Uint8Array, kmxResult: CompilerResult, FDebug: boolean = false): string { +export function WriteCompiledKeyboard( + callbacks: CompilerCallbacks, + kmnfile: string, + keyboardData: Uint8Array, + kvkData: Uint8Array, + kmxResult: CompilerResult, + FDebug: boolean = false +): string { let opts: CompilerOptions = { shouldAddCompilerVersion: false, saveDebug: FDebug @@ -217,18 +224,10 @@ export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: str 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; - } + const isStoreType = (index:number, type: number) => !!(kmxResult.extra.stores[index].storeType & type); + const isDebugStore = (index: number) => isStoreType(index, STORETYPE_DEBUG); + const isReservedStore = (index: number) => isStoreType(index, STORETYPE_RESERVED); + const isOptionStore = (index: number) => isStoreType(index, STORETYPE_OPTION); // Write the stores out FOptionStores = ''; @@ -236,7 +235,7 @@ export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: str 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 (!isDebugStore(i)) { // 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}`; } @@ -245,14 +244,14 @@ export function WriteCompiledKeyboard(callbacks: CompilerCallbacks, kmnfile: str } //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)) { + else if (isOptionStore(i) && !isReservedStore(i)) { 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)}`; + 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 diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/build_fixtures.bat b/developer/src/kmc-kmn/test/fixtures/kmw/build_fixtures.bat new file mode 100644 index 0000000000..1eee334a3a --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/build_fixtures.bat @@ -0,0 +1,17 @@ +@echo off +echo Compiles the keyboards using the legacy kmcomp.exe +echo to use as baseline comparisons for kmc kmw module +rem we run for the .kmx first even though we don't use them because the legacy +rem cmdline web compiler has a bit of an issue generating the VK properly if we +rem build the .kmn directly and haven't already built the .kmx (which generates +rem the .kvk that the web compiler is expecting). This doesn't happen in project +rem mode because the .kmx is always built first. Probably won't fix this because +rem legacy compiler is being replaced anyway. +for %%d in (*.kmn) do ..\..\..\..\..\bin\kmcomp -no-compiler-version -d %%d %%~nd.kmx +for %%d in (*.kmn) do ..\..\..\..\..\bin\kmcomp -no-compiler-version -d %%d %%~nd.js +rem ok, we no longer need .kmx and .kvk once we've built the .js fixtures +del *.kmx +rem We can't do `del *.kvk` because that also deletes *.kvks, thanks 8.3 +rem filenames! But the following horror works: +for %%d in (*.kvk) do if %%~xd == .kvk del %%d + diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.js b/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.js new file mode 100644 index 0000000000..bb410ea941 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.js @@ -0,0 +1,699 @@ +if(typeof keyman === 'undefined') { + console.log('Keyboard requires KeymanWeb 10.0 or later'); + if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later"); +} else { +KeymanWeb.KR(new Keyboard_test_keychars()); +} +function Keyboard_test_keychars() +{ + var modCodes = keyman.osk.modifierCodes; + var keyCodes = keyman.osk.keyCodes; + + this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9; + this.KI="Keyboard_test_keychars"; + this.KN="test_keychars"; + this.KMINVER="10.0"; + this.KV=null; + this.KDU=0; + this.KH=''; + this.KM=0; + this.KBVER="1.0"; + this.KMBM=modCodes.SHIFT /* 0x0010 */; + this.s_letter_6=" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + this.KVS=[]; + this.gs=function(t,e) { + return this.g_Main_0(t,e); + }; + this.gs=function(t,e) { + return this.g_Main_0(t,e); + }; + this.g_Main_0=function(t,e) { + var k=KeymanWeb,r=0,m=0; + if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_SPACE /* 0x20 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"- -"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_1 /* 0x31 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-!-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_QUOTE /* 0xDE */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-\"-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_3 /* 0x33 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-#-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_4 /* 0x34 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-$-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_5 /* 0x35 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-%-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_7 /* 0x37 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-&-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_QUOTE /* 0xDE */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-'-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_9 /* 0x39 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-(-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_0 /* 0x30 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-)-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_8 /* 0x38 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-*-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_EQUAL /* 0xBB */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-+-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_COMMA /* 0xBC */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-,-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_HYPHEN /* 0xBD */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"---"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_PERIOD /* 0xBE */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-.-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_SLASH /* 0xBF */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-/-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-0-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-1-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_2 /* 0x32 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-2-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_3 /* 0x33 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-3-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_4 /* 0x34 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-4-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_5 /* 0x35 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-5-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_6 /* 0x36 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-6-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_7 /* 0x37 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-7-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_8 /* 0x38 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-8-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_9 /* 0x39 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-9-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_COLON /* 0xBA */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-:-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_COLON /* 0xBA */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-;-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_COMMA /* 0xBC */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-<-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_EQUAL /* 0xBB */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-=-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_PERIOD /* 0xBE */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"->-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_SLASH /* 0xBF */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-?-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_2 /* 0x32 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-@-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_A /* 0x41 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-A-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_B /* 0x42 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-B-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_C /* 0x43 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-C-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_D /* 0x44 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-D-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_E /* 0x45 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-E-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_F /* 0x46 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-F-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_G /* 0x47 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-G-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_H /* 0x48 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-H-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_I /* 0x49 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-I-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_J /* 0x4A */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-J-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_K /* 0x4B */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-K-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_L /* 0x4C */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-L-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_M /* 0x4D */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-M-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_N /* 0x4E */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-N-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_O /* 0x4F */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-O-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_P /* 0x50 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-P-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_Q /* 0x51 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-Q-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_R /* 0x52 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-R-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_S /* 0x53 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-S-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_T /* 0x54 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-T-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_U /* 0x55 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-U-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_V /* 0x56 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-V-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_W /* 0x57 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-W-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_X /* 0x58 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-X-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_Y /* 0x59 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-Y-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_Z /* 0x5A */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-Z-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_LBRKT /* 0xDB */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-[-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_BKSLASH /* 0xDC */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-\\-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_RBRKT /* 0xDD */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-]-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_6 /* 0x36 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-^-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_HYPHEN /* 0xBD */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-_-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_BKQUOTE /* 0xC0 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-`-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-a-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_B /* 0x42 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-b-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_C /* 0x43 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-c-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_D /* 0x44 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-d-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_E /* 0x45 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-e-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_F /* 0x46 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-f-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_G /* 0x47 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-g-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_H /* 0x48 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-h-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_I /* 0x49 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-i-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_J /* 0x4A */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-j-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_K /* 0x4B */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-k-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_L /* 0x4C */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-l-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_M /* 0x4D */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-m-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_N /* 0x4E */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-n-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_O /* 0x4F */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-o-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_P /* 0x50 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-p-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Q /* 0x51 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-q-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_R /* 0x52 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-r-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_S /* 0x53 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-s-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_T /* 0x54 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-t-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_U /* 0x55 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-u-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_V /* 0x56 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-v-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_W /* 0x57 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-w-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_X /* 0x58 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-x-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Y /* 0x59 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-y-"); + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_Z /* 0x5A */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-z-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_LBRKT /* 0xDB */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-{-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_BKSLASH /* 0xDC */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-|-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_RBRKT /* 0xDD */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-}-"); + } + } + else if(k.KKM(e, modCodes.SHIFT | modCodes.VIRTUAL_KEY /* 0x4010 */, keyCodes.K_BKQUOTE /* 0xC0 */)) { + if(1){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"-~-"); + } + } + return r; + }; +} diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.kmn b/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.kmn new file mode 100644 index 0000000000..0751b7fc28 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/test_keychars.kmn @@ -0,0 +1,14 @@ +c Verifies every letter on the US English keyboard, ensuring +c that our mapping tables are correct + +store(&NAME) 'test_keychars' +store(&version) '10.0' +store(&targets) 'any' + +begin Unicode > use(Main) + +group(Main) using keys + +store(letter) ' !"#$%&' "'" '()*+,-./' '0'..'9' ':;<=>?@' 'A'..'Z' '[\]^_`' 'a'..'z' '{|}~' + ++ any(letter) > '-' index(letter, 1) '-' diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/test_options.js b/developer/src/kmc-kmn/test/fixtures/kmw/test_options.js new file mode 100644 index 0000000000..2cc9b65619 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/test_options.js @@ -0,0 +1,64 @@ +if(typeof keyman === 'undefined') { + console.log('Keyboard requires KeymanWeb 10.0 or later'); + if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later"); +} else { +KeymanWeb.KR(new Keyboard_test_options()); +} +function Keyboard_test_options() +{ + var modCodes = keyman.osk.modifierCodes; + var keyCodes = keyman.osk.keyCodes; + + this._v=(typeof keyman!="undefined"&&typeof keyman.version=="string")?parseInt(keyman.version,10):9; + this.KI="Keyboard_test_options"; + this.KN="test_options"; + this.KMINVER="10.0"; + this.KV=null; + this.KDU=0; + this.KH=''; + this.KM=0; + this.KBVER="1.0"; + this.KMBM=0 /* 0x0000 */; + this.s_foo_5=KeymanWeb.KLOAD(this.KI,"foo","0"); + this.s8="1"; + this.s9="0"; + this.s10="1"; + this.s11="0"; + this.KVS=['s_foo_5']; + this.gs=function(t,e) { + return this.g_Main_0(t,e); + }; + this.gs=function(t,e) { + return this.g_Main_0(t,e); + }; + this.g_Main_0=function(t,e) { + var k=KeymanWeb,r=0,m=0; + if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_0 /* 0x30 */)) { + if(1){ + r=m=1; // Line 16 + k.KDC(0,t); + this.s_foo_5=this.s11; + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_1 /* 0x31 */)) { + if(1){ + r=m=1; // Line 15 + k.KDC(0,t); + this.s_foo_5=this.s10; + } + } + else if(k.KKM(e, modCodes.VIRTUAL_KEY /* 0x4000 */, keyCodes.K_A /* 0x41 */)) { + if(this.s_foo_5===this.s8){ + r=m=1; // Line 13 + k.KDC(0,t); + k.KO(-1,t,"foo."); + } + else if(this.s_foo_5===this.s9){ + r=m=1; // Line 14 + k.KDC(0,t); + k.KO(-1,t,"no foo."); + } + } + return r; + }; +} diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/test_options.kmn b/developer/src/kmc-kmn/test/fixtures/kmw/test_options.kmn new file mode 100644 index 0000000000..8f08280614 --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/test_options.kmn @@ -0,0 +1,16 @@ +c Adapted from common baseline test keyboard k_021__options.kmn + +store(&NAME) 'test_options' +store(&version) '10.0' +store(&targets) 'any' + +store(foo) '0' + +begin Unicode > use(Main) + +group(Main) using keys + +if(foo = '1') + 'a' > 'foo.' +if(foo = '0') + 'a' > 'no foo.' ++ '1' > set(foo = '1') ++ '0' > set(foo = '0') diff --git a/developer/src/kmc-kmn/test/kmw/test-compiler-manual.ts b/developer/src/kmc-kmn/test/kmw/test-compiler-manual.ts deleted file mode 100644 index f934b97231..0000000000 --- a/developer/src/kmc-kmn/test/kmw/test-compiler-manual.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import fs from 'fs'; -import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { KmnCompiler } from '../../src/compiler/compiler.js'; -import { extractTouchLayout } from './util.js'; - -const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/'); -const fixturesDir = __dirname + '/../../../test/fixtures/kmw/'; -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); -} - -let result = kmnCompiler.runCompiler(infile, outfile, { - shouldAddCompilerVersion: false, - saveDebug: true, // TODO: we should probably use passed debug flag -}); - -if(!result) { - callbacks.printMessages(); - process.exit(1); -} - -const js = new TextDecoder().decode(result.js.data); - -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); -} diff --git a/developer/src/kmc-kmn/test/kmw/test-compiler.ts b/developer/src/kmc-kmn/test/kmw/test-compiler.ts deleted file mode 100644 index d0d059ef2c..0000000000 --- a/developer/src/kmc-kmn/test/kmw/test-compiler.ts +++ /dev/null @@ -1,60 +0,0 @@ -import 'mocha'; -import { assert } from 'chai'; -// import sinonChai from 'sinon-chai'; -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; -import fs from 'fs'; -import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; -import { KmnCompiler } from '../../src/compiler/compiler.js'; -import { extractTouchLayout } from './util.js'; - -const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/'); -const fixturesDir = __dirname + '/../../../test/fixtures/kmw/'; -//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)); - - let result = kmnCompiler.runCompiler(infile, outfile, { - shouldAddCompilerVersion: false, - saveDebug: true, // TODO: we should probably use passed debug flag - }); - - assert.isNotNull(result); - - const js = new TextDecoder().decode(result.js.data); - - 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)); - }); -}); \ No newline at end of file diff --git a/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts new file mode 100644 index 0000000000..04962d023d --- /dev/null +++ b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts @@ -0,0 +1,92 @@ +import 'mocha'; +import { assert } from 'chai'; +// import sinonChai from 'sinon-chai'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import fs from 'fs'; +import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers'; +import { CompilerResult, KmnCompiler } from '../../src/compiler/compiler.js'; +import { ETLResult, extractTouchLayout as parseWebTestResult } from './util.js'; +import { KeymanFileTypes } from '@keymanapp/common-types'; + +const __dirname = dirname(fileURLToPath(import.meta.url)).replace(/\\/g, '/'); +const fixturesDir = __dirname + '/../../../test/fixtures/kmw/'; +//const baselineDir = __dirname + '/../../../../../common/test/keyboards/baseline/'; +// chai.use(sinonChai); + +const debug=false; + +const generateTestFilenames = (id: string) => ({ + fixture: fixturesDir + id + KeymanFileTypes.Binary.WebKeyboard, + source: fixturesDir + id + KeymanFileTypes.Source.KeymanKeyboard, + intermediate: fixturesDir + id + KeymanFileTypes.Binary.Keyboard, + binary: fixturesDir + id + '.test' + KeymanFileTypes.Binary.WebKeyboard +}); + +describe('KeymanWeb Compiler', function() { + const callbacks = new TestCompilerCallbacks(); + const kmnCompiler: KmnCompiler = new KmnCompiler(); + + this.beforeAll(async function() { + assert.isTrue(await kmnCompiler.init(callbacks)); + }); + + this.afterEach(function() { + callbacks.printMessages(); + callbacks.clear(); + }); + + it('should compile a complex keyboard', async function() { + run_test_keyboard(kmnCompiler, 'khmer_angkor'); + }); + + it('should handle option stores', async function() { + // + // This is enough to verify that the option store is set appropriately with + // KLOAD because the fixture has that code present: + // + // this.s_foo_6=KeymanWeb.KLOAD(this.KI,"foo","0"); + // + run_test_keyboard(kmnCompiler, 'test_options'); + }); + + it('should translate every "character style" key correctly', async function() { + // + // This is enough to verify that every character style key is encoded in the + // same way as the fixture. + // + run_test_keyboard(kmnCompiler, 'test_keychars'); + }); +}); + + +function run_test_keyboard(kmnCompiler: KmnCompiler, id: string): { result: CompilerResult, actualCode: string, actual: ETLResult, expectedCode: string, expected: ETLResult } { + const filenames = generateTestFilenames(id); + + let result = kmnCompiler.runCompiler(filenames.source, filenames.intermediate, { + shouldAddCompilerVersion: false, + saveDebug: true, + }); + assert.isNotNull(result); + + let value = { + result, + actualCode: new TextDecoder().decode(result.js.data), + expectedCode: fs.readFileSync(filenames.fixture, 'utf8'), + expected: null, + actual: null, + }; + value.actual = parseWebTestResult(value.actualCode); + value.expected = parseWebTestResult(value.expectedCode); + assert.deepEqual(value.actual.js, value.expected.js); + assert.deepEqual(JSON.parse(value.actual.touchLayout), JSON.parse(value.expected.touchLayout)); + + if(debug) { + // This is mostly to allow us to verify that parseWebTestResult is doing what we want + fs.writeFileSync(filenames.binary + '.strip.js', value.actual.js); + fs.writeFileSync(filenames.fixture + '.strip.js', value.expected.js); + fs.writeFileSync(filenames.binary, value.actualCode); + } + + return value; +} diff --git a/developer/src/kmc-kmn/test/kmw/util.ts b/developer/src/kmc-kmn/test/kmw/util.ts index f63e8dd26e..37d89cdf7b 100644 --- a/developer/src/kmc-kmn/test/kmw/util.ts +++ b/developer/src/kmc-kmn/test/kmw/util.ts @@ -1,5 +1,5 @@ -interface ETLResult { +export interface ETLResult { js: string; touchLayout: string; } @@ -7,7 +7,10 @@ interface ETLResult { export function extractTouchLayout(js: string): ETLResult|null { let m = /KVKL=(?.+?);[\r\n]/ds.exec(js); if(!m) { - return null; + return { + js: js, + touchLayout: 'null' + }; } let kvkl = (m).indices.groups.kvkl; diff --git a/developer/src/kmcmplib/include/kmcmplibapi.h b/developer/src/kmcmplib/include/kmcmplibapi.h index ee9b6cccc7..6252c661f9 100644 --- a/developer/src/kmcmplib/include/kmcmplibapi.h +++ b/developer/src/kmcmplib/include/kmcmplibapi.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __EMSCRIPTEN__ #include @@ -30,6 +31,22 @@ struct KMCMP_COMPILER_OPTIONS { int target; // CKF_KEYMAN, CKF_KEYMANWEB }; +// +// Additional metadata passed sideband from compiler result +// + +#define STORETYPE_STORE 0x01 +#define STORETYPE_RESERVED 0x02 +#define STORETYPE_OPTION 0x04 +#define STORETYPE_DEBUG 0x08 +#define STORETYPE_CALL 0x10 +#define STORETYPE__MASK 0x1F + +struct KMCMP_COMPILER_RESULT_EXTRA_STORE { + int storeType; // STORETYPE__MASK + std::string name; // when debug=false, the .kmx will not have store names +}; + #define COMPILETARGETS_KMX 0x01 #define COMPILETARGETS_JS 0x02 #define COMPILETARGETS__MASK 0x03 @@ -39,6 +56,7 @@ struct KMCMP_COMPILER_RESULT_EXTRA { std::string kmnFilename; std::string kvksFilename; std::string displayMapFilename; + std::vector stores; }; struct KMCMP_COMPILER_RESULT { diff --git a/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp index fdbed181bc..679e36fc0d 100644 --- a/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp +++ b/developer/src/kmcmplib/src/CompileKeyboardBuffer.cpp @@ -7,6 +7,10 @@ #include "CompileKeyboardBuffer.h" #include "../../../../common/windows/cpp/include/keymanversion.h" +namespace kmcmp { + void CopyExtraData(PFILE_KEYBOARD fk); +}; + bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk) { PKMX_WCHAR str, p; @@ -45,6 +49,7 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk) fk->extra->targets = COMPILETARGETS_KMX; fk->extra->kvksFilename = ""; fk->extra->displayMapFilename = ""; + fk->extra->stores.clear(); /* fk->szMessage[0] = 0; fk->szLanguageName[0] = 0;*/ fk->dwBitmapSize = 0; @@ -158,5 +163,26 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk) /* Flag presence of deprecated features */ kmcmp::CheckForDeprecatedFeatures(fk); + /* Extract extra metadata for callers */ + kmcmp::CopyExtraData(fk); + return TRUE; } + +namespace kmcmp { + void CopyExtraData(PFILE_KEYBOARD fk) { + /* Copy stores */ + PFILE_STORE store = fk->dpStoreArray; + for(int i = 0; i < fk->cxStoreArray; i++, store++) { + KMCMP_COMPILER_RESULT_EXTRA_STORE extraStore; + extraStore.storeType = + (store->fIsStore ? STORETYPE_STORE : 0) | + (store->fIsReserved ? STORETYPE_RESERVED : 0) | + (store->fIsOption ? STORETYPE_OPTION : 0) | + (store->fIsDebug ? STORETYPE_DEBUG : 0) | + (store->fIsCall ? STORETYPE_CALL : 0); + extraStore.name = string_from_u16string(store->szName); + fk->extra->stores.push_back(extraStore); + } + } +} \ No newline at end of file diff --git a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp index 805796db3b..5e7cbc4a1c 100644 --- a/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp +++ b/developer/src/kmcmplib/src/CompilerInterfacesWasm.cpp @@ -79,6 +79,38 @@ WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPI return r; } +// This little bit of magic gives us implicit bindings for any `std::vector`, +// so long as we bind `emscripten::value_object`, and comes from: +// https://github.com/emscripten-core/emscripten/issues/11070#issuecomment-717675128 +namespace emscripten { +namespace internal { + +template +struct BindingType> { + using ValBinding = BindingType; + using WireType = ValBinding::WireType; + + static WireType toWireType(const std::vector &vec) { + return ValBinding::toWireType(val::array(vec)); + } + + static std::vector fromWireType(WireType value) { + return vecFromJSArray(ValBinding::fromWireType(value)); + } +}; + +template +struct TypeID::type, + std::vector::type::value_type, + typename Canonicalized::type::allocator_type>>::value>> { + static constexpr TYPEID get() { return TypeID::get(); } +}; + +} // namespace internal +} // namespace emscripten + EMSCRIPTEN_BINDINGS(compiler_interface) { emscripten::class_("CompilerOptions") @@ -109,6 +141,12 @@ EMSCRIPTEN_BINDINGS(compiler_interface) { .property("kmnFilename", &KMCMP_COMPILER_RESULT_EXTRA::kmnFilename) .property("kvksFilename", &KMCMP_COMPILER_RESULT_EXTRA::kvksFilename) .property("displayMapFilename", &KMCMP_COMPILER_RESULT_EXTRA::displayMapFilename) + .property("stores", &KMCMP_COMPILER_RESULT_EXTRA::stores) + ; + + emscripten::value_object("CompilerResultExtraStore") + .field("storeType", &KMCMP_COMPILER_RESULT_EXTRA_STORE::storeType) + .field("name", &KMCMP_COMPILER_RESULT_EXTRA_STORE::name) ; emscripten::function("kmcmp_compile", &kmcmp_wasm_compile);