import { KeyAddress } from "../kmw-compiler/validate-layout-file.js";
import { kmnfile } from "../kmw-compiler/compiler-globals.js";
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerEvent, CompilerMessageSpec as m, CompilerMessageObjectSpec as mx, CompilerMessageDef as def, CompilerMessageSpecWithException, KeymanUrls } from "@keymanapp/developer-utils";
import { ObjectWithCompileContext } from "@keymanapp/common-types";
const Namespace = CompilerErrorNamespace.KmnCompiler;
const SevInfo = CompilerErrorSeverity.Info | Namespace;
const SevHint = CompilerErrorSeverity.Hint | Namespace;
const SevWarn = CompilerErrorSeverity.Warn | Namespace;
const SevError = CompilerErrorSeverity.Error | Namespace;
const SevFatal = CompilerErrorSeverity.Fatal | Namespace;

// For messages from the KeymanWeb compiler, we need to construct our messages
// slightly differently. This could be refactored in the future, as it is not
// obvious which messages should use which function.
// Specifically, this could perhaps use CompilerFileCallbacks which provides a
// default filename.
// Then, there could be a CompilerMessageSpecWithLine
const mw = (code: number, message: string, o?: {filename?: string, line?: number}) : CompilerEvent => ({
  ...m(code, message),
  filename: o?.filename ?? kmnfile,
  line: o?.line,
});

export const enum KmnCompilerMessageRanges {
  RANGE_KMN_COMPILER_MIN    = 0x001, // from kmn_compiler_errors.h
  RANGE_KMN_COMPILER_MAX    = 0x7FF, // from kmn_compiler_errors.h
  RANGE_LEXICAL_MODEL_MIN   = 0x800, // from kmn_compiler_errors.h, deprecated -- this range will not be used in future versions
  RANGE_LEXICAL_MODEL_MAX   = 0x8FF, // from kmn_compiler_errors.h, deprecated -- this range will not be used in future versions
  RANGE_CompilerMessage_Min = 0x900, // All compiler messages listed here must be >= this value
  RANGE_CompilerMessage_Max = 0xFFF, // Highest available error code for kmc-kmn
}

export function keyAddress(address: KeyAddress): string {
  if(!address) {
    return '<param>';
  }
  return `row:${address.rowIndex} col:${address.keyIndex}` +
    (address.direction ? ` flick:${address.direction}` :
    (address.subKeyIndex ? ` longPress:${address.subKeyIndex}` :
    (address.multitapIndex ? ` multitap:${address.multitapIndex}` :
    '')));
}

type KmcmpLibMessageParameters = {p:string[]};

/**
 * @internal
 *
 * Error messages reported by the .kmn compiler. The error codes in this class
 * share an address space with messages from kmn_compiler_errors.h, which are
 * generated by kmcmplib, so codes below 0x900 are reserved for kmcmplib
 * messages.
 *
 * The second half of this class defines the messages from kmcmplib. These
 * messages should correspond to codes in kmn_compiler_errors.h, exclusive
 * severity bits. Message text is defined by kmcmplib and passed through a
 * callback at this time. The message text presented here is currently unused
 * during the compilation process, but is used in the `kmc messages` tool and
 * for documentation.
 *
 * `CERR_` messages that are actually fatals have been renamed to `FATAL_`.
 *
 * @example
 *
 * ```
 *   kmcmplib.CERR_BadCallParams = CERR_FATAL | 0x002 = 0x8002
 *   ERROR_BadCallParams = SevError | 0x0002 = 0x300000 | 0x2000 | 0x002 = 0x302002
 * ```
 */
export class KmnCompilerMessages {
  // TODO: v18.0 we should consider moving error message generation in kmcmplib to
  // kmc-kmn, which would avoid a number of legacy issues. Questions about
  // parameterisation.

  static FATAL_UnexpectedException = SevFatal | 0x900;
  static Fatal_UnexpectedException = (o:{e: any}) => CompilerMessageSpecWithException(
    this.FATAL_UnexpectedException,
    null,
    o.e ?? 'unknown error',
    `Raised when KmnCompiler or one of its components has an internal
    error. If you experience this error, it should be reported to the Keyman
    team for resolution via ${KeymanUrls.NEW_KEYMAN_ISSUE()}.`
  );

  static FATAL_MissingWasmModule = SevFatal | 0x901;
  static Fatal_MissingWasmModule = (o:{e?: any}) => CompilerMessageSpecWithException(
    this.FATAL_MissingWasmModule,
    `Could not instantiate WASM compiler module or initialization failed`,
    o.e ?? 'unknown error',
    `Raised when the kmcmplib component could not be instantiated. This may indicate
    a configuration or dependency issue. Make sure you are running a Javascript
    engine that supports WASM, and that use of WASM is enabled.`
  );

  // Note: this is now unused
  // static FATAL_UnableToSetCompilerOptions = SevFatal | 0x902;
  // static Fatal_UnableToSetCompilerOptions = () => m(this.FATAL_UnableToSetCompilerOptions, null, `Unable to set compiler options`);

  static FATAL_CallbacksNotSet = SevFatal | 0x903;
  static Fatal_CallbacksNotSet = () => CompilerMessageSpecWithException(
    this.FATAL_CallbacksNotSet,
    null,
    `Callbacks were not set with init`,
    `Raised when KmnCompiler or one of its components experiences an internal
    error. If you experience this error, it should be reported to the Keyman
    team for resolution via ${KeymanUrls.NEW_KEYMAN_ISSUE()}.`
  );

  static FATAL_UnicodeSetOutOfRange = SevFatal | 0x904;
  static Fatal_UnicodeSetOutOfRange = (compileContext?: ObjectWithCompileContext) => mx(
    this.FATAL_UnicodeSetOutOfRange, compileContext,
    `UnicodeSet buffer was too small`,
    `Raised when caller to UnicodeSet functions provides an invalid buffer. If
    you experience this error, it should be reported to the Keyman team for
    resolution via ${KeymanUrls.NEW_KEYMAN_ISSUE()}.`
  );

  // TODO: rename the following functions to Error_UsetHasStrings etc

  static ERROR_UnicodeSetHasStrings = SevError | 0x905;
  static Error_UnicodeSetHasStrings = (compileContext?: ObjectWithCompileContext) => mx(
    this.ERROR_UnicodeSetHasStrings, compileContext,
    `uset contains strings, not allowed`,
    `The provided uset uses multi-character strings, (\`{}\` notation, e.g.
    \`[żġħ{ie}{għ}]\`. ). Although full UnicodeSets support strings, LDML
    keyboards do not support multi-character strings in usets. To resolve this,
    reformat the uset to avoid the use of multi-character strings.

    More on uset: ${KeymanUrls.LDML_SPEC('element-uset')}`
  );

