Merge remote-tracking branch 'origin/master' into fv-all/keyboards-json

This commit is contained in:
Darcy Wong 2024-08-05 07:41:29 +07:00
commit 5fca4c7d85
71 changed files with 1917 additions and 1575 deletions

View file

@ -1,5 +1,19 @@
# Keyman Version History
## 18.0.81 alpha 2024-08-03
* chore(developer): Revert "chore(developer): remove redundant references from tsconfig.json" (#12076)
* chore(common): use nvm in builds to select a node version automatically (#12069)
* refactor(developer): move functions and variables into CompilerErrors.cpp (#12030)
* refactor(developer): improve the callbacks for kmcmplib (#12031)
* refactor(developer): align kmcmplib error codes and names with kmc-kmn (#12044)
* refactor(developer): move error reporting to error site in kmcmplib (part 1) (#12045)
* refactor(developer): move error reporting inside `GetCompileTargetsFromTargetsStore` and `ProcessGroupFinish` (#12048)
* refactor(developer): move kmcmplib message construction to kmc-kmn (#12059)
* refactor(developer): rename compiler error reporting functions in kmcmplib (#12060)
* chore(developer): support filename field in kmcmplib compiler messages (#12061)
* refactor(developer): replace `VERIFY_KEYBOARD_VERSION()` calls with `VerifyKeyboardVersion()` (#12063)
## 18.0.80 alpha 2024-07-31
* chore(developer): remove redundant references from tsconfig.json (#12037)

View file

@ -1 +1 @@
18.0.81
18.0.82

View file

@ -29,7 +29,6 @@ const
SZMAX_GROUPNAME = 80;
SZMAX_DEADKEYNAME = 80;
SZMAX_VKDICTIONARYNAME = 80; // I3438
SZMAX_ERRORTEXT = 512;
SZMAX_LANGUAGENAME = 80;
SZMAX_KEYBOARDNAME = 80;

View file

@ -23,21 +23,38 @@
#ifndef _kmn_compiler_errors_h
#define _kmn_compiler_errors_h
// Compiler Error Masks
// Compiler Error Masks -- matches compiler-interfaces.ts
#define CERR_SEVERITY_MASK 0x0000F000
#define CERR_MESSAGE_MASK 0x00000FFF
#define CERR_MASK 0x0000FFFF
namespace CompilerErrorSeverity {
// We use a namespace to stop the enum names leaking out into global namespace scope
enum {
Info = 0x000000, // Informational, not necessarily a problem
Hint = 0x100000, // Something the user might want to be aware of
Warn = 0x200000, // Warning: Not great, but we can keep going.
Error = 0x300000, // Severe error where we can't continue
Fatal = 0x400000, // OOM or should-not-happen internal problem
};
};
// Severity codes
//
// Note: these may not be combined, and are not a bitmask, for historical
// reasons they are separate bits
#define CERR_FATAL 0x00008000
#define CERR_ERROR 0x00004000
#define CERR_WARNING 0x00002000
#define CERR_HINT 0x00001000
#define CERR_INFO 0x00000000
#define MESSAGE_SEVERITY_MASK 0x00F00000 // includes reserved bits, 16 possible severity levels
#define MESSAGE_ERROR_MASK 0x000FFFFF // error | namespace
#define MESSAGE_NAMESPACE_MASK 0x000FF000 // 256 possible namespaces
#define MESSAGE_BASEERROR_MASK 0x00000FFF // error code, 2,048 possible error codes per namespace
#define MESSAGE_RESERVED_MASK 0xFF000000 // do not use these error values at this time
#define MESSAGE_NAMESPACE_KmnCompiler 0x2000
// Severity codes + namespace for shorthand use
#define SevFatal (MESSAGE_NAMESPACE_KmnCompiler | CompilerErrorSeverity::Fatal)
#define SevError (MESSAGE_NAMESPACE_KmnCompiler | CompilerErrorSeverity::Error)
#define SevWarn (MESSAGE_NAMESPACE_KmnCompiler | CompilerErrorSeverity::Warn)
#define SevHint (MESSAGE_NAMESPACE_KmnCompiler | CompilerErrorSeverity::Hint)
#define SevInfo (MESSAGE_NAMESPACE_KmnCompiler | CompilerErrorSeverity::Info)
// These two values are not true error codes; they are used as
// return values in kmcmplib and are never passed to kmc-kmn
#define STATUS_Success 0x00000000 // NOTE: Not a message code
#define STATUS_EndOfFile 0x00000001 // NOTE: Not a message code
// Message codes
//
@ -46,215 +63,216 @@
// sure to update that file also. Note that this correlation is currently
// maintained manually. All values must be below 0x1000 (exclusive of severity
// code).
#define CERR_None 0x00000000
#define CERR_EndOfFile 0x00000001
#define CERR_BadCallParams 0x00008002 // TODO: rename to CFATAL_
#define CERR_CannotAllocateMemory 0x00008004 // TODO: rename to CFATAL_
#define CERR_InfileNotExist 0x00004005 // #10678: reduced from fatal to error in 17.0
// #define CERR_CannotCreateOutfile 0x00004006 // #10678: reduced from fatal to error in 17.0, but unused
#define CERR_UnableToWriteFully 0x00008007 // TODO: rename to CFATAL_
#define CERR_CannotReadInfile 0x00004008 // #10678: reduced from fatal to error in 17.0
#define CERR_SomewhereIGotItWrong 0x00008009 // TODO: rename to CFATAL_
namespace KmnCompilerMessages {
enum {
FATAL_BadCallParams = SevFatal | 0x002,
FATAL_CannotAllocateMemory = SevFatal | 0x004,
ERROR_InfileNotExist = SevError | 0x005, // #10678: reduced from fatal to error in 17.0
// ERROR_CannotCreateOutfile = SevError | 0x006, // #10678: reduced from fatal to error in 17.0, but unused
// FATAL_UnableToWriteFully = SevFatal | 0x007, // unused
ERROR_CannotReadInfile = SevError | 0x008, // #10678: reduced from fatal to error in 17.0
FATAL_SomewhereIGotItWrong = SevFatal | 0x009,
#define CERR_InvalidToken 0x0000400A
#define CERR_InvalidBegin 0x0000400B
#define CERR_InvalidName 0x0000400C
#define CERR_InvalidVersion 0x0000400D
#define CERR_InvalidLanguageLine 0x0000400E
#define CERR_LayoutButNoLanguage 0x0000400F
#define CERR_InvalidLayoutLine 0x00004010
#define CERR_NoVersionLine 0x00004011
#define CERR_InvalidGroupLine 0x00004012
#define CERR_InvalidStoreLine 0x00004013
#define CERR_InvalidCodeInKeyPartOfRule 0x00004014
#define CERR_InvalidDeadkey 0x00004015
#define CERR_InvalidValue 0x00004016
#define CERR_ZeroLengthString 0x00004017
#define CERR_TooManyIndexToKeyRefs 0x00004018
#define CERR_UnterminatedString 0x00004019
#define CERR_StringInVirtualKeySection 0x0000401A
#define CERR_AnyInVirtualKeySection 0x0000401B
#define CERR_InvalidAny 0x0000401C
#define CERR_StoreDoesNotExist 0x0000401D
#define CERR_BeepInVirtualKeySection 0x0000401E
#define CERR_IndexInVirtualKeySection 0x0000401F
#define CERR_InvalidIndex 0x00004020
#define CERR_OutsInVirtualKeySection 0x00004021
#define CERR_InvalidOuts 0x00004022
#define CERR_ContextInVirtualKeySection 0x00004024
#define CERR_InvalidUse 0x00004025
#define CERR_GroupDoesNotExist 0x00004026
#define CERR_VirtualKeyNotAllowedHere 0x00004027
#define CERR_InvalidSwitch 0x00004028
#define CERR_NoTokensFound 0x00004029
#define CERR_InvalidLineContinuation 0x0000402A
#define CERR_LineTooLong 0x0000402B
#define CERR_InvalidCopyright 0x0000402C
#define CERR_CodeInvalidInThisSection 0x0000402D
#define CERR_InvalidMessage 0x0000402E
#define CERR_InvalidLanguageName 0x0000402F
#define CERR_InvalidBitmapLine 0x00004030
#define CERR_CannotReadBitmapFile 0x00004031
#define CERR_IndexDoesNotPointToAny 0x00004032
#define CERR_ReservedCharacter 0x00004033
#define CERR_InvalidCharacter 0x00004034
#define CERR_InvalidCall 0x00004035
#define CERR_CallInVirtualKeySection 0x00004036
#define CERR_CodeInvalidInKeyStore 0x00004037
#define CERR_CannotLoadIncludeFile 0x00004038
ERROR_InvalidToken = SevError | 0x00A,
ERROR_InvalidBegin = SevError | 0x00B,
ERROR_InvalidName = SevError | 0x00C,
ERROR_InvalidVersion = SevError | 0x00D,
ERROR_InvalidLanguageLine = SevError | 0x00E,
ERROR_LayoutButNoLanguage = SevError | 0x00F,
ERROR_InvalidLayoutLine = SevError | 0x010,
ERROR_NoVersionLine = SevError | 0x011,
ERROR_InvalidGroupLine = SevError | 0x012,
ERROR_InvalidStoreLine = SevError | 0x013,
ERROR_InvalidCodeInKeyPartOfRule = SevError | 0x014,
ERROR_InvalidDeadkey = SevError | 0x015,
ERROR_InvalidValue = SevError | 0x016,
ERROR_ZeroLengthString = SevError | 0x017,
ERROR_TooManyIndexToKeyRefs = SevError | 0x018,
ERROR_UnterminatedString = SevError | 0x019,
ERROR_StringInVirtualKeySection = SevError | 0x01A,
ERROR_AnyInVirtualKeySection = SevError | 0x01B,
ERROR_InvalidAny = SevError | 0x01C,
ERROR_StoreDoesNotExist = SevError | 0x01D,
ERROR_BeepInVirtualKeySection = SevError | 0x01E,
ERROR_IndexInVirtualKeySection = SevError | 0x01F,
ERROR_InvalidIndex = SevError | 0x020,
ERROR_OutsInVirtualKeySection = SevError | 0x021,
ERROR_InvalidOuts = SevError | 0x022,
ERROR_ContextInVirtualKeySection = SevError | 0x024,
ERROR_InvalidUse = SevError | 0x025,
ERROR_GroupDoesNotExist = SevError | 0x026,
ERROR_VirtualKeyNotAllowedHere = SevError | 0x027,
ERROR_InvalidSwitch = SevError | 0x028,
ERROR_NoTokensFound = SevError | 0x029,
ERROR_InvalidLineContinuation = SevError | 0x02A,
ERROR_LineTooLong = SevError | 0x02B,
ERROR_InvalidCopyright = SevError | 0x02C,
ERROR_CodeInvalidInThisSection = SevError | 0x02D,
ERROR_InvalidMessage = SevError | 0x02E,
ERROR_InvalidLanguageName = SevError | 0x02F,
ERROR_InvalidBitmapLine = SevError | 0x030,
ERROR_CannotReadBitmapFile = SevError | 0x031,
ERROR_IndexDoesNotPointToAny = SevError | 0x032,
ERROR_ReservedCharacter = SevError | 0x033,
ERROR_InvalidCharacter = SevError | 0x034,
ERROR_InvalidCall = SevError | 0x035,
ERROR_CallInVirtualKeySection = SevError | 0x036,
ERROR_CodeInvalidInKeyStore = SevError | 0x037,
ERROR_CannotLoadIncludeFile = SevError | 0x038,
#define CERR_60FeatureOnly_EthnologueCode 0x00004039
#define CERR_60FeatureOnly_MnemonicLayout 0x0000403A
#define CERR_60FeatureOnly_OldCharPosMatching 0x0000403B
#define CERR_60FeatureOnly_NamedCodes 0x0000403C
#define CERR_60FeatureOnly_Contextn 0x0000403D
#define CERR_501FeatureOnly_Call 0x0000403E
ERROR_60FeatureOnly_EthnologueCode = SevError | 0x039,
ERROR_60FeatureOnly_MnemonicLayout = SevError | 0x03A,
ERROR_60FeatureOnly_OldCharPosMatching = SevError | 0x03B,
ERROR_60FeatureOnly_NamedCodes = SevError | 0x03C,
ERROR_60FeatureOnly_Contextn = SevError | 0x03D,
ERROR_501FeatureOnly_Call = SevError | 0x03E,
#define CERR_InvalidNamedCode 0x0000403F
#define CERR_InvalidSystemStore 0x00004040
ERROR_InvalidNamedCode = SevError | 0x03F,
ERROR_InvalidSystemStore = SevError | 0x040,
#define CERR_60FeatureOnly_VirtualCharKey 0x00004044
ERROR_60FeatureOnly_VirtualCharKey = SevError | 0x044,
#define CERR_VersionAlreadyIncluded 0x00004045
ERROR_VersionAlreadyIncluded = SevError | 0x045,
#define CERR_70FeatureOnly 0x00004046
ERROR_70FeatureOnly = SevError | 0x046,
#define CERR_80FeatureOnly 0x00004047
#define CERR_InvalidInVirtualKeySection 0x00004048
#define CERR_InvalidIf 0x00004049
#define CERR_InvalidReset 0x0000404A
#define CERR_InvalidSet 0x0000404B
#define CERR_InvalidSave 0x0000404C
ERROR_80FeatureOnly = SevError | 0x047,
ERROR_InvalidInVirtualKeySection = SevError | 0x048,
ERROR_InvalidIf = SevError | 0x049,
ERROR_InvalidReset = SevError | 0x04A,
ERROR_InvalidSet = SevError | 0x04B,
ERROR_InvalidSave = SevError | 0x04C,
#define CERR_InvalidEthnologueCode 0x0000404D
ERROR_InvalidEthnologueCode = SevError | 0x04D,
#define CERR_CannotCreateTempfile 0x0000804E // TODO: rename to CFATAL_
FATAL_CannotCreateTempfile = SevFatal | 0x04E,
#define CERR_90FeatureOnly_IfSystemStores 0x0000404F
#define CERR_IfSystemStore_NotFound 0x00004050
#define CERR_90FeatureOnly_SetSystemStores 0x00004051
#define CERR_SetSystemStore_NotFound 0x00004052
#define CERR_90FeatureOnlyVirtualKeyDictionary 0x00004053
ERROR_90FeatureOnly_IfSystemStores = SevError | 0x04F,
ERROR_IfSystemStore_NotFound = SevError | 0x050,
ERROR_90FeatureOnly_SetSystemStores = SevError | 0x051,
ERROR_SetSystemStore_NotFound = SevError | 0x052,
ERROR_90FeatureOnlyVirtualKeyDictionary = SevError | 0x053,
#define CERR_NotSupportedInKeymanWebContext 0x00004054
#define CERR_NotSupportedInKeymanWebOutput 0x00004055
#define CERR_NotSupportedInKeymanWebStore 0x00004056
#define CERR_VirtualCharacterKeysNotSupportedInKeymanWeb 0x00004057
#define CERR_VirtualKeysNotValidForMnemonicLayouts 0x00004058
#define CERR_InvalidTouchLayoutFile 0x00004059
#define CERR_TouchLayoutInvalidIdentifier 0x0000405A
#define CERR_InvalidKeyCode 0x0000405B
#define CERR_90FeatureOnlyLayoutFile 0x0000405C
#define CERR_90FeatureOnlyKeyboardVersion 0x0000405D
#define CERR_KeyboardVersionFormatInvalid 0x0000405E
#define CERR_ContextExHasInvalidOffset 0x0000405F
#define CERR_90FeatureOnlyEmbedCSS 0x00004060
#define CERR_90FeatureOnlyTargets 0x00004061
#define CERR_ContextAndIndexInvalidInMatchNomatch 0x00004062
#define CERR_140FeatureOnlyContextAndNotAnyWeb 0x00004063
ERROR_NotSupportedInKeymanWebContext = SevError | 0x054,
ERROR_NotSupportedInKeymanWebOutput = SevError | 0x055,
ERROR_NotSupportedInKeymanWebStore = SevError | 0x056,
ERROR_VirtualCharacterKeysNotSupportedInKeymanWeb = SevError | 0x057,
ERROR_VirtualKeysNotValidForMnemonicLayouts = SevError | 0x058,
ERROR_InvalidTouchLayoutFile = SevError | 0x059,
ERROR_TouchLayoutInvalidIdentifier = SevError | 0x05A,
ERROR_InvalidKeyCode = SevError | 0x05B,
ERROR_90FeatureOnlyLayoutFile = SevError | 0x05C,
ERROR_90FeatureOnlyKeyboardVersion = SevError | 0x05D,
ERROR_KeyboardVersionFormatInvalid = SevError | 0x05E,
ERROR_ContextExHasInvalidOffset = SevError | 0x05F,
ERROR_90FeatureOnlyEmbedCSS = SevError | 0x060,
ERROR_90FeatureOnlyTargets = SevError | 0x061,
ERROR_ContextAndIndexInvalidInMatchNomatch = SevError | 0x062,
ERROR_140FeatureOnlyContextAndNotAnyWeb = SevError | 0x063,
#define CERR_ExpansionMustFollowCharacterOrVKey 0x00004064
#define CERR_VKeyExpansionMustBeFollowedByVKey 0x00004065
#define CERR_CharacterExpansionMustBeFollowedByCharacter 0x00004066
#define CERR_VKeyExpansionMustUseConsistentShift 0x00004067
#define CERR_ExpansionMustBePositive 0x00004068
ERROR_ExpansionMustFollowCharacterOrVKey = SevError | 0x064,
ERROR_VKeyExpansionMustBeFollowedByVKey = SevError | 0x065,
ERROR_CharacterExpansionMustBeFollowedByCharacter = SevError | 0x066,
ERROR_VKeyExpansionMustUseConsistentShift = SevError | 0x067,
ERROR_ExpansionMustBePositive = SevError | 0x068,
#define CERR_CasedKeysMustContainOnlyVirtualKeys 0x00004069
#define CERR_CasedKeysMustNotIncludeShiftStates 0x0000406A
#define CERR_CasedKeysNotSupportedWithMnemonicLayout 0x0000406B
ERROR_CasedKeysMustContainOnlyVirtualKeys = SevError | 0x069,
ERROR_CasedKeysMustNotIncludeShiftStates = SevError | 0x06A,
ERROR_CasedKeysNotSupportedWithMnemonicLayout = SevError | 0x06B,
#define CERR_CannotUseReadWriteGroupFromReadonlyGroup 0x0000406C
#define CERR_StatementNotPermittedInReadonlyGroup 0x0000406D
#define CERR_OutputInReadonlyGroup 0x0000406E
#define CERR_NewContextGroupMustBeReadonly 0x0000406F
#define CERR_PostKeystrokeGroupMustBeReadonly 0x00004070
ERROR_CannotUseReadWriteGroupFromReadonlyGroup = SevError | 0x06C,
ERROR_StatementNotPermittedInReadonlyGroup = SevError | 0x06D,
ERROR_OutputInReadonlyGroup = SevError | 0x06E,
ERROR_NewContextGroupMustBeReadonly = SevError | 0x06F,
ERROR_PostKeystrokeGroupMustBeReadonly = SevError | 0x070,
#define CERR_DuplicateGroup 0x00004071
#define CERR_DuplicateStore 0x00004072
#define CERR_RepeatedBegin 0x00004073
#define CERR_VirtualKeyInContext 0x00004074
ERROR_DuplicateGroup = SevError | 0x071,
ERROR_DuplicateStore = SevError | 0x072,
ERROR_RepeatedBegin = SevError | 0x073,
ERROR_VirtualKeyInContext = SevError | 0x074,
#define CERR_OutsTooLong 0x00004075
#define CERR_ExtendedStringTooLong 0x00004076
#define CERR_VirtualKeyExpansionTooLong 0x00004077
#define CERR_CharacterRangeTooLong 0x00004078
#define CERR_NonBMPCharactersNotSupportedInKeySection 0x00004079
ERROR_OutsTooLong = SevError | 0x075,
ERROR_ExtendedStringTooLong = SevError | 0x076,
ERROR_VirtualKeyExpansionTooLong = SevError | 0x077,
ERROR_CharacterRangeTooLong = SevError | 0x078,
ERROR_NonBMPCharactersNotSupportedInKeySection = SevError | 0x079,
#define CERR_InvalidTarget 0x0000407A
#define CERR_NoTargetsSpecified 0x0000407B
ERROR_InvalidTarget = SevError | 0x07A,
ERROR_NoTargetsSpecified = SevError | 0x07B,
#define CWARN_TooManyWarnings 0x00002080
#define CWARN_OldVersion 0x00002081
#define CWARN_BitmapNotUsed 0x00002082
#define CWARN_CustomLanguagesNotSupported 0x00002083
#define CWARN_KeyBadLength 0x00002084
#define CWARN_IndexStoreShort 0x00002085
#define CWARN_UnicodeInANSIGroup 0x00002086
#define CWARN_ANSIInUnicodeGroup 0x00002087
#define CWARN_UnicodeSurrogateUsed 0x00002088
#define CWARN_ReservedCharacter 0x00002089
// Note: CWARN_Info has an "info" severity; this changed in 17.0. Earlier versions
// had a special case for CWARN_Info in message output.
#define CWARN_Info 0x0000008A
#define CINFO_Info CWARN_Info
#define CWARN_VirtualKeyWithMnemonicLayout 0x0000208B
#define CWARN_VirtualCharKeyWithPositionalLayout 0x0000208C
#define CWARN_StoreAlreadyUsedAsOptionOrCall 0x0000208D
#define CWARN_StoreAlreadyUsedAsStoreOrCall 0x0000208E
#define CWARN_StoreAlreadyUsedAsStoreOrOption 0x0000208F
WARN_TooManyWarnings = SevWarn | 0x080,
WARN_OldVersion = SevWarn | 0x081,
WARN_BitmapNotUsed = SevWarn | 0x082,
WARN_CustomLanguagesNotSupported = SevWarn | 0x083,
WARN_KeyBadLength = SevWarn | 0x084,
WARN_IndexStoreShort = SevWarn | 0x085,
WARN_UnicodeInANSIGroup = SevWarn | 0x086,
WARN_ANSIInUnicodeGroup = SevWarn | 0x087,
WARN_UnicodeSurrogateUsed = SevWarn | 0x088,
WARN_ReservedCharacter = SevWarn | 0x089,
#define CWARN_PunctuationInEthnologueCode 0x00002090
INFO_MinimumCoreEngineVersion = SevInfo | 0x08A, // renamed from INFO_Info in 18.0-alpha
#define CWARN_TouchLayoutMissingLayer 0x00002091
#define CWARN_TouchLayoutCustomKeyNotDefined 0x00002092
#define CWARN_TouchLayoutMissingRequiredKeys 0x00002093
#define CWARN_HelpFileMissing 0x00002094
#define CWARN_EmbedJsFileMissing 0x00002095
#define CWARN_TouchLayoutFileMissing 0x00002096
#define CWARN_VisualKeyboardFileMissing 0x00002097
#define CWARN_ExtendedShiftFlagsNotSupportedInKeymanWeb 0x00002098 // I4118
#define CWARN_TouchLayoutUnidentifiedKey 0x00002099
#define CHINT_UnreachableKeyCode 0x0000109A
WARN_VirtualKeyWithMnemonicLayout = SevWarn | 0x08B,
WARN_VirtualCharKeyWithPositionalLayout = SevWarn | 0x08C,
WARN_StoreAlreadyUsedAsOptionOrCall = SevWarn | 0x08D,
WARN_StoreAlreadyUsedAsStoreOrCall = SevWarn | 0x08E,
WARN_StoreAlreadyUsedAsStoreOrOption = SevWarn | 0x08F,
#define CWARN_CouldNotCopyJsonFile 0x0000209B
#define CWARN_PlatformNotInTargets 0x0000209C
WARN_PunctuationInEthnologueCode = SevWarn | 0x090,
#define CWARN_HeaderStatementIsDeprecated 0x0000209D
#define CWARN_UseNotLastStatementInRule 0x0000209E
WARN_TouchLayoutMissingLayer = SevWarn | 0x091,
WARN_TouchLayoutCustomKeyNotDefined = SevWarn | 0x092,
WARN_TouchLayoutMissingRequiredKeys = SevWarn | 0x093,
WARN_HelpFileMissing = SevWarn | 0x094,
WARN_EmbedJsFileMissing = SevWarn | 0x095,
// WARN_TouchLayoutFileMissing = SevWarn | 0x096,
// WARN_VisualKeyboardFileMissing = SevWarn | 0x097,
WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb = SevWarn | 0x098, // I4118
WARN_TouchLayoutUnidentifiedKey = SevWarn | 0x099,
HINT_UnreachableKeyCode = SevHint | 0x09A,
#define CWARN_TouchLayoutFontShouldBeSameForAllPlatforms 0x0000209F
#define CWARN_InvalidJSONMetadataFile 0x000020A0
#define CWARN_JSONMetadataOSKFontShouldMatchTouchFont 0x000020A1
#define CWARN_KVKFileIsInSourceFormat 0x000020A2
// WARN_CouldNotCopyJsonFile = SevWarn | 0x09B,
WARN_PlatformNotInTargets = SevWarn | 0x09C,
#define CWARN_DontMixChiralAndNonChiralModifiers 0x000020A3
#define CWARN_MixingLeftAndRightModifiers 0x000020A4
WARN_HeaderStatementIsDeprecated = SevWarn | 0x09D,
WARN_UseNotLastStatementInRule = SevWarn | 0x09E,
#define CWARN_LanguageHeadersDeprecatedInKeyman10 0x000020A5
WARN_TouchLayoutFontShouldBeSameForAllPlatforms = SevWarn | 0x09F,
// WARN_InvalidJSONMetadataFile = SevWarn | 0x0A0,
// WARN_JSONMetadataOSKFontShouldMatchTouchFont = SevWarn | 0x0A1,
WARN_KVKFileIsInSourceFormat = SevWarn | 0x0A2,
#define CHINT_NonUnicodeFile 0x000010A6
WARN_DontMixChiralAndNonChiralModifiers = SevWarn | 0x0A3,
WARN_MixingLeftAndRightModifiers = SevWarn | 0x0A4,
#define CWARN_TooManyErrorsOrWarnings 0x000020A7
WARN_LanguageHeadersDeprecatedInKeyman10 = SevWarn | 0x0A5,
#define CWARN_HotkeyHasInvalidModifier 0x000020A8
HINT_NonUnicodeFile = SevHint | 0x0A6,
#define CWARN_TouchLayoutSpecialLabelOnNormalKey 0x000020A9
// WARN_TooManyErrorsOrWarnings = SevWarn | 0x0A7,
#define CWARN_OptionStoreNameInvalid 0x000020AA
WARN_HotkeyHasInvalidModifier = SevWarn | 0x0A8,
#define CWARN_NulNotFirstStatementInContext 0x000020AB
#define CWARN_IfShouldBeAtStartOfContext 0x000020AC
WARN_TouchLayoutSpecialLabelOnNormalKey = SevWarn | 0x0A9,
#define CWARN_KeyShouldIncludeNCaps 0x000020AD
WARN_OptionStoreNameInvalid = SevWarn | 0x0AA,
#define CHINT_UnreachableRule 0x000010AE
WARN_NulNotFirstStatementInContext = SevWarn | 0x0AB,
WARN_IfShouldBeAtStartOfContext = SevWarn | 0x0AC,
#define CWARN_VirtualKeyInOutput 0x000020AF
WARN_KeyShouldIncludeNCaps = SevWarn | 0x0AD,
#define CHINT_IndexStoreLong 0x000010B0
HINT_UnreachableRule = SevHint | 0x0AE,
#define CERR_BufferOverflow 0x000080C0 // TODO: Rename to CFATAL_...
#define CERR_Break 0x000080C1 // TODO: Rename to CFATAL_...
WARN_VirtualKeyInOutput = SevWarn | 0x0AF,
HINT_IndexStoreLong = SevHint | 0x0B0,
FATAL_BufferOverflow = SevFatal | 0x0C0
// FATAL_Break = SevFatal | 0x0C1, unused
};
}
#endif // _kmn_compiler_errors_h

View file

@ -9,4 +9,7 @@
"include": [
"./*.ts",
],
"references": [
{ "path": "../../../../../common/web/types/" },
]
}

View file

@ -15,6 +15,7 @@
"helpers/*.ts",
],
"references": [
{ "path": "../" },
],
{ "path": "../" },
{ "path": "../../test-helpers/" },
]
}

View file

@ -9,4 +9,9 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/types" },
{ "path": "../kmc-kmn" },
{ "path": "../common/web/utils/" },
]
}

View file

@ -15,5 +15,7 @@
],
"references": [
{ "path": "../" },
{ "path": "../../common/web/test-helpers/" },
{ "path": "../../kmc-package/" },
]
}

View file

@ -16,4 +16,9 @@
"src/ttfmeta/lib/*.js",
"src/imports/langtags.js",
],
"references": [
{ "path": "../../../common/web/types" },
{ "path": "../kmc-package/" },
{ "path": "../common/web/utils/" },
]
}

View file

@ -10,7 +10,7 @@ import { UnicodeSetParser, UnicodeSet, VisualKeyboard, KvkFileReader, KeymanComp
import { CompilerCallbacks, CompilerEvent, CompilerOptions, KeymanFileTypes, KvkFileWriter, KvksFileReader } from '@keymanapp/common-types';
import * as Osk from './osk.js';
import loadWasmHost from '../import/kmcmplib/wasm-host.js';
import { KmnCompilerMessages, mapErrorFromKmcmplib } from './kmn-compiler-messages.js';
import { KmnCompilerMessages } from './kmn-compiler-messages.js';
import { WriteCompiledKeyboard } from '../kmw-compiler/kmw-compiler.js';
//
@ -57,6 +57,15 @@ export interface KmnCompilerResultExtra {
groups: CompilerResultExtraGroup[];
};
/** @internal */
export interface KmnCompilerResultMessage {
errorCode: number;
lineNumber: number;
columnNumber: number;
filename: string;
parameters: string[];
}
/**
* @public
* Internal in-memory build artifacts from a successful compilation
@ -114,16 +123,6 @@ const baseOptions: KmnCompilerOptions = {
warnDeprecatedCode: true,
};
/**
* Allows multiple instances of the Compiler class, by ensuring that the
* 'unique' kmnCompilerCallback global will be correlated with a specific
* instance of the Compiler class
*/
let callbackProcIdentifier = 0;
const
callbackPrefix = 'kmnCompilerCallbacks_';
interface MallocAndFree {
malloc(sz: number) : number;
free(p: number) : null;
@ -139,16 +138,10 @@ let
* external IO.
*/
export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
private readonly callbackID: string; // a unique numeric id added to globals with prefixed names
private callbacks: CompilerCallbacks;
private wasmExports: MallocAndFree;
private options: KmnCompilerOptions;
constructor() {
this.callbackID = callbackPrefix + callbackProcIdentifier.toString();
callbackProcIdentifier++;
}
/**
* Initialize the compiler, including loading the WASM host for kmcmplib.
* Copies options.
@ -220,46 +213,6 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
return true;
}
private compilerMessageCallback = (line: number, code: number, msg: string): number => {
this.callbacks.reportMessage(mapErrorFromKmcmplib(line, code, msg));
return 1;
}
private cachedFile: {filename: string; data: Uint8Array} = {
filename: null,
data: null
};
private loadFileCallback = (filename: string, baseFilename: string, buffer: number, bufferSize: number): number => {
let resolvedFilename = this.callbacks.resolveFilename(baseFilename, filename);
let data: Uint8Array;
if(this.cachedFile.filename == resolvedFilename) {
data = this.cachedFile.data;
}
else {
data = this.callbacks.loadFile(resolvedFilename);
if(!data) {
return -1;
}
this.cachedFile.filename = resolvedFilename;
this.cachedFile.data = data;
}
if(buffer == 0) {
/* We need to return buffer size required */
return data.byteLength;
}
if(bufferSize != data.byteLength) {
/* c8 ignore next 2 */
throw new Error(`loadFileCallback: second call, expected file size ${bufferSize} == ${data.byteLength}`);
}
Module.HEAP8.set(data, buffer);
return 1;
}
private copyWasmResult(wasm_result: any): KmnCompilerResult {
let result: KmnCompilerResult = {
// We cannot Object.assign or {...} on a wasm-defined object, so...
@ -299,6 +252,31 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
return new Uint8Array(new Uint8Array(Module.HEAP8.buffer, offset, size));
}
private toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
private generateKmcmpLibMessage(filename: string, line: number, code: number, parameters: string[]) {
const keys = Object.keys(KmnCompilerMessages);
const m = KmnCompilerMessages as Record<string,any>;
const key = keys.find(key => m[key] === code);
if(!key) {
return `Unknown message ${code.toString(16)} -- message identifier not found`;
}
const o = /^(INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key);
if(!o) {
return `Unknown message ${code.toString(16)} -- message identifier is not a valid format`;
}
const generator = this.toTitleCase(o[1])+'_'+o[2];
if(!generator || typeof m[generator] != 'function') {
return `Unknown message ${code.toString(16)} -- generator function not found`;
}
const result = m[generator]({p:parameters});
result.filename = filename;
result.line = line;
return result;
}
/**
* Compiles a .kmn file to .kmx, .kvk, and/or .js files. Returns an object
* containing binary artifacts on success. The files are passed in by name,
@ -322,12 +300,17 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
outfile = outfile ?? infile.replace(/\.kmn$/i, '.kmx');
(globalThis as any)[this.callbackID] = {
message: this.compilerMessageCallback,
loadFile: this.loadFileCallback
};
const compiler = this;
const wasm_callbacks = Module.WasmCallbackInterface.implement({
message: function(message: KmnCompilerResultMessage) {
compiler.callbacks.reportMessage(compiler.generateKmcmpLibMessage(message.filename, message.lineNumber, message.errorCode, message.parameters));
},
loadFile: function(filename: string, baseFilename: string): number[] {
const data: Uint8Array = compiler.callbacks.loadFile(compiler.callbacks.resolveFilename(baseFilename, filename));
return data ? Array.from(data) : null;
}
});
let wasm_interface = new Module.CompilerInterface();
let wasm_options = new Module.CompilerOptions();
let wasm_result = null;
try {
@ -336,8 +319,8 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
wasm_options.warnDeprecatedCode = options.warnDeprecatedCode;
wasm_options.shouldAddCompilerVersion = options.shouldAddCompilerVersion;
wasm_options.target = 0; // CKF_KEYMAN; TODO use COMPILETARGETS_KMX
wasm_interface.callbacksKey = this.callbackID; // key of object on globalThis
wasm_result = Module.kmcmp_compile(infile, wasm_options, wasm_interface);
wasm_result = Module.kmcmp_compile(infile, wasm_options, wasm_callbacks);
if(!wasm_result.result) {
return null;
}
@ -382,7 +365,7 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
// should have no impact on the final .js if options.debug is false
wasm_options.saveDebug = true;
wasm_result = Module.kmcmp_compile(infile, wasm_options, wasm_interface);
wasm_result = Module.kmcmp_compile(infile, wasm_options, wasm_callbacks);
if(!wasm_result.result) {
return null;
}
@ -405,9 +388,8 @@ export class KmnCompiler implements KeymanCompiler, UnicodeSetParser {
if(wasm_result) {
wasm_result.delete();
}
wasm_interface.delete();
wasm_callbacks.delete();
wasm_options.delete();
delete (globalThis as any)[this.callbackID];
}
}

View file

@ -18,30 +18,6 @@ const mw = (code: number, message: string, o?: {filename?: string, line?: number
line: o?.line,
});
/**
* LogLevel comes from kmn_compiler_errors.h, for legacy compiler error messages
*/
const enum LogLevel {
LEVEL_MASK = 0xF000,
CODE_MASK = 0x0FFF,
CERR_FATAL = 0x8000,
CERR_ERROR = 0x4000,
CERR_WARNING = 0x2000,
CERR_HINT = 0x1000,
CERR_INFO = 0
};
/**
* Translate the legacy compiler error messages to Severity codes
*/
const LogLevelToSeverity: Record<number,number> = {
[LogLevel.CERR_FATAL]: SevFatal,
[LogLevel.CERR_ERROR]: SevError,
[LogLevel.CERR_WARNING]: SevWarn,
[LogLevel.CERR_HINT]: SevHint,
[LogLevel.CERR_INFO]: SevInfo
}
export const enum KmnCompilerMessageRanges {
RANGE_KMN_COMPILER_MIN = 0x001, // from kmn_compiler_errors.h
RANGE_KMN_COMPILER_MAX = 0x7FF, // from kmn_compiler_errors.h
@ -51,6 +27,8 @@ export const enum KmnCompilerMessageRanges {
RANGE_CompilerMessage_Max = 0xFFF, // Highest available error code for kmc-kmn
}
type KmcmpLibMessageParameters = {p:string[]};
/**
* @internal
*
@ -211,10 +189,8 @@ export class KmnCompilerMessages {
to the file.`
);
// static INFO_None = SevInfo | 0x000;
static INFO_EndOfFile = SevInfo | 0x001;
static Info_EndOfFile = () => m(this.INFO_EndOfFile, `(no error - reserved code)`);
// static STATUS_None = 0x000; // This is not a real error
// static STATUS_EndOfFile = 0x001; // This is not a real error
static FATAL_BadCallParams = SevFatal | 0x002;
static Fatal_BadCallParams = () => m(this.FATAL_BadCallParams, `CompileKeyboardFile was called with bad parameters`);
@ -228,8 +204,8 @@ export class KmnCompilerMessages {
// static ERROR_CannotCreateOutfile = SevError | 0x006; // #10678: reduced from fatal to error in 17.0, but unused
// static Error_CannotCreateOutfile = () => m(this.ERROR_CannotCreateOutfile, `Cannot open output file for writing`); unused
static FATAL_UnableToWriteFully = SevFatal | 0x007;
static Fatal_UnableToWriteFully = () => m(this.FATAL_UnableToWriteFully, `Unable to write the file completely`);
// static FATAL_UnableToWriteFully = SevFatal | 0x007;
// static Fatal_UnableToWriteFully = () => m(this.FATAL_UnableToWriteFully, `Unable to write the file completely`);
static ERROR_CannotReadInfile = SevError | 0x008; // #10678: reduced from fatal to error in 17.0
static Error_CannotReadInfile = () => m(this.ERROR_CannotReadInfile, `Cannot read the input file`);
@ -548,10 +524,16 @@ export class KmnCompilerMessages {
static Error_PostKeystrokeGroupMustBeReadonly = () => m(this.ERROR_PostKeystrokeGroupMustBeReadonly, `Group used in begin postKeystroke must be readonly`);
static ERROR_DuplicateGroup = SevError | 0x071;
static Error_DuplicateGroup = () => m(this.ERROR_DuplicateGroup, `A group with this name has already been defined.`);
static Error_DuplicateGroup = (o:KmcmpLibMessageParameters) => m(
this.ERROR_DuplicateGroup,
`A group with the name '${o.p?.[0]}' has already been defined on line ${o.p?.[1]}.`
);
static ERROR_DuplicateStore = SevError | 0x072;
static Error_DuplicateStore = () => m(this.ERROR_DuplicateStore, `A store with this name has already been defined.`);
static Error_DuplicateStore = (o:KmcmpLibMessageParameters) => m(
this.ERROR_DuplicateStore,
`A store with the name '${o.p?.[0]}' has already been defined on line ${o.p?.[1]}.`
);
static ERROR_RepeatedBegin = SevError | 0x073;
static Error_RepeatedBegin = () => m(this.ERROR_RepeatedBegin, `Begin has already been set`);
@ -575,7 +557,10 @@ export class KmnCompilerMessages {
static Error_NonBMPCharactersNotSupportedInKeySection = () => m(this.ERROR_NonBMPCharactersNotSupportedInKeySection, `Characters with codepoints over U+FFFF are not supported in the key part of the rule`);
static ERROR_InvalidTarget = SevError | 0x07A;
static Error_InvalidTarget = () => m(this.ERROR_InvalidTarget, `Unrecognized compile target`);
static Error_InvalidTarget = (o:KmcmpLibMessageParameters) => m(
this.ERROR_InvalidTarget,
`Unrecognized compile target '${o.p?.[0]}'`
);
static ERROR_NoTargetsSpecified = SevError | 0x07B;
static Error_NoTargetsSpecified = () => m(this.ERROR_NoTargetsSpecified, `At least one compile target must be specified`);
@ -610,10 +595,11 @@ export class KmnCompilerMessages {
static WARN_ReservedCharacter = SevWarn | 0x089;
static Warn_ReservedCharacter = () => m(this.WARN_ReservedCharacter, `A Unicode character was found that should not be used`);
// Note: INFO_Info is called CWARN_Info in kmn_compiler_errors.h, but should have an "info" severity
static INFO_Info = SevInfo | 0x08A;
static Info_Info = () => m(this.INFO_Info, `Information`);
static INFO_MinimumCoreEngineVersion = SevInfo | 0x08A;
static Info_MinimumCoreEngineVersion = (o:KmcmpLibMessageParameters) => m(
this.INFO_MinimumCoreEngineVersion,
`The compiler has assigned a minimum engine version of ${o.p?.[0]}.${o.p?.[1]} based on features used in this keyboard`
);
static WARN_VirtualKeyWithMnemonicLayout = SevWarn | 0x08B;
static Warn_VirtualKeyWithMnemonicLayout = () => m(this.WARN_VirtualKeyWithMnemonicLayout, `Virtual key used instead of virtual character key with a mnemonic layout`);
@ -730,7 +716,10 @@ export class KmnCompilerMessages {
static Warn_KeyShouldIncludeNCaps = () => m(this.WARN_KeyShouldIncludeNCaps, `Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches`);
static HINT_UnreachableRule = SevHint | 0x0AE;
static Hint_UnreachableRule = () => m(this.HINT_UnreachableRule, `This rule will never be matched as another rule takes precedence`);
static Hint_UnreachableRule = (o:KmcmpLibMessageParameters) => m(
this.HINT_UnreachableRule,
`This rule will never be matched as the rule on line ${o.p?.[0]} takes precedence`
);
static WARN_VirtualKeyInOutput = SevWarn | 0x0AF;
static Warn_VirtualKeyInOutput = () => m(this.WARN_VirtualKeyInOutput, `Virtual keys are not supported in output`);
@ -744,17 +733,6 @@ export class KmnCompilerMessages {
static FATAL_BufferOverflow = SevFatal | 0x0C0;
static Fatal_BufferOverflow = () => m(this.FATAL_BufferOverflow, `The compiler memory buffer overflowed`);
static FATAL_Break = SevFatal | 0x0C1;
static Fatal_Break = () => m(this.FATAL_Break, `Compiler interrupted by user`);
};
export function mapErrorFromKmcmplib(line: number, code: number, msg: string): CompilerEvent {
const severity = LogLevelToSeverity[code & LogLevel.LEVEL_MASK];
const baseCode = code & LogLevel.CODE_MASK;
const event: CompilerEvent = {
line: line,
code: severity | CompilerErrorNamespace.KmnCompiler | baseCode,
message: msg
};
return event;
// static FATAL_Break = SevFatal | 0x0C1;
// static Fatal_Break = () => m(this.FATAL_Break, `Compiler interrupted by user`);
};

View file

@ -49,7 +49,9 @@ export class KmwCompilerMessages extends KmnCompilerMessages {
static Hint_TouchLayoutUsesUnsupportedGesturesDownlevel = (o:{keyId:string}) => m(this.HINT_TouchLayoutUsesUnsupportedGesturesDownlevel,
`The touch layout uses a flick or multi-tap gesture on key ${def(o.keyId)}, which is only available on version 17.0+ of Keyman`);
static INFO_MinimumEngineVersion = SevInfo | 0x0006;
static Info_MinimumEngineVersion = (o:{version:string}) => m(this.INFO_MinimumEngineVersion,
`The compiler has assigned a minimum web engine version of ${o.version} based on features used in this keyboard`);
static INFO_MinimumWebEngineVersion = SevInfo | 0x0006;
static Info_MinimumWebEngineVersion = (o:{version:string}) => m(
this.INFO_MinimumWebEngineVersion,
`The compiler has assigned a minimum web engine version of ${o.version} based on features used in this keyboard`
);
};

View file

@ -412,7 +412,7 @@ export function WriteCompiledKeyboard(
if((fk.flags & KMX.KMXFile.KF_AUTOMATICVERSION) == KMX.KMXFile.KF_AUTOMATICVERSION) {
// Note: the KeymanWeb compiler is responsible for reporting minimum
// version for the web targets
callbacks.reportMessage(KmwCompilerMessages.Info_MinimumEngineVersion({version:minVer}));
callbacks.reportMessage(KmwCompilerMessages.Info_MinimumWebEngineVersion({version:minVer}));
}
return resultPrefix + resultMinVer + result;

View file

@ -1,7 +1,7 @@
c Description: Verifies that kmcmplib picks up on duplicated groups with Unicode names,
c and that the Unicode names are correctly reported in error messages in UTF-8
store(&NAME) 'cerr_duplicate_group'
store(&NAME) 'error_duplicate_group'
store(&VERSION) '9.0'
begin unicode > use(ខ្មែរ)

View file

@ -1,7 +1,7 @@
c Description: Verifies that kmcmplib picks up on duplicated stores with Unicode names,
c and that the Unicode names are correctly reported in error messages in UTF-8
store(&NAME) 'cerr_duplicate_store'
store(&NAME) 'error_duplicate_store'
store(&VERSION) '9.0'
begin unicode > use(ខ្មែរ)

View file

@ -87,10 +87,9 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
// The min version message from the .kmn compiler is generic 208A INFO_Info;
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
// we expect only 1 of the info messages -- for the .kmx target (not 2)
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_MinimumCoreEngineVersion).length, 1);
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
assert.match(data, /KMINVER="15.0"/, `Could not find expected 'KMINVER="15.0"'`);
@ -101,9 +100,8 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNull(result);
// The min version message from the .kmn compiler is generic 208A INFO_Info
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_MinimumCoreEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.ERROR_TouchLayoutIdentifierRequires15));
});
@ -113,9 +111,8 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result); // only a warning, so output is generated
// The min version message from the .kmn compiler is generic 208A INFO_Info
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_MinimumCoreEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb));
});
@ -125,10 +122,9 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
// The min version message from the .kmn compiler is generic 208A INFO_Info;
// we expect only 1 of the info messages -- for the .kmx target (not 2)
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_MinimumCoreEngineVersion).length, 1);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb));
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
@ -142,10 +138,9 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
// The min version message from the .kmn compiler is generic 208A INFO_Info;
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
// we expect only 1 of the info messages -- for the .kmx target (not 2)
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_MinimumCoreEngineVersion).length, 1);
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
assert.match(data, /KMINVER="14.0"/, `Could not find expected 'KMINVER="14.0"'`);
@ -157,9 +152,8 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNull(result);
// The min version message from the .kmn compiler is generic 208A INFO_Info
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_MinimumCoreEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.ERROR_140FeatureOnlyContextAndNotAnyWeb));
});
@ -168,10 +162,9 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
// The min version message from the .kmn compiler is generic 208A INFO_Info;
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
// we expect only 1 of the info messages -- for the .kmx target (not 2)
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_MinimumCoreEngineVersion).length, 1);
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
assert.match(data, /KMINVER="14.0"/, `Could not find expected 'KMINVER="14.0"'`);
@ -182,9 +175,8 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
// The min version message from the .kmn compiler is generic 208A INFO_Info
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_MinimumCoreEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.WARN_TouchLayoutSpecialLabelOnNormalKey));
});
@ -193,10 +185,9 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
// The min version message from the .kmn compiler is generic 208A INFO_Info;
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
// we expect only 1 of the info messages -- for the .kmx target (not 2)
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1);
assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_MinimumCoreEngineVersion).length, 1);
const data = new TextDecoder('utf-8').decode(result.artifacts.js.data);
assert.match(data, /KMINVER="17.0"/, `Could not find expected 'KMINVER="17.0"'`);
@ -207,9 +198,8 @@ describe('KeymanWeb Compiler', function() {
let result = await kmnCompiler.run(filenames.source, null);
assert.isNotNull(result);
// The min version message from the .kmn compiler is generic 208A INFO_Info
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion));
assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_MinimumCoreEngineVersion));
assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumWebEngineVersion));
assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.HINT_TouchLayoutUsesUnsupportedGesturesDownlevel));
});

View file

@ -1,10 +1,10 @@
import 'mocha';
import { assert } from 'chai';
import { KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
import { KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
import { makePathToFixture } from './helpers/index.js';
import { KmnCompiler } from '../src/main.js';
import { CompilerErrorNamespace } from '@keymanapp/common-types';
import { CompilerErrorMask, CompilerErrorNamespace } from '@keymanapp/common-types';
describe('KmnCompilerMessages', function () {
const callbacks = new TestCompilerCallbacks();
@ -13,6 +13,69 @@ describe('KmnCompilerMessages', function () {
return verifyCompilerMessagesObject(KmnCompilerMessages, CompilerErrorNamespace.KmnCompiler);
});
it('should have a 1:1 correspondence with kmn_compiler_errors.h', function() {
const headerFilename = 'kmn_compiler_errors.h';
const tsFilename = 'kmn-compiler-messages.ts';
// For each message declared in KmnCompilerMessages, look for a
// corresponding message in kmn_compiler_errors.h
const headerPath = makePathToFixture(`../../../common/include/${headerFilename}`);
// Matching the following line pattern from kmn_compiler_errors.h:
//
// FATAL_BadCallParams = SevFatal | 0x002,
const messageRegex = /^\s+((FATAL|ERROR|WARN|HINT|INFO)_[a-z0-9_]+)\s+=\s+Sev(Fatal|Error|Warn|Hint|Info)\s+\|\s+0x([0-9a-f]+)/i;
// Convert matching lines into array of message code data
const cppMessages = callbacks.fs.readFileSync(headerPath, 'utf-8')
.replace(/\r/g, '')
.split('\n')
.filter(line => line.match(messageRegex)) // filter first, run regex twice, ;-)
.map(line => {
const m = line.match(messageRegex);
return { name: m[1], nameSeverity: m[2], maskSeverity: m[3], code: parseInt(m[4],16), found: false }
});
const cppMessagesByName: {[index:string]: {name: string, nameSeverity: string, maskSeveity: string, code: number}} = cppMessages
.reduce((obj, m) => {obj[m.name] = m; return obj}, {} as any);
// We validate that the nameSeverity and maskSeverity line up, as a sanity
// check. This conceptually belongs in kmcmplib tests, but much easier to
// achieve here
cppMessages.forEach(m => {
assert.strictEqual(m.maskSeverity.toUpperCase(), m.nameSeverity, `${headerFilename} has mismatching severity for ${m.name} and ${m.maskSeverity}`)
});
// Use same pattern as verifyCompilerMessagesObject to get list of messages
// from KmnCompilerMessages
const keys = Object.keys(KmnCompilerMessages);
const m = KmnCompilerMessages as Record<string,any>;
for(const key of keys) {
// Verify each object member matches the pattern we expect
if(typeof m[key] == 'number') {
if((m[key] & CompilerErrorMask.BaseError) < KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MIN ||
(m[key] & CompilerErrorMask.BaseError) > KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MAX) {
// Message is not from kmcmplib
continue;
}
const cppMessage = cppMessagesByName[key];
assert.isDefined(cppMessage, `${key} in ${tsFilename} not found in ${headerFilename}`);
assert.equal(cppMessage.name, key, `${key} name in ${tsFilename} does not precisely match ${headerFilename}`);
assert.equal(cppMessage.code, m[key] & CompilerErrorMask.BaseError, `${key} in ${tsFilename} has a different value than ${headerFilename}`);
// Remove the entry so we can spot unpaired messages afterwards
delete cppMessagesByName[key];
}
}
// Ensure there are no remaining messages in kmn_compiler_errors.h
assert.isEmpty(Object.keys(cppMessagesByName), `${headerFilename} has entries not found in ${tsFilename}: ${JSON.stringify(cppMessagesByName)}`);
});
//
// Message tests
//

View file

@ -12,4 +12,9 @@
"src/**/*.ts",
"src/import/kmcmplib/wasm-host.js"
],
"references": [
{ "path": "../../../common/web/types/" },
{ "path": "../../../common/web/keyman-version/" },
{ "path": "../common/web/utils/" },
]
}

View file

@ -13,6 +13,10 @@
"./helpers/index.ts"
],
"references": [
{ "path": "../../../../common/web/keyman-version" },
{ "path": "../../../../common/web/types/" },
{ "path": "../../../../common/tools/hextobin/" },
{ "path": "../../common/web/test-helpers/" },
{ "path": "../" }
]
}