  static ERROR_UnicodeSetHasProperties = SevError | 0x906;
  static Error_UnicodeSetHasProperties = (compileContext?: ObjectWithCompileContext) => mx(
    this.ERROR_UnicodeSetHasProperties, compileContext,
    `uset contains properties, not allowed`,
    `The provided uset uses property notation (\`\\p{…}\` or \`[:…:]\`). LDML
    keyboards do not support Unicode properties in usets, because that would
    make implementations dependent on a particular version of Unicode. To
    resolve this, reformat the uset to avoid the use of properties.

    More on uset: ${KeymanUrls.LDML_SPEC('element-uset')}`
  );

  static ERROR_UnicodeSetSyntaxError = SevError | 0x907;
  static Error_UnicodeSetSyntaxError = (compileContext?: ObjectWithCompileContext) => mx(
    this.ERROR_UnicodeSetSyntaxError, compileContext,
    `uset had a Syntax Error while parsing`,
    `The provided uset has a syntax error and could not be parsed. Verify the
    format of the uset against the specification.

    More on uset: ${KeymanUrls.LDML_SPEC('element-uset')}`
  );

  static ERROR_InvalidKvksFile = SevError | 0x908;
  static Error_InvalidKvksFile = (o:{filename: string, e: any}) => m(
    this.ERROR_InvalidKvksFile,
    `Error encountered parsing ${def(o.filename)}: ${o.e ?? 'unknown error'}`, // Note, not fatal, not reporting to Sentry
    `The .kvks file could not be parsed because it was not a valid XML file.
    There may be additional information in the error message to help you
    resolve the error.

    More on .kvks file format: ${KeymanUrls.FILE_TYPE('kvks')}`
  );

  static WARN_InvalidVkeyInKvksFile = SevWarn | 0x909;
  static Warn_InvalidVkeyInKvksFile = (o:{filename: string, invalidVkey: string}) => m(
    this.WARN_InvalidVkeyInKvksFile,
    `Invalid virtual key ${def(o.invalidVkey)} found in ${def(o.filename)}`,
    `The .kvks file contained a virtual key that was not supported by
    Keyman. Remove this virtual key from the .kvks file.

    Supported virtual keys: ${KeymanUrls.VIRTUAL_KEYS()}`
  );

  static ERROR_InvalidDisplayMapFile = SevError | 0x90A;
  static Error_InvalidDisplayMapFile = (o:{filename: string, e: any}) => m(
    this.ERROR_InvalidDisplayMapFile,
    `Error encountered parsing display map ${def(o.filename)}: ${o.e ?? 'unknown error'}`, // Note, not fatal, not reporting to Sentry
    `The displayMap file could not be parsed because it was not a valid JSON
    file. There may be additional information in the error message to help you
    resolve the error.

    More on displayMap: ${KeymanUrls.KMN_REF('displaymap')}`
  );

  static ERROR_InvalidKvkFile = SevError | 0x90B;
  static Error_InvalidKvkFile = (o:{filename: string, e: any}) => m(
    this.ERROR_InvalidKvkFile,
    `Error encountered loading ${def(o.filename)}: ${o.e ?? 'unknown error'}`, // Note, not fatal, not reporting to Sentry
    `The .kvk file could not be loaded because it was not a valid format. There
    may be additional information in the error message to help you resolve the
    error.

    More on .kvk files: ${KeymanUrls.FILE_TYPE('kvk')}`
  );

  static ERROR_FileNotFound = SevError | 0x90C;
  static Error_FileNotFound = (o:{filename: string}) => m(
    this.ERROR_FileNotFound,
    `File ${def(o.filename)} was not found`,
    `The file was not found on the disk. Verify that you have the correct path
    to the file.`
  );

  // 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`);

  static FATAL_CannotAllocateMemory                           = SevFatal | 0x004;
  static Fatal_CannotAllocateMemory                           = () => m(this.FATAL_CannotAllocateMemory, `Out of memory`);

  static ERROR_InfileNotExist                                 = SevError | 0x005; // #10678: reduced from fatal to error in 17.0
  static Error_InfileNotExist                                 = () => m(this.ERROR_InfileNotExist, `Cannot find the input file`);

  // 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 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`);

  static FATAL_SomewhereIGotItWrong                           = SevFatal | 0x009;
  static Fatal_SomewhereIGotItWrong                           = () => m(this.FATAL_SomewhereIGotItWrong, `Internal error: contact Keyman`);

  static ERROR_InvalidToken                                   = SevError | 0x00A;
  static Error_InvalidToken                                   = () => m(this.ERROR_InvalidToken, `Invalid token found`);

  // kmcmplib: static Error_InvalidBegin                                  = () => m(this.ERROR_InvalidBegin, `Invalid 'begin' statement`);
  static ERROR_InvalidBegin                                   = SevError | 0x00B;
  static Error_InvalidBegin = () => mw(this.ERROR_InvalidBegin,
    `A "begin unicode" statement is required to compile a KeymanWeb keyboard`);

  static ERROR_InvalidName                                    = SevError | 0x00C;
  static Error_InvalidName                                    = () => m(this.ERROR_InvalidName, `Invalid 'name' statement`);

  static ERROR_InvalidVersion                                 = SevError | 0x00D;
  static Error_InvalidVersion                                 = () => m(this.ERROR_InvalidVersion, `Invalid 'version' statement`);

  static ERROR_InvalidLanguageLine                            = SevError | 0x00E;
  static Error_InvalidLanguageLine                            = () => m(this.ERROR_InvalidLanguageLine, `Invalid 'language' statement`);

  static ERROR_LayoutButNoLanguage                            = SevError | 0x00F;
  static Error_LayoutButNoLanguage                            = () => m(this.ERROR_LayoutButNoLanguage, `Layout statement found but no language statement`);

  static ERROR_InvalidLayoutLine                              = SevError | 0x010;
  static Error_InvalidLayoutLine                              = () => m(this.ERROR_InvalidLayoutLine, `Invalid 'layout' statement`);

  static ERROR_NoVersionLine                                  = SevError | 0x011;
  static Error_NoVersionLine                                  = () => m(this.ERROR_NoVersionLine, `No version line found for file`);

  static ERROR_InvalidGroupLine                               = SevError | 0x012;
  static Error_InvalidGroupLine                               = () => m(this.ERROR_InvalidGroupLine, `Invalid 'group' statement`);

  static ERROR_InvalidStoreLine                               = SevError | 0x013;
  static Error_InvalidStoreLine                               = () => m(this.ERROR_InvalidStoreLine, `Invalid 'store' statement`);

  static ERROR_InvalidCodeInKeyPartOfRule                     = SevError | 0x014;
  static Error_InvalidCodeInKeyPartOfRule                     = () => m(this.ERROR_InvalidCodeInKeyPartOfRule, `Invalid statement or code found in key part of rule`);

  static ERROR_InvalidDeadkey                                 = SevError | 0x015;
  static Error_InvalidDeadkey                                 = () => m(this.ERROR_InvalidDeadkey, `Invalid 'deadkey' or 'dk' statement`);

  static ERROR_InvalidValue                                   = SevError | 0x016;
  static Error_InvalidValue                                   = () => m(this.ERROR_InvalidValue, `Invalid value in extended string`);

  static ERROR_ZeroLengthString                               = SevError | 0x017;
  static Error_ZeroLengthString                               = () => m(this.ERROR_ZeroLengthString, `A string of zero characters was found`);

  static ERROR_TooManyIndexToKeyRefs                          = SevError | 0x018;
  static Error_TooManyIndexToKeyRefs                          = () => m(this.ERROR_TooManyIndexToKeyRefs, `Too many index statements refering to key string`);

  static ERROR_UnterminatedString                             = SevError | 0x019;
  static Error_UnterminatedString                             = () => m(this.ERROR_UnterminatedString, `Unterminated string in line`);

  // static ERROR_StringInVirtualKeySection                      = SevError | 0x01A; // no longer used, see #12612
  // static Error_StringInVirtualKeySection                      = () => m(this.ERROR_StringInVirtualKeySection, `extend string illegal in virtual key section`);

  // static ERROR_AnyInVirtualKeySection                         = SevError | 0x01B; // no longer used, see #12612
  // static Error_AnyInVirtualKeySection                         = () => m(this.ERROR_AnyInVirtualKeySection, `'any' statement is illegal in virtual key section`);

  static ERROR_InvalidAny                                     = SevError | 0x01C;
  static Error_InvalidAny                                     = () => m(this.ERROR_InvalidAny, `Invalid 'any' statement`);

  static ERROR_StoreDoesNotExist                              = SevError | 0x01D;
  static Error_StoreDoesNotExist                              = () => m(this.ERROR_StoreDoesNotExist, `Store referenced does not exist`);

  // static ERROR_BeepInVirtualKeySection                        = SevError | 0x01E; // no longer used, see #12612
  // static Error_BeepInVirtualKeySection                        = () => m(this.ERROR_BeepInVirtualKeySection, `'beep' statement is illegal in virtual key section`);

  // static ERROR_IndexInVirtualKeySection                       = SevError | 0x01F; // no longer used, see #12612
  // static Error_IndexInVirtualKeySection                       = () => m(this.ERROR_IndexInVirtualKeySection, `'index' statement is illegal in virtual key section`);

  static ERROR_InvalidIndex                                   = SevError | 0x020;
  static Error_InvalidIndex                                   = () => m(this.ERROR_InvalidIndex, `Invalid 'index' statement`);

  // static ERROR_OutsInVirtualKeySection                        = SevError | 0x021; // no longer used, see #12612
  // static Error_OutsInVirtualKeySection                        = () => m(this.ERROR_OutsInVirtualKeySection, `'outs' statement is illegal in virtual key section`);

  static ERROR_InvalidOuts                                    = SevError | 0x022;
  static Error_InvalidOuts                                    = () => m(this.ERROR_InvalidOuts, `Invalid 'outs' statement`);

  // static ERROR_ContextInVirtualKeySection                     = SevError | 0x024; // no longer used, see #12612
  // static Error_ContextInVirtualKeySection                     = () => m(this.ERROR_ContextInVirtualKeySection, `'context' statement is illegal in virtual key section`);

  static ERROR_InvalidUse                                     = SevError | 0x025;
  static Error_InvalidUse                                     = () => m(this.ERROR_InvalidUse, `Invalid 'use' statement`);

  static ERROR_GroupDoesNotExist                              = SevError | 0x026;
  static Error_GroupDoesNotExist                              = () => m(this.ERROR_GroupDoesNotExist, `Group does not exist`);

  static ERROR_VirtualKeyNotAllowedHere                       = SevError | 0x027;
  static Error_VirtualKeyNotAllowedHere                       = () => m(this.ERROR_VirtualKeyNotAllowedHere, `Virtual key is not allowed here`);

  static ERROR_InvalidSwitch                                  = SevError | 0x028;
  static Error_InvalidSwitch                                  = () => m(this.ERROR_InvalidSwitch, `Invalid 'switch' statement`);

  static ERROR_NoTokensFound                                  = SevError | 0x029;
  static Error_NoTokensFound                                  = () => m(this.ERROR_NoTokensFound, `No tokens found in line`);

  static ERROR_InvalidLineContinuation                        = SevError | 0x02A;
  static Error_InvalidLineContinuation                        = () => m(this.ERROR_InvalidLineContinuation, `Invalid line continuation`);

  static ERROR_LineTooLong                                    = SevError | 0x02B;
  static Error_LineTooLong                                    = () => m(this.ERROR_LineTooLong, `Line too long`);

  static ERROR_InvalidCopyright                               = SevError | 0x02C;
  static Error_InvalidCopyright                               = () => m(this.ERROR_InvalidCopyright, `Invalid 'copyright' statement`);

  static ERROR_CodeInvalidInThisSection                       = SevError | 0x02D;
  static Error_CodeInvalidInThisSection                       = () => m(this.ERROR_CodeInvalidInThisSection, `This line is invalid in this section of the file`);

  static ERROR_InvalidMessage                                 = SevError | 0x02E;
  static Error_InvalidMessage                                 = () => m(this.ERROR_InvalidMessage, `Invalid 'message' statement`);

  static ERROR_InvalidLanguageName                            = SevError | 0x02F;
  static Error_InvalidLanguageName                            = () => m(this.ERROR_InvalidLanguageName, `Invalid 'languagename' statement`);

  static ERROR_InvalidBitmapLine                              = SevError | 0x030;
  static Error_InvalidBitmapLine                              = () => m(this.ERROR_InvalidBitmapLine, `Invalid 'bitmaps' statement`);

  static ERROR_CannotReadBitmapFile                           = SevError | 0x031;
  static Error_CannotReadBitmapFile                           = () => m(this.ERROR_CannotReadBitmapFile, `Cannot open the bitmap or icon file for reading`);

  static ERROR_IndexDoesNotPointToAny                         = SevError | 0x032;
  static Error_IndexDoesNotPointToAny                         = () => m(this.ERROR_IndexDoesNotPointToAny, `An index() in the output does not have a corresponding any() statement`);

  static ERROR_ReservedCharacter                              = SevError | 0x033;
  static Error_ReservedCharacter                              = () => m(this.ERROR_ReservedCharacter, `A reserved character was found`);

  static ERROR_InvalidCharacter                               = SevError | 0x034;
  static Error_InvalidCharacter                               = () => m(this.ERROR_InvalidCharacter, `A character was found that is outside the valid Unicode range (U+0000 - U+10FFFF)`);