View file

@ -9,4 +9,10 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/keyman-version" },
{ "path": "../../../common/web/types/" },
{ "path": "../kmc-kmn/" },
{ "path": "../../../core/include/ldml/"},
]
}

View file

@ -15,5 +15,7 @@
],
"references": [
{ "path": "../" },
{ "path": "../../common/web/test-helpers/" },
{ "path": "../../kmc-package/" },
]
}

View file

@ -8,4 +8,9 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/types" },
{ "path": "../../../common/models/types" },
{ "path": "../common/web/utils" },
]
}

View file

@ -17,5 +17,6 @@
],
"references": [
{ "path": "../" },
{ "path": "../../common/web/test-helpers/" },
]
}

View file

@ -10,4 +10,9 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/keyman-version" },
{ "path": "../../../common/models/types" },
{ "path": "../../../common/web/types" },
]
}

View file

@ -14,5 +14,8 @@
],
"references": [
{ "path": "../" },
{ "path": "../../../../common/web/types" },
{ "path": "../../../../common/web/keyman-version" },
{ "path": "../../common/web/test-helpers/" },
]
}

View file

@ -9,4 +9,8 @@
"include": [
"src/**/*.ts",
],
"references": [
{ "path": "../../../common/web/keyman-version" },
{ "path": "../../../common/web/types" },
]
}