  static ERROR_InvalidCall                                    = SevError | 0x035;
  static Error_InvalidCall                                    = () => m(this.ERROR_InvalidCall, `The 'call' statement is invalid`);

  // static ERROR_CallInVirtualKeySection                        = SevError | 0x036; // no longer used, see #12612
  // static Error_CallInVirtualKeySection                        = () => m(this.ERROR_CallInVirtualKeySection, `'call' statement is illegal in virtual key section`);

  static ERROR_CodeInvalidInKeyStore                          = SevError | 0x037;
  static Error_CodeInvalidInKeyStore                          = () => m(this.ERROR_CodeInvalidInKeyStore, `The statement is invalid inside a store that is used in a key part of the rule`);

  static ERROR_CannotLoadIncludeFile                          = SevError | 0x038;
  static Error_CannotLoadIncludeFile                          = () => m(this.ERROR_CannotLoadIncludeFile, `Cannot load the included file: it is either invalid or does not exist`);


  static ERROR_60FeatureOnly_EthnologueCode                   = SevError | 0x039;
  static Error_60FeatureOnly_EthnologueCode                   = () => m(this.ERROR_60FeatureOnly_EthnologueCode, `EthnologueCode system store requires VERSION 6.0 or higher`);

  static ERROR_60FeatureOnly_MnemonicLayout                   = SevError | 0x03A;
  static Error_60FeatureOnly_MnemonicLayout                   = () => m(this.ERROR_60FeatureOnly_MnemonicLayout, `MnemonicLayout functionality requires VERSION 6.0 or higher`);

  static ERROR_60FeatureOnly_OldCharPosMatching               = SevError | 0x03B;
  static Error_60FeatureOnly_OldCharPosMatching               = () => m(this.ERROR_60FeatureOnly_OldCharPosMatching, `OldCharPosMatching system store requires VERSION 6.0 or higher`);

  static ERROR_60FeatureOnly_NamedCodes                       = SevError | 0x03C;
  static Error_60FeatureOnly_NamedCodes                       = () => m(this.ERROR_60FeatureOnly_NamedCodes, `Named character constants requires VERSION 6.0 or higher`);

  static ERROR_60FeatureOnly_Contextn                         = SevError | 0x03D;
  static Error_60FeatureOnly_Contextn                         = () => m(this.ERROR_60FeatureOnly_Contextn, `Context(n) requires VERSION 6.0 or higher`);

  static ERROR_501FeatureOnly_Call                            = SevError | 0x03E;
  static Error_501FeatureOnly_Call                            = () => m(this.ERROR_501FeatureOnly_Call, `Call() requires VERSION 5.01 or higher`);

  static ERROR_InvalidNamedCode                               = SevError | 0x03F;
  static Error_InvalidNamedCode                               = () => m(this.ERROR_InvalidNamedCode, `Invalid named code constant`);

  static ERROR_InvalidSystemStore                             = SevError | 0x040;
  static Error_InvalidSystemStore                             = () => m(this.ERROR_InvalidSystemStore, `Invalid system store name found`);

  static ERROR_60FeatureOnly_VirtualCharKey                   = SevError | 0x044;
  static Error_60FeatureOnly_VirtualCharKey                   = () => m(this.ERROR_60FeatureOnly_VirtualCharKey, `Virtual character keys require VERSION 6.0 or higher`);

  static ERROR_VersionAlreadyIncluded                         = SevError | 0x045;
  static Error_VersionAlreadyIncluded                         = () => m(this.ERROR_VersionAlreadyIncluded, `Only one VERSION or store(version) line allowed in a source file.`);

  static ERROR_70FeatureOnly                                  = SevError | 0x046;
  static Error_70FeatureOnly                                  = () => m(this.ERROR_70FeatureOnly, `This feature requires store(version) '7.0' or higher`);

  static ERROR_80FeatureOnly                                  = SevError | 0x047;
  static Error_80FeatureOnly                                  = () => m(this.ERROR_80FeatureOnly, `This feature requires store(version) '8.0' or higher`);

  // static ERROR_InvalidInVirtualKeySection                     = SevError | 0x048; // no longer used, see #12612
  // static Error_InvalidInVirtualKeySection                     = () => m(this.ERROR_InvalidInVirtualKeySection, `This statement is not valid in a virtual key section`);

  static ERROR_InvalidIf                                      = SevError | 0x049;
  static Error_InvalidIf                                      = () => m(this.ERROR_InvalidIf, `The if() statement is not valid`);

  static ERROR_InvalidReset                                   = SevError | 0x04A;
  static Error_InvalidReset                                   = () => m(this.ERROR_InvalidReset, `The reset() statement is not valid`);

  static ERROR_InvalidSet                                     = SevError | 0x04B;
  static Error_InvalidSet                                     = () => m(this.ERROR_InvalidSet, `The set() statement is not valid`);

  static ERROR_InvalidSave                                    = SevError | 0x04C;
  static Error_InvalidSave                                    = () => m(this.ERROR_InvalidSave, `The save() statement is not valid`);


  static ERROR_InvalidEthnologueCode                          = SevError | 0x04D;
  static Error_InvalidEthnologueCode                          = () => m(this.ERROR_InvalidEthnologueCode, `Invalid ethnologuecode format`);

  static FATAL_CannotCreateTempfile                           = SevFatal | 0x04E;
  static Fatal_CannotCreateTempfile                          = () => m(this.FATAL_CannotCreateTempfile, `Cannot create temp file`);

  static ERROR_90FeatureOnly_IfSystemStores                   = SevError | 0x04F;
  static Error_90FeatureOnly_IfSystemStores                   = () => m(this.ERROR_90FeatureOnly_IfSystemStores, `if(store) requires store(version) '9.0' or higher`);

  static ERROR_IfSystemStore_NotFound                         = SevError | 0x050;
  static Error_IfSystemStore_NotFound                         = () => m(this.ERROR_IfSystemStore_NotFound, `System store in if() not found`);

  static ERROR_90FeatureOnly_SetSystemStores                  = SevError | 0x051;
  static Error_90FeatureOnly_SetSystemStores                  = () => m(this.ERROR_90FeatureOnly_SetSystemStores, `set(store) requires store(version) '9.0' or higher`);

  static ERROR_SetSystemStore_NotFound                        = SevError | 0x052;
  static Error_SetSystemStore_NotFound                        = () => m(this.ERROR_SetSystemStore_NotFound, `System store in set() not found`);

  static ERROR_90FeatureOnlyVirtualKeyDictionary              = SevError | 0x053;
  static Error_90FeatureOnlyVirtualKeyDictionary              = () => m(this.ERROR_90FeatureOnlyVirtualKeyDictionary, `Custom virtual key names require store(version) '9.0'`);

  static ERROR_NotSupportedInKeymanWebContext                 = SevError | 0x054;
  static Error_NotSupportedInKeymanWebContext = (o:{line:number, code:String}) => mw(this.ERROR_NotSupportedInKeymanWebContext,
    `Statement '${def(o.code)}' is not currently supported in context for web and touch targets`, o);

  static ERROR_NotSupportedInKeymanWebOutput                  = SevError | 0x055;
  static Error_NotSupportedInKeymanWebOutput = (o:{line:number, code:string}) => mw(this.ERROR_NotSupportedInKeymanWebOutput,
    `Statement '${def(o.code)}' is not currently supported in output for web and touch targets`, o);

  static ERROR_NotSupportedInKeymanWebStore                   = SevError | 0x056;
  static Error_NotSupportedInKeymanWebStore = (o:{code:string,store:string}) => mw(this.ERROR_NotSupportedInKeymanWebStore,
    `'${def(o.code)}' is not currently supported in store '${def(o.store)}' when used by any or index for web and touch targets`);

  static ERROR_VirtualCharacterKeysNotSupportedInKeymanWeb    = SevError | 0x057;
  static Error_VirtualCharacterKeysNotSupportedInKeymanWeb = (o:{line:number}) => mw(this.ERROR_VirtualCharacterKeysNotSupportedInKeymanWeb,
    `Virtual character keys not currently supported in KeymanWeb`, o);

  static ERROR_VirtualKeysNotValidForMnemonicLayouts          = SevError | 0x058;
  static Error_VirtualKeysNotValidForMnemonicLayouts = (o:{line:number}) => mw(this.ERROR_VirtualKeysNotValidForMnemonicLayouts,
    `Virtual keys are not valid for mnemonic layouts`, o);

  static ERROR_InvalidTouchLayoutFile                         = SevError | 0x059;
  static Error_InvalidTouchLayoutFile = (o:{filename:string}) => mw(this.ERROR_InvalidTouchLayoutFile,
    `Touch layout file ${def(o.filename)} is not valid`);

  static ERROR_TouchLayoutInvalidIdentifier                   = SevError | 0x05A;
  static Error_TouchLayoutInvalidIdentifier = (o:{keyId:string, platformName: string, layerId:string, address:KeyAddress}) => mw(this.ERROR_TouchLayoutInvalidIdentifier,
    `Key "${def(o.keyId)}" on "${def(o.platformName)}", layer "${def(o.layerId)}" (${keyAddress(o.address)}) has an invalid identifier.`);

  static ERROR_InvalidKeyCode                                 = SevError | 0x05B;
  static Error_InvalidKeyCode = (o:{keyId: string}) => mw(this.ERROR_InvalidKeyCode,
    `Invalid key identifier "${def(o.keyId)}"`);

  static ERROR_90FeatureOnlyLayoutFile                        = SevError | 0x05C;
  static Error_90FeatureOnlyLayoutFile                        = () => m(this.ERROR_90FeatureOnlyLayoutFile, `Touch layout file reference requires store(version) '9.0' or higher`);

  static ERROR_90FeatureOnlyKeyboardVersion                   = SevError | 0x05D;
  static Error_90FeatureOnlyKeyboardVersion                   = () => m(this.ERROR_90FeatureOnlyKeyboardVersion, `KeyboardVersion system store requires store(version) '9.0' or higher`);

  static ERROR_KeyboardVersionFormatInvalid                   = SevError | 0x05E;
  static Error_KeyboardVersionFormatInvalid                   = () => m(this.ERROR_KeyboardVersionFormatInvalid, `KeyboardVersion format is invalid, expecting dot-separated integers`);

  static ERROR_ContextExHasInvalidOffset                      = SevError | 0x05F;
  static Error_ContextExHasInvalidOffset                      = () => m(this.ERROR_ContextExHasInvalidOffset, `context() statement has offset out of range`);

  static ERROR_90FeatureOnlyEmbedCSS                          = SevError | 0x060;
  static Error_90FeatureOnlyEmbedCSS                          = () => m(this.ERROR_90FeatureOnlyEmbedCSS, `Embedding CSS requires store(version) '9.0' or higher`);

  static ERROR_90FeatureOnlyTargets                           = SevError | 0x061;
  static Error_90FeatureOnlyTargets                           = () => m(this.ERROR_90FeatureOnlyTargets, `TARGETS system store requires store(version) '9.0' or higher`);

  static ERROR_ContextAndIndexInvalidInMatchNomatch           = SevError | 0x062;
  static Error_ContextAndIndexInvalidInMatchNomatch           = () => m(this.ERROR_ContextAndIndexInvalidInMatchNomatch, `context and index statements cannot be used in a match or nomatch statement`);

  static ERROR_140FeatureOnlyContextAndNotAnyWeb              = SevError | 0x063;
  static Error_140FeatureOnlyContextAndNotAnyWeb              = () => m(this.ERROR_140FeatureOnlyContextAndNotAnyWeb, `For web and touch platforms, context() statement referring to notany() requires store(version) '14.0' or higher`);


  static ERROR_ExpansionMustFollowCharacterOrVKey             = SevError | 0x064;
  static Error_ExpansionMustFollowCharacterOrVKey             = () => m(this.ERROR_ExpansionMustFollowCharacterOrVKey, `An expansion must follow a character or a virtual key`);

  static ERROR_VKeyExpansionMustBeFollowedByVKey              = SevError | 0x065;
  static Error_VKeyExpansionMustBeFollowedByVKey              = () => m(this.ERROR_VKeyExpansionMustBeFollowedByVKey, `A virtual key expansion must be terminated by a virtual key`);

  static ERROR_CharacterExpansionMustBeFollowedByCharacter    = SevError | 0x066;
  static Error_CharacterExpansionMustBeFollowedByCharacter    = () => m(this.ERROR_CharacterExpansionMustBeFollowedByCharacter, `A character expansion must be terminated by a character key`);

  static ERROR_VKeyExpansionMustUseConsistentShift            = SevError | 0x067;
  static Error_VKeyExpansionMustUseConsistentShift            = () => m(this.ERROR_VKeyExpansionMustUseConsistentShift, `A virtual key expansion must use the same shift state for both terminators`);

  static ERROR_ExpansionMustBePositive                        = SevError | 0x068;
  static Error_ExpansionMustBePositive                        = () => m(this.ERROR_ExpansionMustBePositive, `An expansion must have positive difference (i.e. A-Z, not Z-A)`);

  static ERROR_CasedKeysMustContainOnlyVirtualKeys            = SevError | 0x069;
  static Error_CasedKeysMustContainOnlyVirtualKeys            = () => m(this.ERROR_CasedKeysMustContainOnlyVirtualKeys, `The &CasedKeys system store must contain only virtual keys or characters found on a US English keyboard`);