View file

@ -12,6 +12,8 @@
"./helpers/index.ts",
],
"references": [
{ "path": "../../../../common/web/types/" },
{ "path": "../../common/web/test-helpers/" },
{ "path": "../" }
]
}

View file

@ -9,4 +9,15 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/keyman-version" },
{ "path": "../../../common/web/types" },
{ "path": "../kmc-analyze" },
{ "path": "../kmc-keyboard-info" },
{ "path": "../kmc-kmn" },
{ "path": "../kmc-ldml" },
{ "path": "../kmc-model" },
{ "path": "../kmc-model-info" },
{ "path": "../kmc-package" },
]
}

View file

@ -53,6 +53,14 @@ struct KMCMP_COMPILER_RESULT_EXTRA_GROUP {
std::string name;
};
struct KMCMP_COMPILER_RESULT_MESSAGE {
unsigned int errorCode;
int lineNumber;
int columnNumber;
std::string filename;
std::vector<std::string> parameters;
};
#define COMPILETARGETS_KMX 0x01
#define COMPILETARGETS_JS 0x02
#define COMPILETARGETS__MASK 0x03
@ -75,19 +83,8 @@ struct KMCMP_COMPILER_RESULT {
/**
* @param szText UTF-8 string
*/
typedef int (*kmcmp_CompilerMessageProc)(int line, uint32_t dwMsgCode, const char* szText, void* context);
// parameters in UTF-8
// TODO typical usage:
// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", nullptr, &size)) {
// return error;
// }
// buf = new unsigned char[size];
// if(!kmcmp_LoadFileProc("filename.ico", "/tmp/filename.kmn", buf, &size)) {
// delete[] buf;
// return error;
// }
typedef bool (*kmcmp_LoadFileProc)(const char* loadFilename, const char* baseFilename, void* buffer, int* bufferSize, void* context);
typedef void (*kmcmp_CompilerMessageProc)(const KMCMP_COMPILER_RESULT_MESSAGE &message, void* context);
typedef const std::vector<uint8_t> (*kmcmp_LoadFileProc)(const std::string& loadFilename, const std::string& baseFilename);
/**
* @param pszInfile UTF-8 path to file.kmn

View file

@ -24,7 +24,7 @@ KMX_DWORD VerifyCasedKeys(PFILE_STORE sp) {
if (kmcmp::FMnemonicLayout) {
// The &CasedKeys system store is not supported for
// mnemonic layouts in 14.0
return CERR_CasedKeysNotSupportedWithMnemonicLayout;
return KmnCompilerMessages::ERROR_CasedKeysNotSupportedWithMnemonicLayout;
}
// We will rewrite this store with virtual keys
@ -37,20 +37,20 @@ KMX_DWORD VerifyCasedKeys(PFILE_STORE sp) {
KMX_UINT key = 0, shift = 0;
if (*p != UC_SENTINEL) {
if (!kmcmp::MapUSCharToVK(*p, &key, &shift)) {
return CERR_CasedKeysMustContainOnlyVirtualKeys;
return KmnCompilerMessages::ERROR_CasedKeysMustContainOnlyVirtualKeys;
}
if (shift & K_SHIFTFLAG) {
return CERR_CasedKeysMustNotIncludeShiftStates;
return KmnCompilerMessages::ERROR_CasedKeysMustNotIncludeShiftStates;
}
}
else {
if (*(p + 1) != CODE_EXTENDED) {
return CERR_CasedKeysMustContainOnlyVirtualKeys;
return KmnCompilerMessages::ERROR_CasedKeysMustContainOnlyVirtualKeys;
}
shift = *(p + 2);
key = *(p + 3);
if (shift != ISVIRTUALKEY) {
return CERR_CasedKeysMustNotIncludeShiftStates;
return KmnCompilerMessages::ERROR_CasedKeysMustNotIncludeShiftStates;
}
}
*q++ = UC_SENTINEL;
@ -66,7 +66,7 @@ KMX_DWORD VerifyCasedKeys(PFILE_STORE sp) {
delete[] sp->dpString;
sp->dpString = buf;
return CERR_None;
return STATUS_Success;
}
KMX_DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) {
@ -76,14 +76,14 @@ KMX_DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) {
if (kmcmp::FMnemonicLayout) {
// The &CasedKeys system store is not supported for
// mnemonic layouts in 14.0
return CERR_None;
return STATUS_Success;
}
PFILE_STORE sp = FindSystemStore(fk, TSS_CASEDKEYS);
if (!sp) {
// If there is no &CasedKeys system store, then we do not
// process the key
return CERR_None;
return STATUS_Success;
}
KMX_DWORD msg;
@ -92,11 +92,11 @@ KMX_DWORD ExpandCapsRulesForGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) {
// dereference the array every call
int cxKeyArray = gp->cxKeyArray;
for (int i = 0; i < cxKeyArray; i++) {
if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != CERR_None) {
if ((msg = ExpandCapsRule(gp, &gp->dpKeyArray[i], sp)) != STATUS_Success) {
return msg;
}
}
return CERR_None;
return STATUS_Success;
}
KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) {
@ -106,13 +106,13 @@ KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) {
if (shift == 0) {
// Convert US key cap to a virtual key
if (!kmcmp::MapUSCharToVK(kpp->Key, &key, &shift)) {
return CERR_None;
return STATUS_Success;
}
}
if (shift & (CAPITALFLAG | NOTCAPITALFLAG)) {
// Don't attempt expansion if either Caps Lock flag is specified in the key rule
return CERR_None;
return STATUS_Success;
}
PKMX_WCHAR p = sp->dpString;
@ -125,13 +125,13 @@ KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) {
if (!*p) {
// This key is not modified by Caps Lock
return CERR_None;
return STATUS_Success;
}
// This key is modified by Caps Lock, so we need to duplicate this rule
int offset = (int)(kpp - gp->dpKeyArray);
if(!resizeKeyArray(gp)) {
return CERR_CannotAllocateMemory;
return KmnCompilerMessages::FATAL_CannotAllocateMemory;
}
kpp = &gp->dpKeyArray[offset];
gp->cxKeyArray++;
@ -149,5 +149,5 @@ KMX_DWORD ExpandCapsRule(PFILE_GROUP gp, PFILE_KEY kpp, PFILE_STORE sp) {
kpp->Key = key;
kpp->ShiftFlags = shift | NOTCAPITALFLAG;
return CERR_None;
return STATUS_Success;
}

View file

@ -10,7 +10,7 @@
#include "CheckForDuplicates.h"
KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept {
KMX_BOOL CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept {
KMX_DWORD i;
PFILE_GROUP gp0 = fk->dpGroupArray;
for (i = 0; i < fk->cxGroupArray; i++, gp0++) {
@ -18,18 +18,21 @@ KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept {
continue;
}
if (u16icmp(gp0->szName, gp->szName) == 0) {
snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Group '%s' declared on line %d", string_from_u16string(gp->szName).c_str(), gp0->Line);
return CERR_DuplicateGroup;
ReportCompilerMessage(KmnCompilerMessages::ERROR_DuplicateGroup, {
/*groupName*/ string_from_u16string(gp->szName),
/*lineNumber*/ std::to_string(gp0->Line)
});
return FALSE;
}
}
return CERR_None;
return TRUE;
}
KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept {
KMX_BOOL CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept {
if (!sp->szName[0]) {
// Stores with zero length names are reserved system stores.
// They cannot be defined in user code. This is not an issue.
return CERR_None;
return TRUE;
}
KMX_DWORD i;
PFILE_STORE sp0 = fk->dpStoreArray;
@ -38,9 +41,12 @@ KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept {
continue;
}
if (u16icmp(sp0->szName, sp->szName) == 0) {
snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Store '%s' declared on line %d", string_from_u16string(sp0->szName).c_str(), sp0->line);
return CERR_DuplicateStore;
ReportCompilerMessage(KmnCompilerMessages::ERROR_DuplicateStore, {
/*storeName*/ string_from_u16string(sp0->szName),
/*lineNumber*/ std::to_string(sp0->line)
});
return FALSE;
}
}
return CERR_None;
return TRUE;
}