  static ERROR_CasedKeysMustNotIncludeShiftStates             = SevError | 0x06A;
  static Error_CasedKeysMustNotIncludeShiftStates             = () => m(this.ERROR_CasedKeysMustNotIncludeShiftStates, `The &CasedKeys system store must not include shift states`);

  static ERROR_CasedKeysNotSupportedWithMnemonicLayout        = SevError | 0x06B;
  static Error_CasedKeysNotSupportedWithMnemonicLayout        = () => m(this.ERROR_CasedKeysNotSupportedWithMnemonicLayout, `The &CasedKeys system store is not supported with mnemonic layouts`);

  static ERROR_CannotUseReadWriteGroupFromReadonlyGroup       = SevError | 0x06C;
  static Error_CannotUseReadWriteGroupFromReadonlyGroup       = () => m(this.ERROR_CannotUseReadWriteGroupFromReadonlyGroup, `Group used from a readonly group must also be readonly`);

  static ERROR_StatementNotPermittedInReadonlyGroup           = SevError | 0x06D;
  static Error_StatementNotPermittedInReadonlyGroup           = () => m(this.ERROR_StatementNotPermittedInReadonlyGroup, `Statement is not permitted in output of readonly group`);

  static ERROR_OutputInReadonlyGroup                          = SevError | 0x06E;
  static Error_OutputInReadonlyGroup                          = () => m(this.ERROR_OutputInReadonlyGroup, `Output is not permitted in a readonly group`);

  static ERROR_NewContextGroupMustBeReadonly                  = SevError | 0x06F;
  static Error_NewContextGroupMustBeReadonly                  = () => m(this.ERROR_NewContextGroupMustBeReadonly, `Group used in begin newContext must be readonly`);

  static ERROR_PostKeystrokeGroupMustBeReadonly               = SevError | 0x070;
  static Error_PostKeystrokeGroupMustBeReadonly               = () => m(this.ERROR_PostKeystrokeGroupMustBeReadonly, `Group used in begin postKeystroke must be readonly`);

  static ERROR_DuplicateGroup                                 = SevError | 0x071;
  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                                 = (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`);

  static ERROR_VirtualKeyInContext                            = SevError | 0x074;
  static Error_VirtualKeyInContext                            = () => m(this.ERROR_VirtualKeyInContext, `Virtual keys are not permitted in context`);

  static ERROR_OutsTooLong                                    = SevError | 0x075;
  static Error_OutsTooLong                                    = () => m(this.ERROR_OutsTooLong, `Store cannot be inserted with outs() as it makes the extended string too long`);

  static ERROR_ExtendedStringTooLong                          = SevError | 0x076;
  static Error_ExtendedStringTooLong                          = () => m(this.ERROR_ExtendedStringTooLong, `Extended string is too long`);

  static ERROR_VirtualKeyExpansionTooLong                     = SevError | 0x077;
  static Error_VirtualKeyExpansionTooLong                     = () => m(this.ERROR_VirtualKeyExpansionTooLong, `Virtual key expansion is too large`);

  static ERROR_CharacterRangeTooLong                          = SevError | 0x078;
  static Error_CharacterRangeTooLong                          = () => m(this.ERROR_CharacterRangeTooLong, `Character range is too large and cannot be expanded`);

  static ERROR_NonBMPCharactersNotSupportedInKeySection       = SevError | 0x079;
  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                                  = (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`);

  static WARN_TooManyWarnings                                 = SevWarn | 0x080;
  static Warn_TooManyWarnings                                 = () => m(this.WARN_TooManyWarnings, `Too many warnings or errors`);

  static WARN_OldVersion                                      = SevWarn | 0x081;
  static Warn_OldVersion                                      = () => m(this.WARN_OldVersion, `The keyboard file is an old version`);

  static WARN_BitmapNotUsed                                   = SevWarn | 0x082;
  static Warn_BitmapNotUsed                                   = () => m(this.WARN_BitmapNotUsed, `The 'bitmaps' statement is obsolete and only the first bitmap referred to will be used, you should use 'bitmap'.`);

  static WARN_CustomLanguagesNotSupported                     = SevWarn | 0x083;
  static Warn_CustomLanguagesNotSupported                     = () => m(this.WARN_CustomLanguagesNotSupported, `Languages over 0x1FF, 0x1F are not supported correctly by Windows. You should use no LANGUAGE line instead.`);

  static WARN_KeyBadLength                                    = SevWarn | 0x084;
  static Warn_KeyBadLength                                    = () => m(this.WARN_KeyBadLength, `There are too many characters in the keystroke part of the rule.`);

  static WARN_IndexStoreShort                                 = SevWarn | 0x085;
  static Warn_IndexStoreShort                                 = () => m(this.WARN_IndexStoreShort, `The store referenced in index() is shorter than the store referenced in any()`);

  static WARN_UnicodeInANSIGroup                              = SevWarn | 0x086;
  static Warn_UnicodeInANSIGroup                              = () => m(this.WARN_UnicodeInANSIGroup, `A Unicode character was found in an ANSI group`);

  static WARN_ANSIInUnicodeGroup                              = SevWarn | 0x087;
  static Warn_ANSIInUnicodeGroup                              = () => m(this.WARN_ANSIInUnicodeGroup, `An ANSI character was found in a Unicode group`);

  static WARN_UnicodeSurrogateUsed                            = SevWarn | 0x088;
  static Warn_UnicodeSurrogateUsed                            = () => m(this.WARN_UnicodeSurrogateUsed, `A Unicode surrogate character was found. You should use Unicode scalar values to represent values > U+FFFF`);