View file

@ -1,4 +1,4 @@
#pragma once
KMX_DWORD CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept;
KMX_DWORD CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept;
KMX_BOOL CheckForDuplicateGroup(PFILE_KEYBOARD fk, PFILE_GROUP gp) noexcept;
KMX_BOOL CheckForDuplicateStore(PFILE_KEYBOARD fk, PFILE_STORE sp) noexcept;

View file

@ -33,7 +33,7 @@
*
* @param fk Keyboard to check
*/
bool CheckNCapsConsistency(PFILE_KEYBOARD fk) {
void CheckNCapsConsistency(PFILE_KEYBOARD fk) {
struct CapsUsage {
int ncaps_line, caps_line, neither_line;
};
@ -87,13 +87,11 @@ bool CheckNCapsConsistency(PFILE_KEYBOARD fk) {
if (caps_ncaps_usage[i].neither_line && (caps_ncaps_usage[i].caps_line || caps_ncaps_usage[i].ncaps_line)) {
// We set the current line to one needing work: the developer should add the NCAPS flag
kmcmp::currentLine = caps_ncaps_usage[i].neither_line;
AddWarningBool(CWARN_KeyShouldIncludeNCaps);
ReportCompilerMessage(KmnCompilerMessages::WARN_KeyShouldIncludeNCaps);
}
}
delete[] caps_ncaps_usage;
kmcmp::currentLine = oldCurrentLine;
return TRUE;
}

View file

@ -2,4 +2,4 @@
#include <compfile.h>
bool CheckNCapsConsistency(PFILE_KEYBOARD fk);
void CheckNCapsConsistency(PFILE_KEYBOARD fk);

View file

@ -1,155 +0,0 @@
#include <CompMsg.h>
#include <map>
std::map<KMX_DWORD, const KMX_CHAR*> CompilerErrorMap = {
{ CERR_InvalidLayoutLine , "Invalid 'layout' command"},
{ CERR_NoVersionLine , "No version line found for file"},
{ CERR_InvalidGroupLine , "Invalid 'group' command"},
{ CERR_InvalidStoreLine , "Invalid 'store' command"},
{ CERR_InvalidCodeInKeyPartOfRule , "Invalid command or code found in key part of rule"},
{ CERR_InvalidDeadkey , "Invalid 'deadkey' or 'dk' command"},
{ CERR_InvalidValue , "Invalid value in extended string"},
{ CERR_ZeroLengthString , "A string of zero characters was found"},
{ CERR_TooManyIndexToKeyRefs , "Too many index commands refering to key string"},
{ CERR_UnterminatedString , "Unterminated string in line"},
{ CERR_StringInVirtualKeySection , "extend string illegal in virtual key section"},
{ CERR_AnyInVirtualKeySection , "'any' command is illegal in virtual key section"},
{ CERR_InvalidAny , "Invalid 'any' command"},
{ CERR_StoreDoesNotExist , "Store referenced does not exist"},
{ CERR_BeepInVirtualKeySection , "'beep' command is illegal in virtual key section"},
{ CERR_IndexInVirtualKeySection , "'index' command is illegal in virtual key section"},
{ CERR_BadCallParams , "CompileKeyboardFile was called with bad parameters"},
{ CERR_InfileNotExist , "Cannot find the input file"},
// { CERR_CannotCreateOutfile , "Cannot open output file for writing"}, unused
{ CERR_UnableToWriteFully , "Unable to write the file completely"},
{ CERR_CannotReadInfile , "Cannot read the input file"},
{ CERR_SomewhereIGotItWrong , "Internal error: contact Keyman"},
{ CERR_BufferOverflow , "The compiler memory buffer overflowed"},
{ CERR_Break , "Compiler interrupted by user"},
{ CERR_CannotAllocateMemory , "Out of memory"},
{ CERR_InvalidBitmapLine , "Invalid 'bitmaps' command"},
{ CERR_CannotReadBitmapFile , "Cannot open the bitmap or icon file for reading"},
{ CERR_IndexDoesNotPointToAny , "An index() in the output does not have a corresponding any() statement"},
{ CERR_ReservedCharacter , "A reserved character was found"},
{ CERR_InvalidCharacter , "A character was found that is outside the valid Unicode range (U+0000 - U+10FFFF)"},
{ CERR_InvalidCall , "The 'call' command is invalid"},
{ CERR_CallInVirtualKeySection , "'call' command is illegal in virtual key section"},
{ CERR_CodeInvalidInKeyStore , "The command is invalid inside a store that is used in a key part of the rule"},
{ CERR_CannotLoadIncludeFile , "Cannot load the included file: it is either invalid or does not exist"},
{ CERR_60FeatureOnly_EthnologueCode , "EthnologueCode system store requires VERSION 6.0 or higher"},
{ CERR_60FeatureOnly_MnemonicLayout , "MnemonicLayout functionality requires VERSION 6.0 or higher"},
{ CERR_60FeatureOnly_OldCharPosMatching , "OldCharPosMatching system store requires VERSION 6.0 or higher"},
{ CERR_60FeatureOnly_NamedCodes , "Named character constants requires VERSION 6.0 or higher"},
{ CERR_60FeatureOnly_Contextn , "Context(n) requires VERSION 6.0 or higher"},
{ CERR_501FeatureOnly_Call , "Call() requires VERSION 5.01 or higher"},
{ CERR_InvalidNamedCode , "Invalid named code constant"},
{ CERR_InvalidSystemStore , "Invalid system store name found"},
{ CERR_60FeatureOnly_VirtualCharKey , "Virtual character keys require VERSION 6.0 or higher"},
{ CERR_VersionAlreadyIncluded , "Only one VERSION or store(version) line allowed in a source file."},
{ CERR_70FeatureOnly , "This feature requires store(version) '7.0' or higher"},
{ CERR_80FeatureOnly , "This feature requires store(version) '8.0' or higher"},
{ CERR_InvalidInVirtualKeySection , "This statement is not valid in a virtual key section"},
{ CERR_InvalidIf , "The if() statement is not valid"},
{ CERR_InvalidReset , "The reset() statement is not valid"},
{ CERR_InvalidSet , "The set() statement is not valid"},
{ CERR_InvalidSave , "The save() statement is not valid"},
{ CERR_InvalidEthnologueCode , "Invalid ethnologuecode format"},
{ CERR_90FeatureOnly_IfSystemStores , "if(store) requires store(version) '9.0' or higher"},
{ CERR_IfSystemStore_NotFound , "System store in if() not found"},
{ CERR_90FeatureOnly_SetSystemStores , "set(store) requires store(version) '9.0' or higher"},
{ CERR_SetSystemStore_NotFound , "System store in set() not found"},
{ CERR_90FeatureOnlyVirtualKeyDictionary , "Custom virtual key names require store(version) '9.0'"},
{ CERR_InvalidIndex , "Invalid 'index' command"},
{ CERR_OutsInVirtualKeySection , "'outs' command is illegal in virtual key section"},
{ CERR_InvalidOuts , "Invalid 'outs' command"},
{ CERR_ContextInVirtualKeySection , "'context' command is illegal in virtual key section"},
{ CERR_InvalidUse , "Invalid 'use' command"},
{ CERR_GroupDoesNotExist , "Group does not exist"},
{ CERR_VirtualKeyNotAllowedHere , "Virtual key is not allowed here"},
{ CERR_InvalidSwitch , "Invalid 'switch' command"},
{ CERR_NoTokensFound , "No tokens found in line"},
{ CERR_InvalidLineContinuation , "Invalid line continuation"},
{ CERR_LineTooLong , "Line too long"},
{ CERR_InvalidCopyright , "Invalid 'copyright' command"},
{ CERR_CodeInvalidInThisSection , "This line is invalid in this section of the file"},
{ CERR_InvalidMessage , "Invalid 'message' command"},
{ CERR_InvalidLanguageName , "Invalid 'languagename' command"},
{ CERR_EndOfFile , "(no error - reserved code)"},
{ CERR_InvalidToken , "Invalid token found"},
{ CERR_InvalidBegin , "Invalid 'begin' command"},
{ CERR_InvalidName , "Invalid 'name' command"},
{ CERR_InvalidVersion , "Invalid 'version' command"},
{ CERR_InvalidLanguageLine , "Invalid 'language' command"},
{ CERR_LayoutButNoLanguage , "Layout command found but no language command"},
{ CERR_CannotCreateTempfile , "Cannot create temp file"},
{ CERR_90FeatureOnlyLayoutFile , "Touch layout file reference requires store(version) '9.0' or higher"},
{ CERR_90FeatureOnlyKeyboardVersion , "KeyboardVersion system store requires store(version) '9.0' or higher"},
{ CERR_KeyboardVersionFormatInvalid , "KeyboardVersion format is invalid, expecting dot-separated integers"},
{ CERR_ContextExHasInvalidOffset , "context() statement has offset out of range"},
{ CERR_90FeatureOnlyEmbedCSS , "Embedding CSS requires store(version) '9.0' or higher"},
{ CERR_90FeatureOnlyTargets , "TARGETS system store requires store(version) '9.0' or higher"},
{ CERR_ContextAndIndexInvalidInMatchNomatch , "context and index statements cannot be used in a match or nomatch statement"},
{ CERR_140FeatureOnlyContextAndNotAnyWeb , "For web and touch platforms, context() statement referring to notany() requires store(version) '14.0' or higher"},
{ CERR_ExpansionMustFollowCharacterOrVKey , "An expansion must follow a character or a virtual key"},
{ CERR_VKeyExpansionMustBeFollowedByVKey , "A virtual key expansion must be terminated by a virtual key"},
{ CERR_CharacterExpansionMustBeFollowedByCharacter , "A character expansion must be terminated by a character key"},
{ CERR_VKeyExpansionMustUseConsistentShift , "A virtual key expansion must use the same shift state for both terminators"},
{ CERR_ExpansionMustBePositive , "An expansion must have positive difference (i.e. A-Z, not Z-A)"},
{ CERR_CasedKeysMustContainOnlyVirtualKeys , "The &CasedKeys system store must contain only virtual keys or characters found on a US English keyboard"},
{ CERR_CasedKeysMustNotIncludeShiftStates , "The &CasedKeys system store must not include shift states"},
{ CERR_CasedKeysNotSupportedWithMnemonicLayout , "The &CasedKeys system store is not supported with mnemonic layouts"},
{ CERR_CannotUseReadWriteGroupFromReadonlyGroup , "Group used from a readonly group must also be readonly"},
{ CERR_StatementNotPermittedInReadonlyGroup , "Statement is not permitted in output of readonly group"},
{ CERR_OutputInReadonlyGroup , "Output is not permitted in a readonly group"},
{ CERR_NewContextGroupMustBeReadonly , "Group used in begin newContext must be readonly"},
{ CERR_PostKeystrokeGroupMustBeReadonly , "Group used in begin postKeystroke must be readonly"},
{ CERR_DuplicateGroup , "A group with this name has already been defined."},
{ CERR_DuplicateStore , "A store with this name has already been defined."},
{ CERR_RepeatedBegin , "Begin has already been set"},
{ CERR_VirtualKeyInContext , "Virtual keys are not permitted in context"},
{ CERR_OutsTooLong , "Store cannot be inserted with outs() as it makes the extended string too long" },
{ CERR_ExtendedStringTooLong , "Extended string is too long" },
{ CERR_VirtualKeyExpansionTooLong , "Virtual key expansion is too large" },
{ CERR_CharacterRangeTooLong , "Character range is too large and cannot be expanded" },
{ CERR_NonBMPCharactersNotSupportedInKeySection , "Characters with codepoints over U+FFFF are not supported in the key part of the rule" },
{ CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"},
{ CHINT_NonUnicodeFile , "Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8"},
{ CHINT_IndexStoreLong , "The store referenced in index() is longer than the store referenced in any()"},
{ CWARN_TooManyWarnings , "Too many warnings or errors"},
{ CWARN_OldVersion , "The keyboard file is an old version"},
{ CWARN_BitmapNotUsed , "The 'bitmaps' statement is obsolete and only the first bitmap referred to will be used, you should use 'bitmap'."},
{ CWARN_CustomLanguagesNotSupported , "Languages over 0x1FF, 0x1F are not supported correctly by Windows. You should use no LANGUAGE line instead."},
{ CWARN_KeyBadLength , "There are too many characters in the keystroke part of the rule."},
{ CWARN_IndexStoreShort , "The store referenced in index() is shorter than the store referenced in any()"},
{ CWARN_UnicodeInANSIGroup , "A Unicode character was found in an ANSI group"},
{ CWARN_ANSIInUnicodeGroup , "An ANSI character was found in a Unicode group"},
{ CWARN_UnicodeSurrogateUsed , "A Unicode surrogate character was found. You should use Unicode scalar values to represent values > U+FFFF"},
{ CWARN_ReservedCharacter , "A Unicode character was found that should not be used"},
{ CWARN_Info , "Information"},
{ CWARN_VirtualKeyWithMnemonicLayout , "Virtual key used instead of virtual character key with a mnemonic layout"},
{ CWARN_VirtualCharKeyWithPositionalLayout , "Virtual character key used with a positional layout instead of mnemonic layout"},
{ CWARN_StoreAlreadyUsedAsOptionOrCall , "Store already used as an option or in a call statement and should not be used as a normal store"},
{ CWARN_StoreAlreadyUsedAsStoreOrCall , "Store already used as a normal store or in a call statement and should not be used as an option"},
{ CWARN_StoreAlreadyUsedAsStoreOrOption , "Store already used as a normal store or as an option and should not be used in a call statement"},
{ CWARN_PunctuationInEthnologueCode , "Punctuation should not be used to separate Ethnologue codes; instead use spaces"},
{ CWARN_PlatformNotInTargets , "The specified platform is not a target platform"},
{ CWARN_HeaderStatementIsDeprecated , "Header statements are deprecated; use instead the equivalent system store"},
{ CWARN_UseNotLastStatementInRule , "A rule with use() statements in the output should not have other content following the use() statements"},
{ CWARN_KVKFileIsInSourceFormat , ".kvk file should be binary but is an XML file"},
{ CWARN_DontMixChiralAndNonChiralModifiers , "Don't mix the use of left/right modifiers with non-left/right modifiers in the same platform"},
{ CWARN_MixingLeftAndRightModifiers , "Left and right modifiers should not both be used in the same rule"},
{ CWARN_LanguageHeadersDeprecatedInKeyman10 , "This language header has been deprecated in Keyman 10. Instead, add language metadata in the package file"},
{ CWARN_HotkeyHasInvalidModifier , "Hotkey has modifiers that are not supported. Use only SHIFT, CTRL and ALT"},
{ CWARN_NulNotFirstStatementInContext , "nul must be the first statement in the context"},
{ CWARN_IfShouldBeAtStartOfContext , "if, platform and baselayout should be at start of context (after nul, if present)"},
{ CWARN_KeyShouldIncludeNCaps , "Other rules which reference this key include CAPS or NCAPS modifiers, so this rule must include NCAPS modifier to avoid inconsistent matches"},
{ CWARN_VirtualKeyInOutput , "Virtual keys are not supported in output"},
{ 0, nullptr }
};
const KMX_CHAR *GetCompilerErrorString(KMX_DWORD code) {
return CompilerErrorMap[code];
}

View file

@ -1,5 +0,0 @@
#include "km_types.h"
#include <kmn_compiler_errors.h>
const KMX_CHAR *GetCompilerErrorString(KMX_DWORD code) ;

View file

@ -20,13 +20,13 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
kmcmp::FMnemonicLayout = FALSE;
if (!fk) {
AddCompileError(CERR_SomewhereIGotItWrong);
ReportCompilerMessage(KmnCompilerMessages::FATAL_SomewhereIGotItWrong);
return FALSE;
}
str = new KMX_WCHAR[LINESIZE];
if (!str) {
AddCompileError(CERR_CannotAllocateMemory);
ReportCompilerMessage(KmnCompilerMessages::FATAL_CannotAllocateMemory);
return FALSE;
}
@ -66,39 +66,42 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
if(kmcmp::FShouldAddCompilerVersion) {
u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", KEYMAN_VersionMajor, KEYMAN_VersionMinor, KEYMAN_VersionPatch, 0);
AddStore(fk, TSS_KEYMANCOPYRIGHT, str);
if(!AddStore(fk, TSS_KEYMANCOPYRIGHT, str)) {
return FALSE;
}
}
/* Add a system store for the Keyman edition number */
AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0");
AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman");
if(!AddStore(fk, TSS_CUSTOMKEYMANEDITION, u"0")) {
return FALSE;
}
if(!AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, u"Keyman")) {
return FALSE;
}
int offset = 0;
// must preprocess for group and store names -> this isn't really necessary, but never mind!
while ((msg = ReadLine(infile, sz, offset, str, TRUE)) == CERR_None)
while ((msg = ReadLine(infile, sz, offset, str, TRUE)) == STATUS_Success)
{
p = str;
switch (LineTokenType(&p))
{
case T_VERSION:
*(p + 4) = 0;
if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) {
AddCompileError(msg);
if (!AddStore(fk, TSS_VERSION, p)) {
return FALSE;
}
break;
case T_GROUP:
if ((msg = ProcessGroupLine(fk, p)) != CERR_None) {
AddCompileError(msg);
if (!ProcessGroupLine(fk, p)) {
return FALSE;
}
break;
case T_STORE:
if ((msg = ProcessStoreLine(fk, p)) != CERR_None) {
AddCompileError(msg);
if (!ProcessStoreLine(fk, p)) {
return FALSE;
}
break;
@ -108,8 +111,8 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
}
}
if (msg != CERR_EndOfFile) {
AddCompileError(msg);
if (msg != STATUS_EndOfFile) {
ReportCompilerMessage(msg);
return FALSE;
}
@ -121,32 +124,30 @@ bool CompileKeyboardBuffer(KMX_BYTE* infile, int sz, PFILE_KEYBOARD fk)
kmcmp::CodeConstants->reindex();
/* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */
while ((msg = ReadLine(infile, sz, offset, str, FALSE)) == CERR_None)
while ((msg = ReadLine(infile, sz, offset, str, FALSE)) == STATUS_Success)
{
msg = ParseLine(fk, str);
if (msg != CERR_None) {
AddCompileError(msg);
if(!ParseLine(fk, str)) {
return FALSE;
}
}
if (msg != CERR_EndOfFile) {
AddCompileError(msg);
if (msg != STATUS_EndOfFile) {
ReportCompilerMessage(msg);
return FALSE;
}
ProcessGroupFinish(fk);
if(!ProcessGroupFinish(fk)) {
return FALSE;
}
if (kmcmp::FSaveDebug) kmcmp::RecordDeadkeyNames(fk);
/* Add the compiler version as a system store */
if ((msg = kmcmp::AddCompilerVersionStore(fk)) != CERR_None) {
AddCompileError(msg);
if (!kmcmp::AddCompilerVersionStore(fk)) {
return FALSE;
}
if ((msg = BuildVKDictionary(fk)) != CERR_None) {
AddCompileError(msg);
if (!BuildVKDictionary(fk)) {
return FALSE;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
#include "pch.h"
#include <kmcmplibapi.h>
#include <kmcmplib.h>
#include <kmn_compiler_errors.h>
namespace kmcmp {
int ErrChr;
int nErrors = 0;
kmcmp_CompilerMessageProc msgproc = nullptr;
void* msgprocContext = NULL;
std::string messageFilename;
}
void ReportCompilerMessage(KMX_DWORD msg, const std::vector<std::string>& parameters) {
const KMX_DWORD severity = msg & MESSAGE_SEVERITY_MASK;
if (severity == CompilerErrorSeverity::Error || severity == CompilerErrorSeverity::Fatal) {
kmcmp::nErrors++;
}
KMCMP_COMPILER_RESULT_MESSAGE message;
message.errorCode = msg;
message.lineNumber = kmcmp::currentLine + 1;
message.columnNumber = kmcmp::ErrChr;
message.parameters = parameters;
message.filename = kmcmp::messageFilename;
(*kmcmp::msgproc)(message, kmcmp::msgprocContext);
kmcmp::ErrChr = 0;
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <vector>
namespace kmcmp {
extern kmcmp_CompilerMessageProc msgproc;
extern void* msgprocContext;
extern std::string messageFilename;
}
void ReportCompilerMessage(KMX_DWORD msg, const std::vector<std::string>& parameters = {});

View file

@ -25,39 +25,38 @@ EXTERN bool kmcmp_CompileKeyboard(
kmcmp::CompileTarget = options.target;
if (!messageProc || !loadFileProc || !pszInfile) {
AddCompileError(CERR_BadCallParams);
ReportCompilerMessage(KmnCompilerMessages::FATAL_BadCallParams);
return FALSE;
}
msgproc = messageProc;
loadfileproc = loadFileProc;
msgprocContext = (void*)procContext;
kmcmp::messageFilename = pszInfile;
kmcmp::msgproc = messageProc;
kmcmp::loadfileproc = loadFileProc;
kmcmp::msgprocContext = (void*)procContext;
kmcmp::currentLine = 0;
kmcmp::nErrors = 0;
int sz;
if(!loadFileProc(pszInfile, "", nullptr, &sz, msgprocContext)) {
AddCompileError(CERR_InfileNotExist);
std::vector<uint8_t> bufvec = kmcmp::loadfileproc(pszInfile, "");
sz = bufvec.size();
if(!sz) {
ReportCompilerMessage(KmnCompilerMessages::ERROR_InfileNotExist);
return FALSE;
}
if(sz < 3) {
// Technically, a 3 byte file can never be a valid .kmn, so we can shortcut
// here and avoid testing outside memory bounds for looking at BOM
AddCompileError(CERR_CannotReadInfile);
ReportCompilerMessage(KmnCompilerMessages::ERROR_CannotReadInfile);
return FALSE;
}
KMX_BYTE* infile = new KMX_BYTE[sz+1];
if(!infile) {
AddCompileError(CERR_CannotAllocateMemory);
return FALSE;
}
if(!loadFileProc(pszInfile, "", infile, &sz, msgprocContext)) {
delete[] infile;
AddCompileError(CERR_CannotReadInfile);
ReportCompilerMessage(KmnCompilerMessages::FATAL_CannotAllocateMemory);
return FALSE;
}
std::copy(bufvec.begin(), bufvec.end(), infile);
infile[sz] = 0; // zero-terminate for safety, not technically needed but helps avoid memory bugs
int offset = 0;
@ -70,7 +69,7 @@ EXTERN bool kmcmp_CompileKeyboard(
int sz16;
if(!UTF16TempFromUTF8(infile, sz, &infile16, &sz16)) {
delete[] infile;
AddCompileError(CERR_CannotCreateTempfile);
ReportCompilerMessage(KmnCompilerMessages::FATAL_CannotCreateTempfile);
return FALSE;
}
delete[] infile;
@ -96,8 +95,8 @@ EXTERN bool kmcmp_CompileKeyboard(
//TODO: FreeKeyboardPointers(fk);
if(msg != CERR_None) {
AddCompileError(msg);
if(msg != STATUS_Success) {
ReportCompilerMessage(msg);
return FALSE;
}

View file

@ -3,49 +3,20 @@
#ifdef __EMSCRIPTEN__
/*
WASM interface for compiler message callback
*/
EM_JS(int, wasm_msgproc, (int line, int msgcode, const char* text, char* context), {
const proc = globalThis[UTF8ToString(context)].message;
if(!proc || typeof proc != 'function') {
console.log(`[${line}: ${msgcode}: ${UTF8ToString(text)}]`);
return 0;
} else {
return proc(line, msgcode, UTF8ToString(text));
struct WasmCallbackInterface {
virtual void message(const KMCMP_COMPILER_RESULT_MESSAGE& message) = 0;
virtual const std::vector<uint8_t> loadFile(const std::string& filename, const std::string& baseFilename) = 0;
virtual ~WasmCallbackInterface() {}
};
struct WasmCallbackInterfaceWrapper : public emscripten::wrapper<WasmCallbackInterface> {
EMSCRIPTEN_WRAPPER(WasmCallbackInterfaceWrapper);
void message(const KMCMP_COMPILER_RESULT_MESSAGE& message) {
return call<void>("message", message);
}
});
EM_JS(int, wasm_loadfileproc, (const char* filename, const char* baseFilename, void* buffer, int bufferSize, char* context), {
const proc = globalThis[UTF8ToString(context)].loadFile;
if(!proc || typeof proc != 'function') {
return 0;
} else {
if(buffer == 0) {
return proc(UTF8ToString(filename), UTF8ToString(baseFilename), 0, 0);
} else {
return proc(UTF8ToString(filename), UTF8ToString(baseFilename), buffer, bufferSize);
}
const std::vector<uint8_t> loadFile(const std::string& filename, const std::string& baseFilename) {
return call<const std::vector<uint8_t>>("loadFile", filename, baseFilename);
}
});
bool wasm_LoadFileProc(const char* filename, const char* baseFilename, void* buffer, int* bufferSize, void* context) {
char* msgProc = static_cast<char*>(context);
if(buffer == nullptr) {
*bufferSize = wasm_loadfileproc(filename, baseFilename, 0, 0, msgProc);
return *bufferSize != -1;
} else {
return wasm_loadfileproc(filename, baseFilename, buffer, *bufferSize, msgProc) == 1;
}
}
int wasm_CompilerMessageProc(int line, uint32_t dwMsgCode, const char* szText, void* context) {
char* msgProc = static_cast<char*>(context);
return wasm_msgproc(line, dwMsgCode, szText, msgProc);
}
struct WASM_COMPILER_INTERFACE {
std::string callbacksKey; // key of callbacks object on globalThis
};
struct WASM_COMPILER_RESULT {
@ -56,16 +27,28 @@ struct WASM_COMPILER_RESULT {
KMCMP_COMPILER_RESULT_EXTRA extra;
};
WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPILER_OPTIONS options, const WASM_COMPILER_INTERFACE intf) {
WasmCallbackInterface* globalCallbacks = nullptr;
const std::vector<uint8_t> wasm_LoadFileProc(const std::string& filename, const std::string& baseFilename) {
return globalCallbacks->loadFile(filename, baseFilename);
}
void wasm_CompilerMessageProc(const KMCMP_COMPILER_RESULT_MESSAGE& message, void* context) {
globalCallbacks->message(message);
}
WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPILER_OPTIONS options, WasmCallbackInterface& callbacks) {
WASM_COMPILER_RESULT r = {false};
KMCMP_COMPILER_RESULT kr;
globalCallbacks = &callbacks;
r.result = kmcmp_CompileKeyboard(
pszInfile.c_str(),
options,
wasm_CompilerMessageProc,
wasm_LoadFileProc,
intf.callbacksKey.c_str(),
"", //TODO: eliminate?
kr
);
@ -76,6 +59,8 @@ WASM_COMPILER_RESULT kmcmp_wasm_compile(std::string pszInfile, const KMCMP_COMPI
r.extra = kr.extra;
}
globalCallbacks = nullptr;
return r;
}
@ -119,6 +104,12 @@ int kmcmp_testSentry() {
EMSCRIPTEN_BINDINGS(compiler_interface) {
emscripten::class_<WasmCallbackInterface>("WasmCallbackInterface")
.function("message", &WasmCallbackInterface::message, emscripten::pure_virtual())
.function("loadFile", &WasmCallbackInterface::loadFile, emscripten::pure_virtual())
.allow_subclass<WasmCallbackInterfaceWrapper>("WasmCallbackInterfaceWrapper")
;
emscripten::class_<KMCMP_COMPILER_OPTIONS>("CompilerOptions")
.constructor<>()
.property("saveDebug", &KMCMP_COMPILER_OPTIONS::saveDebug)
@ -128,11 +119,6 @@ EMSCRIPTEN_BINDINGS(compiler_interface) {
.property("target", &KMCMP_COMPILER_OPTIONS::target)
;
emscripten::class_<WASM_COMPILER_INTERFACE>("CompilerInterface")
.constructor<>()
.property("callbacksKey", &WASM_COMPILER_INTERFACE::callbacksKey)
;
emscripten::class_<WASM_COMPILER_RESULT>("CompilerResult")
.constructor<>()
.property("result", &WASM_COMPILER_RESULT::result)
@ -141,6 +127,15 @@ EMSCRIPTEN_BINDINGS(compiler_interface) {
.property("extra", &WASM_COMPILER_RESULT::extra)
;
emscripten::class_<KMCMP_COMPILER_RESULT_MESSAGE>("CompilerResultMessage")
.constructor<>()
.property("errorCode", &KMCMP_COMPILER_RESULT_MESSAGE::errorCode)
.property("lineNumber", &KMCMP_COMPILER_RESULT_MESSAGE::lineNumber)
.property("columnNumber", &KMCMP_COMPILER_RESULT_MESSAGE::columnNumber)
.property("filename", &KMCMP_COMPILER_RESULT_MESSAGE::filename)
.property("parameters", &KMCMP_COMPILER_RESULT_MESSAGE::parameters)
;
emscripten::class_<KMCMP_COMPILER_RESULT_EXTRA>("CompilerResultExtra")
.constructor<>()
.property("targets", &KMCMP_COMPILER_RESULT_EXTRA::targets)

View file

@ -6,15 +6,14 @@
#include "kmcmplib.h"
#include "DeprecationChecks.h"
KMX_BOOL kmcmp::WarnDeprecatedHeader() { // I4866
if( AWarnDeprecatedCode_GLOBAL_LIB){
AddWarningBool(CWARN_HeaderStatementIsDeprecated);
void kmcmp::WarnDeprecatedHeader() { // I4866
if (AWarnDeprecatedCode_GLOBAL_LIB) {
ReportCompilerMessage(KmnCompilerMessages::WARN_HeaderStatementIsDeprecated);
}
return TRUE;
}
/* Flag presence of deprecated features */
KMX_BOOL kmcmp::CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) {
void kmcmp::CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) {
/*
For Keyman 10, we deprecated:
// < Keyman 7
@ -31,7 +30,7 @@ KMX_BOOL kmcmp::CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) {
PFILE_STORE sp;
if (!AWarnDeprecatedCode_GLOBAL_LIB) {
return TRUE;
return;
}
if (fk->version >= VERSION_100) {
@ -42,12 +41,10 @@ KMX_BOOL kmcmp::CheckForDeprecatedFeatures(PFILE_KEYBOARD fk) {
sp->dwSystemID == TSS_ETHNOLOGUECODE ||
sp->dwSystemID == TSS_WINDOWSLANGUAGES) {
kmcmp::currentLine = sp->line;
AddWarningBool(CWARN_LanguageHeadersDeprecatedInKeyman10);
ReportCompilerMessage(KmnCompilerMessages::WARN_LanguageHeadersDeprecatedInKeyman10);
}
}
}
kmcmp::currentLine = oldCurrentLine;
return TRUE;
}

View file

@ -4,6 +4,6 @@
#include "kmcmplib.h"
namespace kmcmp{
KMX_BOOL WarnDeprecatedHeader();
KMX_BOOL CheckForDeprecatedFeatures(PFILE_KEYBOARD fk);
void WarnDeprecatedHeader();
void CheckForDeprecatedFeatures(PFILE_KEYBOARD fk);
}

View file

@ -120,15 +120,14 @@ KMX_BOOL NamedCodeConstants::LoadFile(PFILE_KEYBOARD fk, const KMX_WCHAR *filena
int FileSize;
KMX_BYTE* Buf;
if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), nullptr, &FileSize, msgprocContext)) {
std::vector<uint8_t> bufvec = loadfileproc(szNameUtf8, fk->extra->kmnFilename);
FileSize = bufvec.size();
if(!FileSize) {
return FALSE;
}
Buf = new KMX_BYTE[FileSize+1];
if(!loadfileproc(szNameUtf8.c_str(), fk->extra->kmnFilename.c_str(), Buf, &FileSize, msgprocContext)) {
delete[] Buf;
return FALSE;
}
std::copy(bufvec.begin(), bufvec.end(), Buf);
Buf[FileSize] = 0; // zero-terminate for strtok
char* filetok;

View file

@ -25,7 +25,8 @@ namespace kmcmp {
}
}
KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp) {
void VerifyUnreachableRules(PFILE_GROUP gp) {
PFILE_KEY kp = gp->dpKeyArray;
KMX_DWORD i;
@ -41,8 +42,9 @@ KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp) {
if (kp->Line != k1.Line && reportedLines.count(kp->Line) == 0) {
reportedLines.insert(kp->Line);
kmcmp::currentLine = kp->Line;
snprintf(ErrExtraLIB, ERR_EXTRA_LIB_LEN, " Overridden by rule on line %d", k1.Line);
AddWarning(CHINT_UnreachableRule);
ReportCompilerMessage(KmnCompilerMessages::HINT_UnreachableRule, {
/* LineNumber */ std::to_string(k1.Line)
});
}
}
else {
@ -51,6 +53,4 @@ KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp) {
}
kmcmp::currentLine = oldCurrentLine;
return CERR_None;
}

View file

@ -1,3 +1,3 @@
#pragma once
KMX_DWORD VerifyUnreachableRules(PFILE_GROUP gp);
void VerifyUnreachableRules(PFILE_GROUP gp);

View file

@ -31,7 +31,6 @@
#define SZMAX_STORENAME 80
#define SZMAX_GROUPNAME 80
#define SZMAX_DEADKEYNAME 80
#define SZMAX_ERRORTEXT 512
#define SZMAX_VKDICTIONARYNAME 80
#define MAX_WARNINGS 100
@ -167,26 +166,4 @@ const KMX_DWORD sz_FILE_DEADKEY = sizeof(FILE_DEADKEY);
const KMX_DWORD sz_FILE_VKDICTIONARY = sizeof(FILE_VKDICTIONARY);
const KMX_DWORD sz_FILE_KEYBOARD = sizeof(FILE_KEYBOARD);
struct COMPMSG {
KMX_CHAR szText[SZMAX_ERRORTEXT];
KMX_DWORD Line;
KMX_DWORD dwMsgCode;
};
typedef COMPMSG *PCOMPMSG;
struct COMPILEMESSAGES {
int nMessages;
int nErrors;
PCOMPMSG cm;
KMX_DWORD fatalCode;
KMX_CHAR szFatalText[SZMAX_ERRORTEXT];
KMX_DWORD currentLine;
};
typedef COMPILEMESSAGES *PCOMPILEMESSAGES;
#endif // _COMPFILE_H

View file

@ -3,9 +3,9 @@
#include <kmcmplibapi.h>
#include "compfile.h"
#include "NamedCodeConstants.h"
#include <CompilerErrors.h>
namespace kmcmp {
KMX_BOOL AddCompileWarning(char* buf);
extern int currentLine;
extern KMX_BOOL FShouldAddCompilerVersion;
extern KMX_BOOL FSaveDebug, FCompilerWarningsAsErrors; // I4865 // I4866
@ -17,34 +17,24 @@ namespace kmcmp {
extern int BeginLine[4];
extern int currentLine;
extern NamedCodeConstants *CodeConstants;
extern kmcmp_LoadFileProc loadfileproc;
void RecordDeadkeyNames(PFILE_KEYBOARD fk);
KMX_DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk);
KMX_BOOL AddCompilerVersionStore(PFILE_KEYBOARD fk);
}
extern kmcmp_CompilerMessageProc msgproc;
extern kmcmp_LoadFileProc loadfileproc;
extern void* msgprocContext;
extern KMX_BOOL AWarnDeprecatedCode_GLOBAL_LIB;
#define ERR_EXTRA_LIB_LEN 256
extern char ErrExtraLIB[ERR_EXTRA_LIB_LEN];
KMX_BOOL AddCompileError(KMX_DWORD msg);
/// Use AddWarningBool for functions that return bool or KMX_BOOL
#define AddWarningBool(warn) { if(AddCompileError(warn)) return FALSE; }
/// Use AddWarning for functions that return KMX_DWORD
#define AddWarning(warn) { if(AddCompileError(warn)) return CERR_Break; }
PKMX_WCHAR strtowstr(PKMX_STR in);
PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID);
bool UTF16TempFromUTF8(KMX_BYTE* infile, int sz, KMX_BYTE** tempfile, int *sz16);
KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize);
KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID= NULL);
KMX_BOOL AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID= NULL);
KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess);
KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str);
KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk);
KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_BOOL ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str);
KMX_BOOL ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_BOOL ProcessGroupFinish(PFILE_KEYBOARD fk);
KMX_BOOL ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
int LineTokenType(PKMX_WCHAR *str);
KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk); // I3438
KMX_BOOL BuildVKDictionary(PFILE_KEYBOARD fk); // I3438

View file

@ -72,9 +72,9 @@ lib = library('kmcmplib',
'CheckNCapsConsistency.cpp',
'CompileKeyboardBuffer.cpp',
'Compiler.cpp',
'CompilerErrors.cpp',
'CompilerInterfaces.cpp',
'CompilerInterfacesWasm.cpp',
'CompMsg.cpp',
'cp1252.cpp',
'DeprecationChecks.cpp',
'Edition.cpp',

View file

@ -5,8 +5,6 @@
#include "versioning.h"
KMX_BOOL kmcmp::CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) {
KMX_CHAR buf[128];
if (fk->dwFlags & KF_AUTOMATICVERSION) {
if (fk->version <= 0) {
fk->version = VERSION_60; // minimum version that we can be safe with
@ -15,8 +13,10 @@ KMX_BOOL kmcmp::CheckKeyboardFinalVersion(PFILE_KEYBOARD fk) {
if(kmcmp::CompileTarget != CKF_KEYMANWEB) {
// Note: the KeymanWeb compiler is responsible for reporting minimum
// version for the web targets
snprintf(buf, 128, "The compiler has assigned a minimum engine version of %d.%d based on features used in this keyboard", (int)((fk->version & 0xFF00) >> 8), (int)(fk->version & 0xFF));
AddCompileWarning(buf);
ReportCompilerMessage(KmnCompilerMessages::INFO_MinimumCoreEngineVersion, {
/* majorVersion */ std::to_string((fk->version & 0xFF00) >> 8),
/* minorVersion */ std::to_string(fk->version & 0xFF)
});
}
}

View file

@ -3,11 +3,6 @@
#include "compfile.h"
#include "../../../../common/include/km_types.h"
#define VERIFY_KEYBOARD_VERSION(fk, ver, err) { \
if(!VerifyKeyboardVersion((fk), (ver))) \
return (err); \
}
namespace kmcmp {
KMX_BOOL CheckKeyboardFinalVersion(PFILE_KEYBOARD fk);
}

View file

@ -34,6 +34,7 @@ int main(int argc, char *argv[]) {
setup();
test_kmcmp_CompileKeyboard(argv[1]);
setup();
test_GetCompileTargetsFromTargetsStore();
return 0;
@ -65,35 +66,72 @@ void test_kmcmp_CompileKeyboard(char *kmn_file) {
options.target = CKF_KEYMAN;
assert(!kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result));
assert(error_vec.size() == 1);
assert(error_vec[0] == CERR_CannotReadInfile);
assert(error_vec[0] == KmnCompilerMessages::ERROR_InfileNotExist); // zero byte no longer gives us KmnCompilerMessages::ERROR_CannotReadInfile
unlink(kmn_file);
}
extern KMX_DWORD GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store, int &targets);
extern KMX_BOOL GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store, int &targets);
void test_GetCompileTargetsFromTargetsStore() {
int targets = 0;
assert(GetCompileTargetsFromTargetsStore(u"any", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"any", targets));
assert(error_vec.size() == 0);
assert(targets == (COMPILETARGETS_KMX | COMPILETARGETS_JS));
assert(GetCompileTargetsFromTargetsStore(u"windows", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"windows", targets));
assert(error_vec.size() == 0);
assert(targets == COMPILETARGETS_KMX);
assert(GetCompileTargetsFromTargetsStore(u"desktop", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"desktop", targets));
assert(error_vec.size() == 0);
assert(targets == COMPILETARGETS_KMX);
assert(GetCompileTargetsFromTargetsStore(u"mobile", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"mobile", targets));
assert(error_vec.size() == 0);
assert(targets == COMPILETARGETS_JS);
assert(GetCompileTargetsFromTargetsStore(u"web", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"web", targets));
assert(error_vec.size() == 0);
assert(targets == COMPILETARGETS_JS);
assert(GetCompileTargetsFromTargetsStore(u"desktop mobile", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"desktop mobile", targets));
assert(error_vec.size() == 0);
assert(targets == (COMPILETARGETS_KMX | COMPILETARGETS_JS));
assert(GetCompileTargetsFromTargetsStore(u"desktop tablet", targets) == CERR_None);
setup();
assert(GetCompileTargetsFromTargetsStore(u"desktop tablet", targets));
assert(error_vec.size() == 0);
assert(targets == (COMPILETARGETS_KMX | COMPILETARGETS_JS));
assert(GetCompileTargetsFromTargetsStore(u"foo bar baz", targets) == CERR_InvalidTarget);
setup();
assert(!GetCompileTargetsFromTargetsStore(u"foo bar baz", targets));
assert(error_vec.size() == 1);
assert(error_vec[0] == KmnCompilerMessages::ERROR_InvalidTarget);
assert(targets == 0);
assert(GetCompileTargetsFromTargetsStore(u"windows chromeos", targets) == CERR_InvalidTarget);
setup();
assert(!GetCompileTargetsFromTargetsStore(u"windows chromeos", targets));
assert(error_vec.size() == 1);
assert(error_vec[0] == KmnCompilerMessages::ERROR_InvalidTarget);
assert(targets == 0);
assert(GetCompileTargetsFromTargetsStore(u" ", targets) == CERR_NoTargetsSpecified);
setup();
assert(!GetCompileTargetsFromTargetsStore(u" ", targets));
assert(error_vec.size() == 1);
assert(error_vec[0] == KmnCompilerMessages::ERROR_NoTargetsSpecified);
assert(targets == 0);
assert(GetCompileTargetsFromTargetsStore(u"", targets) == CERR_NoTargetsSpecified);
setup();
assert(!GetCompileTargetsFromTargetsStore(u"", targets));
assert(error_vec.size() == 1);
assert(error_vec[0] == KmnCompilerMessages::ERROR_NoTargetsSpecified);
assert(targets == 0);
}

View file

@ -3,15 +3,14 @@
#include "../include/kmcmplibapi.h"
#include "../src/kmx_u16.h"
#include "../src/compfile.h"
#include "../src/CompMsg.h"
#include "../src/CompilerErrors.h"
#include "../../common/include/kmn_compiler_errors.h"
#include "../../../../common/include/km_types.h"
#include "../../../../common/include/kmx_file.h"
PKMX_WCHAR strtowstr(PKMX_STR in);
PKMX_STR wstrtostr(PKMX_WCHAR in);
KMX_BOOL AddCompileError(KMX_DWORD msg);
KMX_DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_BOOL ProcessBeginLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD ValidateMatchNomatchOutput(PKMX_WCHAR p);
KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString);
PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags);
@ -25,19 +24,13 @@ bool isIntegerWstring(PKMX_WCHAR p);
bool hasPreamble(std::u16string result);
KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode, PKMX_WCHAR pklIn, PKMX_WCHAR pklKey, PKMX_WCHAR pklOut);
extern kmcmp_CompilerMessageProc msgproc;
namespace kmcmp {
extern int nErrors;
extern int ErrChr;
extern int BeginLine[4];
extern int CompileTarget;
KMX_BOOL AddCompileWarning(char* buf);
}
#define ERR_EXTRA_LIB_LEN 256
extern char ErrExtraLIB[ERR_EXTRA_LIB_LEN];
class CompilerTest : public testing::Test {
protected:
FILE_KEYBOARD fileKeyboard;
@ -53,11 +46,10 @@ class CompilerTest : public testing::Test {
}
void initGlobals() {
msgproc = NULL;
szText_stub[0] = '\0';
kmcmp::msgproc = msgproc_collect;
msgproc_errors.clear();
kmcmp::nErrors = 0;
kmcmp::ErrChr = 0;
ErrExtraLIB[0] = '\0';
kmcmp::BeginLine[BEGIN_ANSI] = -1;
kmcmp::BeginLine[BEGIN_UNICODE] = -1;
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
@ -115,21 +107,14 @@ class CompilerTest : public testing::Test {
}
public:
static KMX_CHAR szText_stub[];
static std::vector<KMCMP_COMPILER_RESULT_MESSAGE> msgproc_errors;
static int msgproc_true_stub(int line, uint32_t dwMsgCode, const char* szText, void* context) {
strcpy(szText_stub, szText);
return 1;
};
static int msgproc_false_stub(int line, uint32_t dwMsgCode, const char* szText, void* context) {
strcpy(szText_stub, szText);
return 0;
};
static void msgproc_collect(const KMCMP_COMPILER_RESULT_MESSAGE &message, void* context) {
msgproc_errors.push_back(message);
}
};
#define COMPILE_ERROR_MAX_LEN (SZMAX_ERRORTEXT + 1 + 280)
KMX_CHAR CompilerTest::szText_stub[COMPILE_ERROR_MAX_LEN];
std::vector<KMCMP_COMPILER_RESULT_MESSAGE> CompilerTest::msgproc_errors;
TEST_F(CompilerTest, strtowstr_test) {
EXPECT_EQ(0, u16cmp(u"hello", strtowstr((PKMX_STR)"hello")));
@ -141,129 +126,120 @@ TEST_F(CompilerTest, wstrtostr_test) {
EXPECT_EQ(0, strcmp("", wstrtostr((PKMX_WCHAR)u"")));
};
TEST_F(CompilerTest, AddCompileWarning_test) {
msgproc = msgproc_false_stub;
const char *const WARNING_TEXT = "warning";
EXPECT_EQ(0, kmcmp::nErrors);
EXPECT_FALSE(kmcmp::AddCompileWarning((PKMX_CHAR)WARNING_TEXT));
EXPECT_EQ(0, strcmp(WARNING_TEXT, szText_stub));
EXPECT_EQ(0, kmcmp::nErrors);
};
// TEST_F(CompilerTest, ReportCompilerMessage_test) {
// msgproc = msgproc_true_stub;
// kmcmp::ErrChr = 0;
TEST_F(CompilerTest, AddCompileError_test) {
msgproc = msgproc_true_stub;
kmcmp::ErrChr = 0;
ErrExtraLIB[0] = '\0';
KMX_CHAR expected[COMPILE_ERROR_MAX_LEN];
// // SevFatal
// EXPECT_EQ(0, kmcmp::nErrors);
// EXPECT_EQ(SevFatal, KmnCompilerMessages::FATAL_CannotCreateTempfile & SevFatal);
// EXPECT_TRUE(ReportCompilerMessage(KmnCompilerMessages::FATAL_CannotCreateTempfile));
// EXPECT_EQ(1, kmcmp::nErrors);
// CERR_FATAL
EXPECT_EQ(0, kmcmp::nErrors);
EXPECT_EQ(CERR_FATAL, CERR_CannotCreateTempfile & CERR_FATAL);
EXPECT_TRUE(AddCompileError(CERR_CannotCreateTempfile));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_CannotCreateTempfile), szText_stub));
EXPECT_EQ(1, kmcmp::nErrors);
// // SevError
// EXPECT_EQ(SevError, KmnCompilerMessages::ERROR_InvalidLayoutLine & SevError);
// EXPECT_FALSE(ReportCompilerMessage(KmnCompilerMessages::ERROR_InvalidLayoutLine));
// EXPECT_EQ(2, kmcmp::nErrors);
// CERR_ERROR
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_InvalidLayoutLine), szText_stub));
EXPECT_EQ(2, kmcmp::nErrors);
// // Unknown
// const KMX_DWORD UNKNOWN_ERROR = 0x00004FFF; // top of range ERROR
// EXPECT_EQ(SevError, UNKNOWN_ERROR & SevError);
// EXPECT_FALSE(ReportCompilerMessage(UNKNOWN_ERROR));
// sprintf(expected, "Unknown error %x", UNKNOWN_ERROR);
// EXPECT_EQ(3, kmcmp::nErrors);
// Unknown
const KMX_DWORD UNKNOWN_ERROR = 0x00004FFF; // top of range ERROR
EXPECT_EQ(CERR_ERROR, UNKNOWN_ERROR & CERR_ERROR);
EXPECT_FALSE(AddCompileError(UNKNOWN_ERROR));
sprintf(expected, "Unknown error %x", UNKNOWN_ERROR);
EXPECT_EQ(0, strcmp(expected, szText_stub));
EXPECT_EQ(3, kmcmp::nErrors);
// // ErrChr
// const int ERROR_CHAR_INDEX = 42;
// kmcmp::ErrChr = ERROR_CHAR_INDEX ;
// EXPECT_EQ(SevError, KmnCompilerMessages::ERROR_InvalidLayoutLine & SevError);
// EXPECT_FALSE(ReportCompilerMessage(KmnCompilerMessages::ERROR_InvalidLayoutLine));
// kmcmp::ErrChr = 0;
// EXPECT_EQ(4, kmcmp::nErrors);
// ErrChr
const int ERROR_CHAR_INDEX = 42;
kmcmp::ErrChr = ERROR_CHAR_INDEX ;
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
sprintf(expected, "%s character offset: %d", GetCompilerErrorString(CERR_InvalidLayoutLine), ERROR_CHAR_INDEX);
EXPECT_EQ(0, strcmp(expected, szText_stub));
kmcmp::ErrChr = 0;
EXPECT_EQ(4, kmcmp::nErrors);
// ErrExtraLIB
const char *const EXTRA_LIB_TEXT = " extra lib";
strcpy(ErrExtraLIB, EXTRA_LIB_TEXT);
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_FALSE(AddCompileError(CERR_InvalidLayoutLine));
sprintf(expected, "%s%s", GetCompilerErrorString(CERR_InvalidLayoutLine), EXTRA_LIB_TEXT);
EXPECT_EQ(0, strcmp(expected, szText_stub));
ErrExtraLIB[0] = '\0';
EXPECT_EQ(5, kmcmp::nErrors);
// msgproc returns FALSE
msgproc = msgproc_false_stub;
EXPECT_EQ(CERR_ERROR, CERR_InvalidLayoutLine & CERR_ERROR);
EXPECT_TRUE(AddCompileError(CERR_InvalidLayoutLine));
EXPECT_EQ(0, strcmp(GetCompilerErrorString(CERR_InvalidLayoutLine), szText_stub));
EXPECT_EQ(6, kmcmp::nErrors);
};
// // msgproc returns FALSE
// msgproc = msgproc_false_stub;
// EXPECT_EQ(SevError, KmnCompilerMessages::ERROR_InvalidLayoutLine & SevError);
// EXPECT_TRUE(ReportCompilerMessage(KmnCompilerMessages::ERROR_InvalidLayoutLine));
// EXPECT_EQ(6, kmcmp::nErrors);
// };
TEST_F(CompilerTest, ProcessBeginLine_test) {
KMX_WCHAR str[LINESIZE];
// CERR_NoTokensFound
// KmnCompilerMessages::ERROR_NoTokensFound
msgproc_errors.clear();
u16cpy(str, u"");
EXPECT_EQ(CERR_NoTokensFound, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_NoTokensFound, msgproc_errors[0].errorCode);
// CERR_InvalidToken
// KmnCompilerMessages::ERROR_InvalidToken
msgproc_errors.clear();
u16cpy(str, u"abc >");
EXPECT_EQ(CERR_InvalidToken, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, msgproc_errors[0].errorCode);
// CERR_RepeatedBegin, BEGIN_UNICODE
// KmnCompilerMessages::ERROR_RepeatedBegin, BEGIN_UNICODE
msgproc_errors.clear();
kmcmp::BeginLine[BEGIN_UNICODE] = 0; // not -1
u16cpy(str, u" unicode>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_RepeatedBegin, msgproc_errors[0].errorCode);
kmcmp::BeginLine[BEGIN_UNICODE] = -1;
// CERR_RepeatedBegin, BEGIN_ANSI
// KmnCompilerMessages::ERROR_RepeatedBegin, BEGIN_ANSI
msgproc_errors.clear();
kmcmp::BeginLine[BEGIN_ANSI] = 0; // not -1
u16cpy(str, u" ansi>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_RepeatedBegin, msgproc_errors[0].errorCode);
kmcmp::BeginLine[BEGIN_ANSI] = -1;
// CERR_RepeatedBegin, BEGIN_NEWCONTEXT
// KmnCompilerMessages::ERROR_RepeatedBegin, BEGIN_NEWCONTEXT
msgproc_errors.clear();
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = 0; // not -1
u16cpy(str, u" newContext>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_RepeatedBegin, msgproc_errors[0].errorCode);
kmcmp::BeginLine[BEGIN_NEWCONTEXT] = -1;
// CERR_RepeatedBegin, BEGIN_POSTKEYSTROKE
// KmnCompilerMessages::ERROR_RepeatedBegin, BEGIN_POSTKEYSTROKE
msgproc_errors.clear();
kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = 0; // not -1
u16cpy(str, u" postKeystroke>");
EXPECT_EQ(CERR_RepeatedBegin, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(FALSE, ProcessBeginLine(&fileKeyboard, str));
EXPECT_EQ(1, msgproc_errors.size());
EXPECT_EQ(KmnCompilerMessages::ERROR_RepeatedBegin, msgproc_errors[0].errorCode);
kmcmp::BeginLine[BEGIN_POSTKEYSTROKE] = -1;
};
TEST_F(CompilerTest, ValidateMatchNomatchOutput_test) {
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput(NULL));
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput((PKMX_WCHAR)u""));
EXPECT_EQ(STATUS_Success, ValidateMatchNomatchOutput(NULL));
EXPECT_EQ(STATUS_Success, ValidateMatchNomatchOutput((PKMX_WCHAR)u""));
const KMX_WCHAR context[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_CONTEXT, 'd', 'e', 'f', 0 };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)context));
EXPECT_EQ(KmnCompilerMessages::ERROR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)context));
const KMX_WCHAR contextex[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_CONTEXTEX, 'd', 'e', 'f', 0 };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)contextex));
EXPECT_EQ(KmnCompilerMessages::ERROR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)contextex));
const KMX_WCHAR index[] = { 'a', 'b', 'c', UC_SENTINEL, CODE_INDEX, 'd', 'e', 'f', 0 };
EXPECT_EQ(CERR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)index));
EXPECT_EQ(KmnCompilerMessages::ERROR_ContextAndIndexInvalidInMatchNomatch, ValidateMatchNomatchOutput((PKMX_WCHAR)index));
const KMX_WCHAR sentinel[] = { 'a', 'b', 'c', UC_SENTINEL, 'd', 'e', 'f', 0 };
EXPECT_EQ(CERR_None, ValidateMatchNomatchOutput((PKMX_WCHAR)sentinel));
EXPECT_EQ(STATUS_Success, ValidateMatchNomatchOutput((PKMX_WCHAR)sentinel));
};
// KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str)
// KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// KMX_BOOL ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str)
// KMX_BOOL ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// int kmcmp::cmpkeys(const void *key, const void *elem)
// KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk)
// KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// KMX_BOOL ProcessGroupFinish(PFILE_KEYBOARD fk)
// KMX_BOOL ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// bool resizeStoreArray(PFILE_KEYBOARD fk)
// bool resizeKeyArray(PFILE_GROUP gp, int increment)
// KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID)
// KMX_BOOL AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, const KMX_WCHAR * str, KMX_DWORD *dwStoreID)
// KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str)
// KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp)
// KMX_BOOL ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp)
// int GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store)
TEST_F(CompilerTest, IsValidKeyboardVersion_test) {
@ -281,7 +257,7 @@ TEST_F(CompilerTest, IsValidKeyboardVersion_test) {
// KMX_DWORD kmcmp::AddCompilerVersionStore(PFILE_KEYBOARD fk)
// KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR context, PKMX_WCHAR output, PKMX_WCHAR key)
// KMX_BOOL CheckContextStatementPositions(PKMX_WCHAR context)
// void CheckContextStatementPositions(PKMX_WCHAR context)
// KMX_DWORD CheckUseStatementsInOutput(PKMX_WCHAR output)
// KMX_DWORD CheckVirtualKeysInOutput(PKMX_WCHAR output)
// KMX_DWORD InjectContextToReadonlyOutput(PKMX_WCHAR pklOut)
@ -300,10 +276,10 @@ TEST_F(CompilerTest, ProcessKeyLineImpl_test) {
// #11643: non-BMP characters do not makes sense for key codes
u16cpy(str, u"+ 'A' > 'test'\n"); // baseline
EXPECT_EQ(CERR_None, ProcessKeyLineImpl(&fileKeyboard, str, TRUE, pklIn, pklKey, pklOut));
EXPECT_EQ(STATUS_Success, ProcessKeyLineImpl(&fileKeyboard, str, TRUE, pklIn, pklKey, pklOut));
u16cpy(str, u"+ '\U00010000' > 'test'\n"); // surrogate pair
EXPECT_EQ(CERR_NonBMPCharactersNotSupportedInKeySection, ProcessKeyLineImpl(&fileKeyboard, str, TRUE, pklIn, pklKey, pklOut));
EXPECT_EQ(KmnCompilerMessages::ERROR_NonBMPCharactersNotSupportedInKeySection, ProcessKeyLineImpl(&fileKeyboard, str, TRUE, pklIn, pklKey, pklOut));
delete[] pklIn;
delete[] pklKey;
@ -672,22 +648,22 @@ TEST_F(CompilerTest, GetXStringImpl_test) {
PKMX_WCHAR newp = nullptr;
KMX_WCHAR token[128];
// CERR_BufferOverflow, max=0
EXPECT_EQ(CERR_BufferOverflow, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 0, 0, &newp, FALSE));
// KmnCompilerMessages::FATAL_BufferOverflow, max=0
EXPECT_EQ(KmnCompilerMessages::FATAL_BufferOverflow, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 0, 0, &newp, FALSE));
// CERR_None, no token
// STATUS_Success, no token
u16cpy(str, u"");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_NoTokensFound, empty
// KmnCompilerMessages::ERROR_NoTokensFound, empty
u16cpy(str, u"");
u16cpy(token, u"c");
EXPECT_EQ(CERR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str, token, output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str,token, output, 80, 0, &newp, FALSE));
// CERR_NoTokensFound, whitespace
// KmnCompilerMessages::ERROR_NoTokensFound, whitespace
u16cpy(str, u" ");
u16cpy(token, u"c");
EXPECT_EQ(CERR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str, token, output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_NoTokensFound, GetXStringImpl(tstr, &fileKeyboard, str, token, output, 80, 0, &newp, FALSE));
}
// tests strings starting with 'x' or 'd'
@ -699,28 +675,28 @@ TEST_F(CompilerTest, GetXStringImpl_type_xd_test) {
// hex 32-bit
u16cpy(str, u"x10330"); // Gothic A
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_GothicA[] = { 0xD800, 0xDF30, 0 }; // see UTF32ToUTF16
EXPECT_EQ(0, u16cmp(tstr_GothicA, tstr));
// decimal 8-bit
u16cpy(str, u"d18");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"\u0012", tstr));
// hex capital 8-bit
u16cpy(str, u"X12");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"\u0012", tstr));
// hex 32-bit, CERR_InvalidCharacter
// hex 32-bit, KmnCompilerMessages::ERROR_InvalidCharacter
u16cpy(str, u"x110000");
EXPECT_EQ(CERR_InvalidCharacter, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidCharacter, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, valid
u16cpy(str, u"dk(A)");
EXPECT_EQ(0, (int)fileKeyboard.cxDeadKeyArray);
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_dk_valid[] = { UC_SENTINEL, CODE_DEADKEY, 1, 0 }; // setup deadkeys
EXPECT_EQ(0, u16cmp(tstr_dk_valid, tstr));
fileKeyboard.cxDeadKeyArray = 0;
@ -728,22 +704,22 @@ TEST_F(CompilerTest, GetXStringImpl_type_xd_test) {
// deadkey, valid
u16cpy(str, u"deadkey(A)");
EXPECT_EQ(0, (int)fileKeyboard.cxDeadKeyArray);
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_deadkey_valid[] = { UC_SENTINEL, CODE_DEADKEY, 1, 0 }; // setup deadkeys
EXPECT_EQ(0, u16cmp(tstr_deadkey_valid, tstr));
fileKeyboard.cxDeadKeyArray = 0;
// dk, CERR_InvalidDeadkey, bad character
// dk, KmnCompilerMessages::ERROR_InvalidDeadkey, bad character
u16cpy(str, u"dk(%)");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, CERR_InvalidDeadkey, no close delimiter => NULL
// dk, KmnCompilerMessages::ERROR_InvalidDeadkey, no close delimiter => NULL
u16cpy(str, u"dk(");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// dk, CERR_InvalidDeadkey, empty delimiters => empty string
// dk, KmnCompilerMessages::ERROR_InvalidDeadkey, empty delimiters => empty string
u16cpy(str, u"dk()");
EXPECT_EQ(CERR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidDeadkey, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
}
// tests strings starting with double quote
@ -755,18 +731,18 @@ TEST_F(CompilerTest, GetXStringImpl_type_double_quote_test) {
// valid
u16cpy(str, u"\"abc\"");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"abc", tstr));
// CERR_UnterminatedString
// KmnCompilerMessages::ERROR_UnterminatedString
u16cpy(str, u"\"abc");
EXPECT_EQ(CERR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_ExtendedStringTooLong
// KmnCompilerMessages::ERROR_ExtendedStringTooLong
u16cpy(str, u"\"abc\"");
EXPECT_EQ(CERR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
EXPECT_EQ(KmnCompilerMessages::ERROR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
// CERR_StringInVirtualKeySection *** TODO ***
// KmnCompilerMessages::ERROR_StringInVirtualKeySection *** TODO ***
}
// tests strings starting with single quote
@ -778,18 +754,18 @@ TEST_F(CompilerTest, GetXStringImpl_type_single_quote_test) {
// valid
u16cpy(str, u"\'abc\'");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(0, u16cmp(u"abc", tstr));
// CERR_UnterminatedString
// KmnCompilerMessages::ERROR_UnterminatedString
u16cpy(str, u"\'abc");
EXPECT_EQ(CERR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_UnterminatedString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_ExtendedStringTooLong
// KmnCompilerMessages::ERROR_ExtendedStringTooLong
u16cpy(str, u"\'abc\'");
EXPECT_EQ(CERR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
EXPECT_EQ(KmnCompilerMessages::ERROR_ExtendedStringTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 2, 0, &newp, FALSE)); // max reduced to force error
// CERR_StringInVirtualKeySection *** TODO ***
// KmnCompilerMessages::ERROR_StringInVirtualKeySection *** TODO ***
}
// tests strings starting with 'a'
@ -805,59 +781,59 @@ TEST_F(CompilerTest, GetXStringImpl_type_a_test) {
u16cpy(file_store[1].szName, u"b");
u16cpy(file_store[2].szName, u"c");
// CERR_InvalidToken
// KmnCompilerMessages::ERROR_InvalidToken
u16cpy(str, u"abc");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_AnyInVirtualKeySection *** TODO ***
// KmnCompilerMessages::ERROR_AnyInVirtualKeySection *** TODO ***
// CERR_InvalidAny, no close delimiter => NULL
// KmnCompilerMessages::ERROR_InvalidAny, no close delimiter => NULL
u16cpy(str, u"any(");
EXPECT_EQ(CERR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_InvalidAny, empty delimiters => empty string
// KmnCompilerMessages::ERROR_InvalidAny, empty delimiters => empty string
u16cpy(str, u"any()");
EXPECT_EQ(CERR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_InvalidAny, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
// KmnCompilerMessages::ERROR_InvalidAny, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
u16cpy(str, u"any( )");
EXPECT_EQ(CERR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidAny, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_StoreDoesNotExist
// KmnCompilerMessages::ERROR_StoreDoesNotExist
u16cpy(str, u"any(d)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_StoreDoesNotExist, space before store
// KmnCompilerMessages::ERROR_StoreDoesNotExist, space before store
u16cpy(str, u"any( d)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_StoreDoesNotExist, space after store
// KmnCompilerMessages::ERROR_StoreDoesNotExist, space after store
u16cpy(str, u"any(d )");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// CERR_ZeroLengthString
// KmnCompilerMessages::ERROR_ZeroLengthString
u16cpy(str, u"any(b)");
file_store[1].dpString = (PKMX_WCHAR)u"";
EXPECT_EQ(CERR_ZeroLengthString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_ZeroLengthString, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// valid
u16cpy(str, u"any(b)");
file_store[1].dpString = (PKMX_WCHAR)u"abc"; // non-empty
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_any_valid[] = { UC_SENTINEL, CODE_ANY, 2, 0 };
EXPECT_EQ(0, u16cmp(tstr_any_valid, tstr));
// space before store, valid
u16cpy(str, u"any( b)");
file_store[1].dpString = (PKMX_WCHAR)u"abc"; // non-empty
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_any_space_before_valid[] = { UC_SENTINEL, CODE_ANY, 2, 0 };
EXPECT_EQ(0, u16cmp(tstr_any_space_before_valid, tstr));
// space after store, valid (see I11937, #11938)
u16cpy(str, u"any(b )");
file_store[1].dpString = (PKMX_WCHAR)u"abc"; // non-empty
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_any_space_after_valid[] = { UC_SENTINEL, CODE_ANY, 2, 0 };
EXPECT_EQ(0, u16cmp(tstr_any_space_after_valid, tstr));
}
@ -870,53 +846,53 @@ TEST_F(CompilerTest, GetXStringImpl_type_b_test) {
KMX_WCHAR output[GLOBAL_BUFSIZE];
PKMX_WCHAR newp = nullptr;
// CERR_InvalidToken
// KmnCompilerMessages::ERROR_InvalidToken
u16cpy(str, u"bcd");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// beep, CERR_BeepInVirtualKeySection *** TODO ***
// beep, KmnCompilerMessages::ERROR_BeepInVirtualKeySection *** TODO ***
// beep, valid
u16cpy(str, u"beep");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_beep_valid[] = { UC_SENTINEL, CODE_BEEP, 0 };
EXPECT_EQ(0, u16cmp(tstr_beep_valid, tstr));
// baselayout, CERR_90FeatureOnly_IfSystemStores
// baselayout, KmnCompilerMessages::ERROR_90FeatureOnly_IfSystemStores
fileKeyboard.version = VERSION_80;
fileKeyboard.dwFlags = 0u;
u16cpy(str, u"baselayout");
EXPECT_EQ(CERR_90FeatureOnly_IfSystemStores, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_90FeatureOnly_IfSystemStores, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
fileKeyboard.version = VERSION_90;
// baselayout, CERR_InvalidInVirtualKeySection *** TODO ***
// baselayout, KmnCompilerMessages::ERROR_InvalidInVirtualKeySection *** TODO ***
// baselayout, no close delimiter => NULL
fileKeyboard.version = VERSION_90;
u16cpy(str, u"baselayout(");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// baselayout, empty delimiters => empty string
fileKeyboard.version = VERSION_90;
u16cpy(str, u"baselayout()");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// baselayout, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
fileKeyboard.version = VERSION_90;
u16cpy(str, u"baselayout( )");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// baselayout, CERR_InvalidToken from process_baselayout
// baselayout, KmnCompilerMessages::ERROR_InvalidToken from process_baselayout
fileKeyboard.version = VERSION_90;
u16cpy(str, u"baselayout(abc)");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// baselayout, valid
fileKeyboard.version = VERSION_90;
fileKeyboard.cxStoreArray = 0;
fileKeyboard.dpStoreArray = nullptr;
u16cpy(str, u"baselayout(beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_baselayout_valid[] = { UC_SENTINEL, CODE_IFSYSTEMSTORE, TSS_BASELAYOUT+1, 2, 1, 0 };
EXPECT_EQ(0, u16cmp(tstr_baselayout_valid, tstr));
@ -925,7 +901,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_b_test) {
fileKeyboard.cxStoreArray = 0;
fileKeyboard.dpStoreArray = nullptr;
u16cpy(str, u"baselayout( beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_baselayout_space_before_valid[] = { UC_SENTINEL, CODE_IFSYSTEMSTORE, TSS_BASELAYOUT+1, 2, 1, 0 };
EXPECT_EQ(0, u16cmp(tstr_baselayout_space_before_valid, tstr));
@ -934,7 +910,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_b_test) {
fileKeyboard.cxStoreArray = 0;
fileKeyboard.dpStoreArray = nullptr;
u16cpy(str, u"baselayout(beep )");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_baselayout_space_after_valid[] = { UC_SENTINEL, CODE_IFSYSTEMSTORE, TSS_BASELAYOUT+1, 2, 1, 0 };
EXPECT_EQ(0, u16cmp(tstr_baselayout_space_after_valid, tstr));
}
@ -953,56 +929,56 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
u16cpy(option[1].szName, u"b");
u16cpy(option[2].szName, u"c");
// CERR_InvalidToken
// KmnCompilerMessages::ERROR_InvalidToken
u16cpy(str, u"ijk");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, CERR_80FeatureOnly
// if, KmnCompilerMessages::ERROR_80FeatureOnly
fileKeyboard.version = VERSION_70;
fileKeyboard.dwFlags = 0u;
u16cpy(str, u"if");
EXPECT_EQ(CERR_80FeatureOnly, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_80FeatureOnly, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
fileKeyboard.version = VERSION_80;
// if, CERR_InvalidInVirtualKeySection *** TODO ***
// if, KmnCompilerMessages::ERROR_InvalidInVirtualKeySection *** TODO ***
// if, no close delimiter => NULL
fileKeyboard.version = VERSION_80;
u16cpy(str, u"if(");
EXPECT_EQ(CERR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, empty delimiters => empty string
fileKeyboard.version = VERSION_80;
fileKeyboard.dwFlags = 0u;
u16cpy(str, u"if()");
EXPECT_EQ(CERR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
fileKeyboard.version = VERSION_80;
fileKeyboard.dwFlags = 0u;
u16cpy(str, u"if( )");
EXPECT_EQ(CERR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, invalid
fileKeyboard.version = VERSION_80;
u16cpy(str, u"if(abc)");
EXPECT_EQ(CERR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIf, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, CERR_90FeatureOnly_IfSystemStores
// if, KmnCompilerMessages::ERROR_90FeatureOnly_IfSystemStores
fileKeyboard.version = VERSION_80;
u16cpy(str, u"if(&BITMAP=)");
EXPECT_EQ(CERR_90FeatureOnly_IfSystemStores, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_90FeatureOnly_IfSystemStores, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, CERR_IfSystemStore_NotFound
// if, KmnCompilerMessages::ERROR_IfSystemStore_NotFound
fileKeyboard.version = VERSION_90;
u16cpy(str, u"if(&abc=)");
EXPECT_EQ(CERR_IfSystemStore_NotFound, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_IfSystemStore_NotFound, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, system store, equal, valid
fileKeyboard.version = VERSION_90;
fileKeyboard.cxStoreArray = 3u;
u16cpy(str, u"if(&BITMAP=beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_equal_system_store_valid[] = { UC_SENTINEL, CODE_IFSYSTEMSTORE, 2, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_equal_system_store_valid, tstr));
@ -1010,14 +986,14 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.version = VERSION_90;
fileKeyboard.cxStoreArray = 3u;
u16cpy(str, u"if(&BITMAP!=beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_not_equal_system_store_valid[] = { UC_SENTINEL, CODE_IFSYSTEMSTORE, 2, 1, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_not_equal_system_store_valid, tstr));
// if, option, CERR_StoreDoesNotExist
// if, option, KmnCompilerMessages::ERROR_StoreDoesNotExist
fileKeyboard.version = VERSION_80;
u16cpy(str, u"if(d=beep)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// if, option, equal, valid
fileKeyboard.version = VERSION_80;
@ -1025,7 +1001,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.dpStoreArray = option;
option[1].fIsOption = TRUE;
u16cpy(str, u"if(b=beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_option_valid[] = { UC_SENTINEL, CODE_IFOPT, 2, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_option_valid, tstr));
@ -1035,7 +1011,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.dpStoreArray = option;
option[1].fIsOption = TRUE;
u16cpy(str, u"if(b =beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_option_space_before_assign_valid[] = { UC_SENTINEL, CODE_IFOPT, 2, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_option_space_before_assign_valid, tstr));
@ -1045,7 +1021,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.dpStoreArray = option;
option[1].fIsOption = TRUE;
u16cpy(str, u"if(b= beep)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_option_space_before_rhs_valid[] = { UC_SENTINEL, CODE_IFOPT, 2, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_option_space_before_rhs_valid, tstr));
@ -1055,7 +1031,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.dpStoreArray = option;
option[1].fIsOption = TRUE;
u16cpy(str, u"if(b=beep )");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_if_option_space_after_rhs_valid[] = { UC_SENTINEL, CODE_IFOPT, 2, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_if_option_space_after_rhs_valid, tstr));
@ -1068,53 +1044,53 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
u16cpy(file_store[1].szName, u"b");
u16cpy(file_store[2].szName, u"c");
// index, CERR_InvalidInVirtualKeySection *** TODO ***
// index, KmnCompilerMessages::ERROR_InvalidInVirtualKeySection *** TODO ***
// index, no close delimiter => NULL
u16cpy(str, u"index(");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, empty delimiters => empty string
u16cpy(str, u"index()");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
u16cpy(str, u"index( )");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, no comma or space
u16cpy(str, u"index(b)");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, no comma, space before store
u16cpy(str, u"index( b)");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, no comma, space after store
u16cpy(str, u"index(b )");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, CERR_StoreDoesNotExist
// index, KmnCompilerMessages::ERROR_StoreDoesNotExist
u16cpy(str, u"index(d,4)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, comma, offset=0
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
u16cpy(str, u"index(b,0)");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, comma, negative offset
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
u16cpy(str, u"index(b,-1)");
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, comma, valid
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
u16cpy(str, u"index(b,4)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_index_comma_valid[] = { UC_SENTINEL, CODE_INDEX, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_index_comma_valid, tstr));
@ -1122,7 +1098,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
u16cpy(str, u"index( b,4)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_index_initial_space_and_comma_valid[] = { UC_SENTINEL, CODE_INDEX, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_index_initial_space_and_comma_valid, tstr));
@ -1130,7 +1106,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
u16cpy(str, u"index(b, 4)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_index_comma_and_space_valid[] = { UC_SENTINEL, CODE_INDEX, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_index_comma_and_space_valid, tstr));
@ -1138,7 +1114,7 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
u16cpy(str, u"index(b 4)");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_index_space_valid[] = { UC_SENTINEL, CODE_INDEX, 2, 4, 0 };
EXPECT_EQ(0, u16cmp(tstr_index_space_valid, tstr));
@ -1146,33 +1122,33 @@ TEST_F(CompilerTest, GetXStringImpl_type_i_test) {
u16cpy(str, u"index(b,42)");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_index_two_digit_valid[] = { UC_SENTINEL, CODE_INDEX, 2, 42, 0 };
EXPECT_EQ(0, u16cmp(tstr_index_two_digit_valid, tstr));
// index, comma, non-digit parameter, CERR_InvalidIndex
// index, comma, non-digit parameter, KmnCompilerMessages::ERROR_InvalidIndex
u16cpy(str, u"index(b,g)");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, comma, no parameter, CERR_InvalidIndex
// index, comma, no parameter, KmnCompilerMessages::ERROR_InvalidIndex
u16cpy(str, u"index(b,)");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, space and comma, no parameter, CERR_InvalidIndex
// index, space and comma, no parameter, KmnCompilerMessages::ERROR_InvalidIndex
u16cpy(str, u"index(b ,)");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// index, comma, no parameter but space, CERR_InvalidIndex
// index, comma, no parameter but space, KmnCompilerMessages::ERROR_InvalidIndex
u16cpy(str, u"index(b, )");
fileKeyboard.cxStoreArray = 3u;
fileKeyboard.dpStoreArray = file_store;
EXPECT_EQ(CERR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidIndex, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
}
// tests strings starting with 'o'
@ -1190,61 +1166,61 @@ TEST_F(CompilerTest, GetXStringImpl_type_o_test) {
u16cpy(file_store[1].szName, u"b");
u16cpy(file_store[2].szName, u"c");
// CERR_InvalidToken
// KmnCompilerMessages::ERROR_InvalidToken
u16cpy(str, u"opq");
EXPECT_EQ(CERR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, CERR_OutsInVirtualKeySection *** TODO ***
// outs, KmnCompilerMessages::ERROR_OutsInVirtualKeySection *** TODO ***
// outs, no close delimiter => NULL
u16cpy(str, u"outs(");
EXPECT_EQ(CERR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, empty delimiters => empty string
u16cpy(str, u"outs()");
EXPECT_EQ(CERR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, space in delimiters (see I11814, I11937, #11910, #11894, #11938)
u16cpy(str, u"outs( )");
EXPECT_EQ(CERR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidOuts, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, CERR_StoreDoesNotExist
// outs, KmnCompilerMessages::ERROR_StoreDoesNotExist
u16cpy(str, u"outs(d)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, CERR_StoreDoesNotExist, space before store
// outs, KmnCompilerMessages::ERROR_StoreDoesNotExist, space before store
u16cpy(str, u"outs( d)");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, CERR_StoreDoesNotExist, space after store
// outs, KmnCompilerMessages::ERROR_StoreDoesNotExist, space after store
u16cpy(str, u"outs(d )");
EXPECT_EQ(CERR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_StoreDoesNotExist, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
// outs, CERR_OutsTooLong
// outs, KmnCompilerMessages::ERROR_OutsTooLong
PKMX_WCHAR dpString = (PKMX_WCHAR)u"abc";
file_store[1].dpString = dpString; // length 4 => max should be > 4, otherwise a CERR_OutsTooLong is emitted
file_store[1].dpString = dpString; // length 4 => max should be > 4, otherwise a ERROR_OutsTooLong is emitted
int max = u16len(dpString) + 1; // 4, including terminating '\0'
u16cpy(str, u"outs(b)");
EXPECT_EQ(CERR_OutsTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, max, 0, &newp, FALSE)); // max reduced to force error
EXPECT_EQ(KmnCompilerMessages::ERROR_OutsTooLong, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, max, 0, &newp, FALSE)); // max reduced to force error
// outs, valid
file_store[1].dpString = (PKMX_WCHAR)u"abc";
u16cpy(str, u"outs(b)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_outs_valid[] = { 'a', 'b', 'c', 0 };
EXPECT_EQ(0, u16cmp(tstr_outs_valid, tstr));
// outs, space before store, valid
file_store[1].dpString = (PKMX_WCHAR)u"abc";
u16cpy(str, u"outs( b)");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_outs_space_before_valid[] = { 'a', 'b', 'c', 0 };
EXPECT_EQ(0, u16cmp(tstr_outs_space_before_valid, tstr));
// outs, space after store, valid (see I11937, #11938)
file_store[1].dpString = (PKMX_WCHAR)u"abc";
u16cpy(str, u"outs(b )");
EXPECT_EQ(CERR_None, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(STATUS_Success, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
const KMX_WCHAR tstr_outs_space_after_valid[] = { 'a', 'b', 'c', 0 };
EXPECT_EQ(0, u16cmp(tstr_outs_space_after_valid, tstr));
}
@ -1262,25 +1238,25 @@ TEST_F(CompilerTest, GetXStringImpl_type_o_test) {
// int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p)
TEST_F(CompilerTest, ProcessEthnologueStore_test) {
EXPECT_EQ(CERR_None, ProcessEthnologueStore((PKMX_WCHAR)u"abc"));
EXPECT_EQ(CWARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u";abc"));
EXPECT_EQ(CWARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u",abc"));
EXPECT_EQ(CERR_None, ProcessEthnologueStore((PKMX_WCHAR)u" abc"));
EXPECT_EQ(CERR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc "));
EXPECT_EQ(CERR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abcd"));
EXPECT_EQ(CERR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"ab"));
EXPECT_EQ(CERR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"a"));
EXPECT_EQ(CERR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"a2b"));
EXPECT_EQ(CERR_None, ProcessEthnologueStore((PKMX_WCHAR)u"")); // needs correcting ... see #11955
EXPECT_EQ(CERR_None, ProcessEthnologueStore((PKMX_WCHAR)u"abc def"));
EXPECT_EQ(CERR_None, ProcessEthnologueStore((PKMX_WCHAR)u"abc def"));
EXPECT_EQ(CWARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc,def"));
EXPECT_EQ(CWARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc;def"));
EXPECT_EQ(STATUS_Success, ProcessEthnologueStore((PKMX_WCHAR)u"abc"));
EXPECT_EQ(KmnCompilerMessages::WARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u";abc"));
EXPECT_EQ(KmnCompilerMessages::WARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u",abc"));
EXPECT_EQ(STATUS_Success, ProcessEthnologueStore((PKMX_WCHAR)u" abc"));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc "));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abcd"));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"ab"));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"a"));
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"a2b"));
EXPECT_EQ(STATUS_Success, ProcessEthnologueStore((PKMX_WCHAR)u"")); // needs correcting ... see #11955
EXPECT_EQ(STATUS_Success, ProcessEthnologueStore((PKMX_WCHAR)u"abc def"));
EXPECT_EQ(STATUS_Success, ProcessEthnologueStore((PKMX_WCHAR)u"abc def"));
EXPECT_EQ(KmnCompilerMessages::WARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc,def"));
EXPECT_EQ(KmnCompilerMessages::WARN_PunctuationInEthnologueCode, ProcessEthnologueStore((PKMX_WCHAR)u"abc;def"));
}
// KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk)
// void SetChecksum(PKMX_BYTE buf, PKMX_DWORD CheckSum, KMX_DWORD sz)
// KMX_BOOL kmcmp::CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall)
// void kmcmp::CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall)
// KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, KMX_BYTE**data, size_t& dataSize)
// KMX_DWORD ReadLine(KMX_BYTE* infile, int sz, int& offset, PKMX_WCHAR wstr, KMX_BOOL PreProcess)
@ -1288,17 +1264,17 @@ TEST_F(CompilerTest, GetRHS_test) {
KMX_WCHAR str[LINESIZE];
KMX_WCHAR tstr[128];
// CERR_NoTokensFound, empty string
// KmnCompilerMessages::ERROR_NoTokensFound, empty string
u16cpy(str, u"");
EXPECT_EQ(CERR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
// CERR_NoTokensFound, no '>'
// KmnCompilerMessages::ERROR_NoTokensFound, no '>'
u16cpy(str, u"abc");
EXPECT_EQ(CERR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_NoTokensFound, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
// CERR_None
// STATUS_Success
u16cpy(str, u"> nul c\n");
EXPECT_EQ(CERR_None, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
EXPECT_EQ(STATUS_Success, GetRHS(&fileKeyboard, str, tstr, 80, 0, FALSE));
}
// void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax)
@ -1326,7 +1302,7 @@ TEST_F(CompilerTest, isIntegerWstring_test) {
}
// KMX_DWORD kmcmp::UTF32ToUTF16(int n, int *n1, int *n2)
// KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk)
// KMX_BOOL BuildVKDictionary(PFILE_KEYBOARD fk)
// int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p)
// void kmcmp::RecordDeadkeyNames(PFILE_KEYBOARD fk)

View file

@ -1,19 +0,0 @@
#include <gtest/gtest.h>
#include "../../common/include/kmn_compiler_errors.h"
#include "../../../../common/include/km_types.h"
const KMX_CHAR *GetCompilerErrorString(KMX_DWORD code);
class CompMsgTest : public testing::Test {
protected:
CompMsgTest() {}
~CompMsgTest() override {}
void SetUp() override {}
void TearDown() override {}
};
TEST_F(CompMsgTest, GetCompilerErrorString) {
EXPECT_EQ(nullptr, GetCompilerErrorString(CERR_None));
EXPECT_EQ(nullptr, GetCompilerErrorString(0x00004FFF)); // top of range ERROR
EXPECT_EQ("Invalid 'layout' command", GetCompilerErrorString(CERR_InvalidLayoutLine));
};

View file

@ -24,11 +24,6 @@
using namespace std;
#define CERR_FATAL 0x00008000
#define CERR_ERROR 0x00004000
#define CERR_WARNING 0x00002000
#define CERR_HINT 0x00001000
int main(int argc, char *argv[])
{
if(argc < 4) {
@ -53,8 +48,7 @@ int main(int argc, char *argv[])
if(*p == '\\') *p = '/';
}
char first5[6] = "CERR_";
char* pfirst5 = first5;
const char* first5 = "CERR_";
KMCMP_COMPILER_RESULT result;
KMCMP_COMPILER_OPTIONS options;
@ -66,7 +60,7 @@ int main(int argc, char *argv[])
if(kmcmp_CompileKeyboard(kmn_file, options, msgproc, loadfileProc, nullptr, result)) {
char* testname = strrchr( (char*) kmn_file, '/') + 1;
if(strncmp(testname, pfirst5, 5) == 0){
if(strncmp(testname, first5, strlen(first5)) == 0){
return __LINE__; // exit code: CERR_ in Name + no Error found
}
@ -99,7 +93,7 @@ int main(int argc, char *argv[])
char* testname = strrchr( (char*) kmn_file, '/') + 1;
// Does testname contain CERR_ && contains '_' on pos 9 ? -> Get ErrorValue
if ((strncmp(testname, pfirst5, 5) == 0) && (testname[9] == '_')) {
if ((strncmp(testname, first5, strlen(first5)) == 0) && (testname[9] == '_')) {
char* ErrNr = strchr(testname, '_') +1 ;
std::istringstream(ErrNr) >> std::hex >> error_val;

View file

@ -168,17 +168,6 @@ gtestcompilertest = executable('gtest-compiler-test', 'gtest-compiler-test.cpp',
test('gtest-compiler-test', gtestcompilertest)
gtestcompmsgtest = executable('gtest-compmsg-test', 'gtest-compmsg-test.cpp',
cpp_args: defns + flags,
include_directories: inc,
name_suffix: name_suffix,
link_args: links + tests_links,
objects: lib.extract_all_objects(),
dependencies: [ icuuc_dep, gtest_dep, gmock_dep ],
)
test('gtest-compmsg-test', gtestcompmsgtest)
gtestkmx_u16test = executable('gtest-kmx_u16-test', 'gtest-kmx_u16-test.cpp',
cpp_args: defns + flags,
include_directories: inc,

View file

@ -7,53 +7,57 @@
std::vector<int> error_vec;
int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context) {
error_vec.push_back(dwMsgCode);
void msgproc(const KMCMP_COMPILER_RESULT_MESSAGE &message, void* context) {
error_vec.push_back(message.errorCode);
const char*t = "unknown";
switch(dwMsgCode & 0xF000) {
case CERR_HINT: t=" hint"; break;
case CERR_WARNING: t="warning"; break;
case CERR_ERROR: t=" error"; break;
case CERR_FATAL: t=" fatal"; break;
switch(message.errorCode & MESSAGE_SEVERITY_MASK) {
case CompilerErrorSeverity::Hint: t=" SevHint"; break;
case CompilerErrorSeverity::Warn: t=" SevWarn"; break;
case CompilerErrorSeverity::Error: t="SevError"; break;
case CompilerErrorSeverity::Fatal: t="SevFatal"; break;
case CompilerErrorSeverity::Info: t=" SevInfo"; break;
}
printf("line %d %s %4.4x: %s\n", line, t, (unsigned int)dwMsgCode, szText);
return 1;
printf("[CompilerMessage] %s(%d): %s | 0x%3.3x (",
message.filename.c_str(),
message.lineNumber,
t,
(unsigned int)message.errorCode & MESSAGE_BASEERROR_MASK
);
for(auto it = message.parameters.begin(); it != message.parameters.end(); it++) {
printf("%s'%s'", it == message.parameters.begin() ? "" : ", ", it->c_str());
}
printf(")\n");
}
bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context) {
const std::vector<uint8_t> loadfileProc(const std::string& filename, const std::string& baseFilename) {
std::string resolvedFilename = filename;
if(baseFilename && *baseFilename && IsRelativePath(filename)) {
if(baseFilename.length() && IsRelativePath(filename.c_str())) {
char* p;
if ((p = strrchr_slash((char*)baseFilename)) != nullptr) {
std::string basePath = std::string(baseFilename, (int)(p - baseFilename + 1));
resolvedFilename = basePath;
const char *base = baseFilename.c_str();
if ((p = strrchr_slash((char*)base)) != nullptr) {
resolvedFilename = std::string(baseFilename, 0, (int)(p - base + 1));
resolvedFilename.append(filename);
}
}
std::vector<uint8_t> buf;
FILE* fp = Open_File(resolvedFilename.c_str(), "rb");
if(!fp) {
return false;
return buf;
}
if(!data) {
// return size
if(fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
return false;
}
*size = ftell(fp);
if(*size == -1L) {
fclose(fp);
return false;
}
} else {
// return data
if(fread(data, 1, *size, fp) != (size_t)(*size)) {
fclose(fp);
return false;
}
if(fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
return buf;
}
size_t size = ftell(fp);
buf.resize(size);
fseek(fp, 0, SEEK_SET);
if(fread(buf.data(), 1, size, fp) != size) {
buf.resize(0);
}
fclose(fp);
return true;
return buf;
}

View file

@ -2,7 +2,7 @@
#include <vector>
int msgproc(int line, uint32_t dwMsgCode, const char* szText, void* context);
bool loadfileProc(const char* filename, const char* baseFilename, void* data, int* size, void* context);
void msgproc(const KMCMP_COMPILER_RESULT_MESSAGE &message, void* context);
const std::vector<uint8_t> loadfileProc(const std::string& filename, const std::string& baseFilename);
extern std::vector<int> error_vec;

View file

@ -22,4 +22,8 @@
"include": [
"src/**/*.ts"
],
"references": [
{ "path": "../../../common/web/keyman-version" },
{ "path": "../common/web/utils" },
]
}

View file

@ -53,9 +53,13 @@ sudo mk-build-deps --install linux/debian/control
#### Node.js
Node.js v18 is required for Core builds, Web builds, and Developer command line tool builds and usage.
Node.js is required for Core builds, Web builds, and Developer command line tool builds and usage.
Follow the instructions on the [NodeSource Distributions](https://github.com/nodesource/distributions#table-of-contents) page.
Our recommended way to install node.js is with
[nvm](https://github.com/nvm-sh/nvm). This makes it easy to switch between
versions of node.js.
See [node.md](node.md) for more information.
#### Emscripten

18
docs/build/macos.md vendored
View file

@ -77,10 +77,24 @@ PATH="$HOMEBREW_PREFIX/opt/coreutils/libexec/gnubin:$PATH"
## KeymanWeb Dependencies
* node.js 22+, emscripten 3.1.46 or later
* node.js, emscripten 3.1.46 or later
### node.js
Our recommended way to install node.js is with
[nvm](https://github.com/nvm-sh/nvm). This makes it easy to switch between
versions of node.js.
Alternatively, you can install node.js with `brew`.
See [node.md](node.md) for more information.
### emscripten
To install emscripten:
```shell
brew install node emscripten
brew install emscripten
```
Note: if you install emscripten with brew on macOS, only emscripten binaries are

71
docs/build/node.md vendored Normal file
View file

@ -0,0 +1,71 @@
# Node use in the Keyman project
For a given release cycle, we select and pin to a specific version of node.js,
and try to use that throughout. The version in use can be found in
`package.json/engines.node`.
If we encounter a blocking issue, we will upgrade to a known-good release, but
this is exceptional.
Generally, as a developer, if you use the same major version as is found in
package.json, you probably won't have significant issues. However, if you are
switching between stable branches and alpha branches, you may find the required
node version changes.
## Automatic node version selection
The Keyman build system can be configured to manage the node.js version for you.
This does have several caveats, so it is not enabled by default.
When this is enabled, the build system will download the required node version
and make it available on the PATH, and it will also stop the build if the node
version mismatches. This happens in `build.sh configure` steps for any
Typescript/Javascript project.
To enable automatic node version selection, add the variable `KEYMAN_USE_NVM=1`
to your environment.
### Caveats
nvm (for macOS/Linux) and nvm-windows use somewhat different paradigms, so the
caveats vary per platform.
### Caveats on macOS/Linux
On macOS/Linux, nvm is provided as a shell function that modifies the current
environment PATH to make a specific node version available -- and so this does
not affect unrelated processes.
1. You must use [nvm](https://github.com/nvm-sh/nvm) (macOS/Linux) to install
and manage Node versions.
2. The Keyman build environment will create a symlink at `~/.keyman/node`
pointing to the version of node selected by nvm.
3. You should add `$HOME/.keyman/node` to the front of your `PATH` variable,
e.g. in `~/.bash_profile`. This means that the node version _will_ be set for
the entire system, unlike with standard nvm usage. This allows build scripts
to run without calling `nvm` for each invocation.
### Caveats on Windows
On Windows, nvm-windows creates a symlink to the current node version, and
this symlink is what is on the system PATH, so it affects all processes on the
system.
1. You must use [nvm-windows](https://github.com/coreybutler/nvm-windows) to
install and manage Node versions.
2. By default on Windows, creating symlinks requires elevation. While
nvm-windows does this for you, it can be irritating to have a build script
pause for elevation. In Local Group Policy Editor, `Computer Configuration`,
`Windows Settings`, `Security Settings`, `Local Policies`,
`User Rights Assignment`, you can add your username to the
`Create symbolic links` policy (and reboot), to avoid elevation.
## Build Agents
The Keyman build agents use nvm as described above, including the caveats.
## Implementation
See `_select_node_version_with_nvm()` in
`/resources/build/shellHelperFunctions.sh`, and
`/resources/build/_builder_nvm.sh`.

24
docs/build/windows.md vendored
View file

@ -206,7 +206,15 @@ installed emsdk (most likely %LocalAppData%\emsdk\upstream\emscripten)
**Environment variables**:
* `EMSCRIPTEN_BASE`: `<your-emsdk-path>\upstream\emscripten`
After installing emscripten, you'll need to install node.js and openjdk:
After installing emscripten, you'll need to install node.js and openjdk.
#### node.js
Our recommended way to install node.js is to use
[nvm-windows](https://github.com/coreybutler/nvm-windows). This makes it
easy to switch between versions of node.js.
Alternatively, use Powershell + Chocolatey to install node.js:
```ps1
# Elevated PowerShell
@ -214,9 +222,23 @@ After installing emscripten, you'll need to install node.js and openjdk:
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install nodejs
```
See [node.md](node.md) for more information.
#### openjdk
Use Powershell + Chocolatey to install OpenJDK:
```ps1
# Elevated PowerShell
# for *much* faster download, hide progress bar (PowerShell/PowerShell#2138)
$ProgressPreference = 'SilentlyContinue'
choco install openjdk
```
### Windows Platform Dependencies
**Projects**:

View file

@ -60,7 +60,7 @@ https://help.keyman.com/developer/engine/android/latest-version/
| KEYMAN_MIN_VERSION_CPP | 17 |
| KEYMAN_MIN_VERSION_EMSCRIPTEN | 3.1.44 |
| KEYMAN_MIN_VERSION_MESON | 1.0.0 |
| KEYMAN_MIN_VERSION_NODE_MAJOR | 18 |
| KEYMAN_MIN_VERSION_NODE_MAJOR | 20 |
| KEYMAN_MIN_VERSION_NPM | 10.5.1 |
| KEYMAN_MIN_VERSION_VISUAL_STUDIO | 2019 |
| KEYMAN_VERSION_JAVA | 11 |

2
package-lock.json generated
View file

@ -66,7 +66,7 @@
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.x"
"node": "20.16.0"
}
},
"common/models/templates": {

View file

@ -63,6 +63,6 @@
"@keymanapp/ldml-keyboard-constants": "file:core/include/ldml"
},
"engines": {
"node": "^18.x"
"node": "20.16.0"
}
}

51
resources/build/_builder_nvm.sh Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env bash
# See also /docs/build/node.md
set -e
set -u
if [[ $# -lt 1 ]]; then
echo "This script is called by shellHelperFunctions.sh, _select_node_version_with_nvm()"
echo "during build.sh configure steps. It is not intended to be called directly, as it"
echo "is a wrapper for nvm."
exit 99
fi
REQUIRED_NODE_VERSION="$1"
if [[ -z "${NVM_DIR+x}" ]]; then
export NVM_DIR="$HOME/.nvm"
fi
#
# nvm on macos and linux is a shell function. `source`ing it into our scripts is
# fragile because it uses some of the same variable names that we do. We have
# marked our most precious variables as `readonly`, which breaks nvm re-use of
# them also! Safest way to work around this is to load nvm in a child process
# and run it there. Downside is then of course we don't get the `PATH` changes
# that nvm does for us, so we have to do it ourselves back in the caller,
# `_select_node_version_with_nvm()`.
#
# Note: the readonly attribute for variables is not inherited by child
# processes.
#
# See also https://github.com/nvm-sh/nvm/issues/3257 (among others)
#
type -t nvm >/dev/null || {
source "$NVM_DIR/nvm.sh"
type -t nvm >/dev/null || {
echo "Failed to find nvm"
exit 1
}
}
nvm install "$REQUIRED_NODE_VERSION"
nvm use "$REQUIRED_NODE_VERSION"
# Beware the hardcoded path below -- it should already be in the system PATH
mkdir -p "$HOME/.keyman"
rm -f "$HOME/.keyman/node"
ln -s "$NVM_BIN" "$HOME/.keyman/node"

View file

@ -6,27 +6,29 @@
# Linux/macOS: jq
#
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
JQ_THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
# . "${THIS_SCRIPT%/*}/build-utils.sh"
## END STANDARD BUILD SCRIPT INCLUDE
if [[ -z "${JQ+x}" ]]; then
## START STANDARD BUILD SCRIPT INCLUDE
# adjust relative paths as necessary
JQ_THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
# . "${THIS_SCRIPT%/*}/build-utils.sh"
## END STANDARD BUILD SCRIPT INCLUDE
case "${OSTYPE}" in
"cygwin")
JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe
;;
"msys")
JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe
;;
*)
JQ=jq
;;
esac
case "${OSTYPE}" in
"cygwin")
JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe
;;
"msys")
JQ=$(dirname "$JQ_THIS_SCRIPT")/jq-win64.exe
;;
*)
JQ=jq
;;
esac
readonly JQ
readonly JQ
# JQ with inplace file replacement
function jqi() {
cat <<< "$($JQ -c "$1" < "$2")" > "$2"
}
# JQ with inplace file replacement
function jqi() {
cat <<< "$($JQ -c "$1" < "$2")" > "$2"
}
fi

View file

@ -13,7 +13,7 @@ KEYMAN_MIN_TARGET_VERSION_UBUNTU=20.04 # Ubuntu 20.04 Focal
KEYMAN_MIN_TARGET_VERSION_CHROME=95.0 # Final version that runs on Android 5.0
# Dependency versions
KEYMAN_MIN_VERSION_NODE_MAJOR=18
KEYMAN_MIN_VERSION_NODE_MAJOR=20 # node version source of truth is /package.json:/engines/node
KEYMAN_MIN_VERSION_NPM=10.5.1 # 10.5.0 has bug, discussed in #10350
KEYMAN_MIN_VERSION_EMSCRIPTEN=3.1.44 # Warning: 3.1.45 is bad (#9529); newer versions work
KEYMAN_MAX_VERSION_EMSCRIPTEN=3.1.58 # See #9529

View file

@ -1,5 +1,16 @@
#!/usr/bin/env bash
# We only import JQ if not already defined; jq is used by
# _select_node_version_with_nvm()
if [[ -z "${JQ+x}" ]]; then
if [[ "$BUILDER_OS" == win ]]; then
. "$KEYMAN_ROOT/resources/build/jq.inc.sh"
else
JQ=jq
fi
fi
# Allows for a quick macOS check for those scripts requiring a macOS environment.
verify_on_mac() {
if [[ "${OSTYPE}" != "darwin"* ]]; then
@ -243,6 +254,13 @@ verify_npm_setup() {
fi
builder_set_module_has_been_built /external/npm-ci
# If we are on CI environment, automatically select a node version with nvm
# Also, a developer can set KEYMAN_USE_NVM variable to get this behaviour
# automatically too (see /docs/build/node.md)
if [[ "$VERSION_ENVIRONMENT" != local || ! -z "${KEYMAN_USE_NVM+x}" ]]; then
_select_node_version_with_nvm
fi
# Check if Node.JS/npm is installed.
type npm >/dev/null ||\
builder_die "Build environment setup error detected! Please ensure Node.js is installed!"
@ -252,4 +270,29 @@ verify_npm_setup() {
try_multiple_times npm ci
popd > /dev/null
}
}
# Use nvm to select a node version according to package.json
# see /docs/build/node.md
_select_node_version_with_nvm() {
local REQUIRED_NODE_VERSION="$("$JQ" -r '.engines.node' "$KEYMAN_ROOT/package.json")"
if [[ $BUILDER_OS != win ]]; then
# launch nvm in a sub process, see _builder_nvm.sh for details
"$KEYMAN_ROOT/resources/build/_builder_nvm.sh" "$REQUIRED_NODE_VERSION"
else
nvm install "$REQUIRED_NODE_VERSION"
nvm use "$REQUIRED_NODE_VERSION"
fi
# Now, check that the node version is correct, on all systems
# Note: On windows, `nvm use` and `nvm install` always return success.
# https://github.com/coreybutler/nvm-windows/issues/738
# note the 'v' prefix that node emits (and npm doesn't!)
local CURRENT_NODE_VERSION="$(node --version)"
if [[ "$CURRENT_NODE_VERSION" != "v$REQUIRED_NODE_VERSION" ]]; then
builder_die "Attempted to select node.js version $REQUIRED_NODE_VERSION but found $CURRENT_NODE_VERSION instead"
fi
}