  static WARN_ReservedCharacter                               = SevWarn | 0x089;
  static Warn_ReservedCharacter                               = () => m(this.WARN_ReservedCharacter, `A Unicode character was found that should not be used`);

  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`);

  static WARN_VirtualCharKeyWithPositionalLayout              = SevWarn | 0x08C;
  static Warn_VirtualCharKeyWithPositionalLayout              = () => m(this.WARN_VirtualCharKeyWithPositionalLayout, `Virtual character key used with a positional layout instead of mnemonic layout`);

  static WARN_StoreAlreadyUsedAsOptionOrCall                  = SevWarn | 0x08D;
  static Warn_StoreAlreadyUsedAsOptionOrCall                  = () => m(this.WARN_StoreAlreadyUsedAsOptionOrCall, `Store already used as an option or in a call statement and should not be used as a normal store`);

  static WARN_StoreAlreadyUsedAsStoreOrCall                   = SevWarn | 0x08E;
  static Warn_StoreAlreadyUsedAsStoreOrCall                   = () => m(this.WARN_StoreAlreadyUsedAsStoreOrCall, `Store already used as a normal store or in a call statement and should not be used as an option`);

  static WARN_StoreAlreadyUsedAsStoreOrOption                 = SevWarn | 0x08F;
  static Warn_StoreAlreadyUsedAsStoreOrOption                 = () => m(this.WARN_StoreAlreadyUsedAsStoreOrOption, `Store already used as a normal store or as an option and should not be used in a call statement`);


  static WARN_PunctuationInEthnologueCode                     = SevWarn | 0x090;
  static Warn_PunctuationInEthnologueCode                     = () => m(this.WARN_PunctuationInEthnologueCode, `Punctuation should not be used to separate Ethnologue codes; instead use spaces`);

  static WARN_TouchLayoutMissingLayer                         = SevWarn | 0x091;
  static Warn_TouchLayoutMissingLayer = (o:{keyId:string, platformName:string, layerId:string, nextLayer:string, address:KeyAddress}) => mw(this.WARN_TouchLayoutMissingLayer,
    `Key "${def(o.keyId)}" on platform "${def(o.platformName)}", layer "${def(o.layerId)}" (${keyAddress(o.address)}), references a missing layer "${def(o.nextLayer)}"`);

  static WARN_TouchLayoutCustomKeyNotDefined                  = SevWarn | 0x092;
  static Warn_TouchLayoutCustomKeyNotDefined = (o:{keyId:string, platformName:string, layerId:string, address:KeyAddress}) => mw(this.WARN_TouchLayoutCustomKeyNotDefined,
    `Key "${def(o.keyId)}" on platform "${def(o.platformName)}", layer "${def(o.layerId)}"  (${keyAddress(o.address)}), is a custom key but has no corresponding rule in the source.`);

  static WARN_TouchLayoutMissingRequiredKeys                  = SevWarn | 0x093;
  static Warn_TouchLayoutMissingRequiredKeys = (o:{layerId:string, platformName:string, missingKeys:string}) => mw(this.WARN_TouchLayoutMissingRequiredKeys,
    `Layer "${def(o.layerId)}" on platform "${def(o.platformName)}" is missing the required key(s) '${def(o.missingKeys)}'.`);

  static WARN_HelpFileMissing                                 = SevWarn | 0x094;
  static Warn_HelpFileMissing = (o:{line:number, helpFilename:string, e:any}) => mw(this.WARN_HelpFileMissing,
    `File ${def(o.helpFilename)} could not be loaded: ${(o.e??'').toString()}`,o);

  static WARN_EmbedJsFileMissing                              = SevWarn | 0x095;
  static Warn_EmbedJsFileMissing = (o:{line:number, jsFilename: string, e:any}) => mw(this.WARN_EmbedJsFileMissing,
    `File ${def(o.jsFilename)} could not be loaded: ${(o.e??'').toString()}`, o);

  // static WARN_TouchLayoutFileMissing                          = SevWarn | 0x096;
  // static WARN_VisualKeyboardFileMissing                       = SevWarn | 0x097;

  static WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb       = SevWarn | 0x098;
  static Warn_ExtendedShiftFlagsNotSupportedInKeymanWeb = (o:{line:number,flags:string}) => mw(this.WARN_ExtendedShiftFlagsNotSupportedInKeymanWeb,
    `Extended shift flags ${def(o.flags)} are not supported in KeymanWeb`, o);

  static WARN_TouchLayoutUnidentifiedKey                      = SevWarn | 0x099;
  static Warn_TouchLayoutUnidentifiedKey = (o:{layerId:string, address:KeyAddress}) => mw(this.WARN_TouchLayoutUnidentifiedKey,
    `A key (${keyAddress(o.address)}) on layer "${def(o.layerId)}" has no identifier.`);

  static HINT_UnreachableKeyCode                              = SevHint | 0x09A;
  static Hint_UnreachableKeyCode = (o:{line:number,key:string}) => mw(this.HINT_UnreachableKeyCode,
    `The rule will never be matched for key ${def(o.key)} because its key code is never fired.`, o);

  // static WARN_CouldNotCopyJsonFile                            = SevWarn | 0x09B;

  static WARN_PlatformNotInTargets                            = SevWarn | 0x09C;
  static Warn_PlatformNotInTargets                            = () => m(this.WARN_PlatformNotInTargets, `The specified platform is not a target platform`);

  static WARN_HeaderStatementIsDeprecated                     = SevWarn | 0x09D;
  static Warn_HeaderStatementIsDeprecated                     = () => m(this.WARN_HeaderStatementIsDeprecated, `Header statements are deprecated; use instead the equivalent system store`);

  static WARN_UseNotLastStatementInRule                       = SevWarn | 0x09E;
  static Warn_UseNotLastStatementInRule                       = () => m(this.WARN_UseNotLastStatementInRule, `A rule with use() statements in the output should not have other content following the use() statements`);

  static WARN_TouchLayoutFontShouldBeSameForAllPlatforms      = SevWarn | 0x09F;
  static Warn_TouchLayoutFontShouldBeSameForAllPlatforms = () => mw(this.WARN_TouchLayoutFontShouldBeSameForAllPlatforms,
    `The touch layout font should be the same for all platforms.`);

  // static WARN_InvalidJSONMetadataFile                         = SevWarn | 0x0A0;
  // static WARN_JSONMetadataOSKFontShouldMatchTouchFont         = SevWarn | 0x0A1;

  static WARN_KVKFileIsInSourceFormat                         = SevWarn | 0x0A2;
  static Warn_KVKFileIsInSourceFormat                         = () => m(this.WARN_KVKFileIsInSourceFormat, `.kvk file should be binary but is an XML file`);

  // kmcmplib: static Warn_DontMixChiralAndNonChiralModifiers              = () => m(this.WARN_DontMixChiralAndNonChiralModifiers, `Don't mix the use of left/right modifiers with non-left/right modifiers in the same platform`);
  static WARN_DontMixChiralAndNonChiralModifiers              = SevWarn | 0x0A3;
  static Warn_DontMixChiralAndNonChiralModifiers = () => mw(this.WARN_DontMixChiralAndNonChiralModifiers,
    `This keyboard contains Ctrl,Alt and LCtrl,LAlt,RCtrl,RAlt sets of modifiers. Use only one or the other set for web target.`);

  static WARN_MixingLeftAndRightModifiers                     = SevWarn | 0x0A4;
  static Warn_MixingLeftAndRightModifiers                     = () => m(this.WARN_MixingLeftAndRightModifiers, `Left and right modifiers should not both be used in the same rule`);

  static WARN_LanguageHeadersDeprecatedInKeyman10             = SevWarn | 0x0A5;
  static Warn_LanguageHeadersDeprecatedInKeyman10             = () => m(this.WARN_LanguageHeadersDeprecatedInKeyman10, `This language header has been deprecated in Keyman 10. Instead, add language metadata in the package file`);

  static HINT_NonUnicodeFile                                  = SevHint | 0x0A6;
  static Hint_NonUnicodeFile                                  = () => m(this.HINT_NonUnicodeFile, `Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8`);

  // static WARN_TooManyErrorsOrWarnings                         = SevWarn | 0x0A7;

  static WARN_HotkeyHasInvalidModifier                        = SevWarn | 0x0A8;
  static Warn_HotkeyHasInvalidModifier                        = () => m(this.WARN_HotkeyHasInvalidModifier, `Hotkey has modifiers that are not supported. Use only SHIFT, CTRL and ALT`);

  static WARN_TouchLayoutSpecialLabelOnNormalKey              = SevWarn | 0x0A9;
  static Warn_TouchLayoutSpecialLabelOnNormalKey = (o:{keyId:string, platformName:string, layerId:string, label:string, address:KeyAddress}) =>
    mw(this.WARN_TouchLayoutSpecialLabelOnNormalKey,
    `Key "${def(o.keyId)}" on platform "${def(o.platformName)}", layer "${def(o.layerId)}"  (${keyAddress(o.address)}) does not have `+
    `the key type "Special" or "Special (active)" but has the label "${def(o.label)}". This feature is only supported in Keyman 14 or later`);

  static WARN_OptionStoreNameInvalid                          = SevWarn | 0x0AA;
  static Warn_OptionStoreNameInvalid = (o:{name:string}) => mw(this.WARN_OptionStoreNameInvalid,
    `The option store ${def(o.name)} should be named with characters in the range A-Z, a-z, 0-9 and _ only.`);

  static WARN_NulNotFirstStatementInContext                   = SevWarn | 0x0AB;
  static Warn_NulNotFirstStatementInContext                   = () => m(this.WARN_NulNotFirstStatementInContext, `nul must be the first statement in the context`);

  static WARN_IfShouldBeAtStartOfContext                      = SevWarn | 0x0AC;
  static Warn_IfShouldBeAtStartOfContext                      = () => m(this.WARN_IfShouldBeAtStartOfContext, `if, platform and baselayout should be at start of context (after nul, if present)`);

  static WARN_KeyShouldIncludeNCaps                           = SevWarn | 0x0AD;
  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                                 = (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`);

  static HINT_IndexStoreLong                                  = SevHint | 0x0B0;
  static Hint_IndexStoreLong                                  = () => m(
    this.HINT_IndexStoreLong,
    `The store referenced in index() is longer than the store referenced in any()`,
  );

  static ERROR_ContextExCannotReferenceIf                     = SevError | 0x0B1;
  static Error_ContextExCannotReferenceIf                     = () => m(
    this.ERROR_ContextExCannotReferenceIf,
    `The offset in context() points to a non-character if() statement in the context`,
  );

  static ERROR_ContextExCannotReferenceNul                    = SevError | 0x0B2;
  static Error_ContextExCannotReferenceNul                    = () => m(
    this.ERROR_ContextExCannotReferenceNul,
    `The offset in context() points to a non-character nul statement in the context`,
  );

  static ERROR_TextBeforeOrAfterNulInOutput                   = SevError | 0x0B3;
  static Error_TextBeforeOrAfterNulInOutput                    = () => m(
    this.ERROR_TextBeforeOrAfterNulInOutput,
    `A rule with a nul statement in the output cannot also output text or text-emitting statements`,
    `If a rule has [\`nul\`](https://help.keyman.com/developer/language/reference/nul)
    in the output, it means that it deletes any context referenced on the left-hand
    side of the rule, and has no other output, so it does not make sense to have any
    content apart from the \`nul\` statement in the output.

    The following statements can still be used with \`nul\` in the output:
    * [\`beep()\`](https://help.keyman.com/developer/language/reference/beep)
    * [\`call()\`](https://help.keyman.com/developer/language/reference/call)
    * [\`reset()\`](https://help.keyman.com/developer/language/reference/reset)
    * [\`return\`](https://help.keyman.com/developer/language/reference/return)
    * [\`save()\`](https://help.keyman.com/developer/language/reference/save)
    * [\`set()\`](https://help.keyman.com/developer/language/reference/set)
    * [\`use()\`](https://help.keyman.com/developer/language/reference/use)`
  );

  static ERROR_NameMustBeAtLeastOneCharLong                   = SevError | 0x0B4;
  static Error_NameMustBeAtLeastOneCharLong                   = () => m(
    this.ERROR_NameMustBeAtLeastOneCharLong,
    `The name parameter is required`,
  );

  static ERROR_NameMustBeAtMostNCharsLong                     = SevError | 0x0B5;
  static Error_NameMustBeAtMostNCharsLong                     = (o:{name:string,length:string}) => m(
    this.ERROR_NameMustBeAtMostNCharsLong,
    `The referenced name '${def(o.name)}' must be at most ${def(o.length)} characters long`,
  );

  static ERROR_NameContainsInvalidCharacter                   = SevError | 0x0B6;
  static Error_NameContainsInvalidCharacter                   = (o:{name:string}) => m(
    this.ERROR_NameContainsInvalidCharacter,
    `The referenced name '${def(o.name)}' contains an invalid character`,
    `Names (or identifiers) must not contain spaces, commas, parentheses,
    square brackets, control characters, or Unicode non-characters.`
  );

  static ERROR_NameMustNotContainSpaces                       = SevError | 0x0B7;
  static Error_NameMustNotContainSpaces                       = (o:{name:string}) => m(
    this.ERROR_NameMustNotContainSpaces,
    `The referenced name '${def(o.name)}' must not contain spaces`,
    `Names (or identifiers) must not contain spaces, commas, parentheses,
    square brackets, control characters, or Unicode non-characters.`
  );

  static ERROR_NameMustNotContainComma                        = SevError | 0x0B8;
  static Error_NameMustNotContainComma                        = (o:{name:string}) => m(
    this.ERROR_NameMustNotContainComma,
    `The referenced name '${def(o.name)}' must not contain commas`,
    `Names (or identifiers) must not contain spaces, commas, parentheses,
    square brackets, control characters, or Unicode non-characters.`
  );

  static ERROR_NameMustNotContainParentheses                  = SevError | 0x0B9;
  static Error_NameMustNotContainParentheses                  = (o:{name:string}) => m(
    this.ERROR_NameMustNotContainParentheses,
    `The referenced name '${def(o.name)}' must not contain opening or closing parentheses`,
    `Names (or identifiers) must not contain spaces, commas, parentheses,
    square brackets, control characters, or Unicode non-characters.`
  );

  static ERROR_NameMustNotContainSquareBrackets               = SevError | 0x0BA;
  static Error_NameMustNotContainSquareBrackets               = (o:{name:string}) => m(
    this.ERROR_NameMustNotContainSquareBrackets,
    `The referenced name '${def(o.name)}' must not contain opening or closing square brackets`,
    `Names (or identifiers) must not contain spaces, commas, parentheses,
    square brackets, control characters, or Unicode non-characters.`
  );

  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`);
};
