### Imports ###
import argparse
+import importlib
import io
import os
import re
import sys
import token
-import importlib
-from tokenize import generate_tokens
-from re import VERBOSE
from pprint import pformat
+from re import VERBOSE
+from tokenize import generate_tokens
-from kll_lib.hid_dict import *
from kll_lib.containers import *
+from kll_lib.hid_dict import *
from funcparserlib.lexer import make_tokenizer, Token, LexerError
from funcparserlib.parser import (some, a, many, oneplus, skip, finished, maybe, skip, forward_decl, NoParseError)
# Optional Arguments
pArgs.add_argument( '-b', '--backend', type=str, default="kiibohd",
help="Specify target backend for the KLL compiler.\n"
- "Default: kiibohd" )
+ "Default: kiibohd\n"
+ "Options: kiibohd, json" )
pArgs.add_argument( '-d', '--default', type=str, nargs='+',
help="Specify .kll files to layer on top of the default map to create a combined map." )
pArgs.add_argument( '-p', '--partial', type=str, nargs='+', action='append',
help="Specify .kll files to generate partial map, multiple files per flag.\n"
"Each -p defines another partial map.\n"
"Base .kll files (that define the scan code maps) must be defined for each partial map." )
- pArgs.add_argument( '-t', '--template', type=str, default="templates/kiibohdKeymap.h",
+ pArgs.add_argument( '-t', '--templates', type=str, nargs='+',
help="Specify template used to generate the keymap.\n"
- "Default: templates/kiibohdKeymap.h" )
- pArgs.add_argument( '--defines-template', type=str, default="templates/kiibohdDefs.h",
- help="Specify template used to generate kll_defs.h.\n"
- "Default: templates/kiibohdDefs.h" )
- pArgs.add_argument( '-o', '--output', type=str, default="generatedKeymap.h",
+ "Default: <backend specific>" )
+ pArgs.add_argument( '-o', '--outputs', type=str, nargs='+',
help="Specify output file. Writes to current working directory by default.\n"
- "Default: generatedKeymap.h" )
- pArgs.add_argument( '--defines-output', type=str, default="kll_defs.h",
- help="Specify output path for kll_defs.h. Writes to current working directory by default.\n"
- "Default: kll_defs.h" )
+ "Default: <backend specific>" )
pArgs.add_argument( '-h', '--help', action="help",
help="This message." )
for filename in partial:
checkFileExists( filename )
- return (baseFiles, defaultFiles, partialFileSets, args.backend, args.template, args.defines_template, args.output, args.defines_output)
+ return (baseFiles, defaultFiles, partialFileSets, args.backend, args.templates, args.outputs)
( 'Space', ( r'[ \t\r\n]+', ) ),
( 'USBCode', ( r'U(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ),
( 'USBCodeStart', ( r'U\[', ) ),
+ ( 'ConsCode', ( r'CONS(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ),
+ ( 'ConsCodeStart', ( r'CONS\[', ) ),
+ ( 'SysCode', ( r'SYS(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ),
+ ( 'SysCodeStart', ( r'SYS\[', ) ),
+ ( 'LedCode', ( r'LED(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ),
+ ( 'LedCodeStart', ( r'LED\[', ) ),
( 'ScanCode', ( r'S((0x[0-9a-fA-F]+)|([0-9]+))', ) ),
( 'ScanCodeStart', ( r'S\[', ) ),
( 'CodeEnd', ( r'\]', ) ),
( 'Dash', ( r'-', ) ),
( 'Plus', ( r'\+', ) ),
( 'Parenthesis', ( r'\(|\)', ) ),
+ ( 'None', ( r'None', ) ),
( 'Number', ( r'-?(0x[0-9a-fA-F]+)|(0|([1-9][0-9]*))', VERBOSE ) ),
( 'Name', ( r'[A-Za-z_][A-Za-z_0-9]*', ) ),
( 'VariableContents', ( r'''[^"' ;:=>()]+''', ) ),
def make_scanCode( token ):
scanCode = int( token[1:], 0 )
# Check size, to make sure it's valid
- if scanCode > 0xFF:
- print ( "{0} ScanCode value {1} is larger than 255".format( ERROR, scanCode ) )
- raise
+ # XXX Add better check that takes symbolic names into account (i.e. U"Latch5")
+ #if scanCode > 0xFF:
+ # print ( "{0} ScanCode value {1} is larger than 255".format( ERROR, scanCode ) )
+ # raise
return scanCode
-def make_usbCode( token ):
+def make_hidCode( type, token ):
# If first character is a U, strip
if token[0] == "U":
token = token[1:]
+ # CONS specifier
+ elif 'CONS' in token:
+ token = token[4:]
+ # SYS specifier
+ elif 'SYS' in token:
+ token = token[3:]
# If using string representation of USB Code, do lookup, case-insensitive
if '"' in token:
try:
- usbCode = kll_hid_lookup_dictionary[ token[1:-1].upper() ]
+ hidCode = kll_hid_lookup_dictionary[ type ][ token[1:-1].upper() ][1]
except LookupError as err:
- print ( "{0} {1} is an invalid USB Code Lookup...".format( ERROR, err ) )
+ print ( "{0} {1} is an invalid USB HID Code Lookup...".format( ERROR, err ) )
raise
else:
- usbCode = int( token, 0 )
+ # Already tokenized
+ if type == 'USBCode' and token[0] == 'USB' or type == 'SysCode' and token[0] == 'SYS' or type == 'ConsCode' and token[0] == 'CONS':
+ hidCode = token[1]
+ # Convert
+ else:
+ hidCode = int( token, 0 )
+
+ # Check size if a USB Code, to make sure it's valid
+ # XXX Add better check that takes symbolic names into account (i.e. U"Latch5")
+ #if type == 'USBCode' and hidCode > 0xFF:
+ # print ( "{0} USBCode value {1} is larger than 255".format( ERROR, hidCode ) )
+ # raise
+
+ # Return a tuple, identifying which type it is
+ if type == 'USBCode':
+ return make_usbCode_number( hidCode )
+ elif type == 'ConsCode':
+ return make_consCode_number( hidCode )
+ elif type == 'SysCode':
+ return make_sysCode_number( hidCode )
+
+ print ( "{0} Unknown HID Specifier '{1}'".format( ERROR, type ) )
+ raise
- # Check size, to make sure it's valid
- if usbCode > 0xFF:
- print ( "{0} USBCode value {1} is larger than 255".format( ERROR, usbCode ) )
- raise
- return usbCode
+def make_usbCode( token ):
+ return make_hidCode( 'USBCode', token )
+
+def make_consCode( token ):
+ return make_hidCode( 'ConsCode', token )
+
+def make_sysCode( token ):
+ return make_hidCode( 'SysCode', token )
+
+def make_hidCode_number( type, token ):
+ lookup = {
+ 'ConsCode' : 'CONS',
+ 'SysCode' : 'SYS',
+ 'USBCode' : 'USB',
+ }
+ return ( lookup[ type ], token )
+
+def make_usbCode_number( token ):
+ return make_hidCode_number( 'USBCode', token )
+
+def make_consCode_number( token ):
+ return make_hidCode_number( 'ConsCode', token )
+
+def make_sysCode_number( token ):
+ return make_hidCode_number( 'SysCode', token )
+
+ # Replace key-word with None specifier (which indicates a noneOut capability)
+def make_none( token ):
+ return [[[('NONE', 0)]]]
def make_seqString( token ):
# Shifted Characters, and amount to move by to get non-shifted version
( ":", 0x01 ),
( "^", -0x10 ),
( "_", -0x18 ),
- ( "{}|", -0x1E ),
- ( "~", -0x20 ),
+ ( "{}|~", -0x1E ),
( "@", -0x32 ),
( "?", -0x38 ),
)
listOfLists = []
- shiftKey = kll_hid_lookup_dictionary["SHIFT"]
+ shiftKey = kll_hid_lookup_dictionary['USBCode']["SHIFT"]
# Creates a list of USB codes from the string: sequence (list) of combos (lists)
for char in token[1:-1]:
# Do KLL HID Lookup on non-shifted character
# NOTE: Case-insensitive, which is why the shift must be pre-computed
- usbCode = kll_hid_lookup_dictionary[ processedChar.upper() ]
+ usbCode = kll_hid_lookup_dictionary['USBCode'][ processedChar.upper() ]
# Create Combo for this character, add shift key if shifted
charCombo = []
return list( range( start, end + 1 ) )
# Range can go from high to low or low to high
- # Warn on 0-9 (as this does not do what one would expect) TODO
+ # Warn on 0-9 for USBCodes (as this does not do what one would expect) TODO
# Lookup USB HID tags and convert to a number
-def make_usbCode_range( rangeVals ):
+def make_hidCode_range( type, rangeVals ):
# Check if already integers
if isinstance( rangeVals[0], int ):
start = rangeVals[0]
else:
- start = make_usbCode( rangeVals[0] )
+ start = make_hidCode( type, rangeVals[0] )[1]
if isinstance( rangeVals[1], int ):
end = rangeVals[1]
else:
- end = make_usbCode( rangeVals[1] )
+ end = make_hidCode( type, rangeVals[1] )[1]
# Swap start, end if start is greater than end
if start > end:
start, end = end, start
# Iterate from start to end, and generate the range
- return list( range( start, end + 1 ) )
- pass
+ listRange = list( range( start, end + 1 ) )
+
+ # Convert each item in the list to a tuple
+ for item in range( len( listRange ) ):
+ listRange[ item ] = make_hidCode_number( type, listRange[ item ] )
+ return listRange
+
+def make_usbCode_range( rangeVals ):
+ return make_hidCode_range( 'USBCode', rangeVals )
+
+def make_sysCode_range( rangeVals ):
+ return make_hidCode_range( 'SysCode', rangeVals )
+
+def make_consCode_range( rangeVals ):
+ return make_hidCode_range( 'ConsCode', rangeVals )
## Base Rules
# Converts USB Codes into Capabilities
-def usbCodeToCapability( items ):
+# These are tuples (<type>, <integer>)
+def hidCodeToCapability( items ):
# Items already converted to variants using optionExpansion
for variant in range( 0, len( items ) ):
# Sequence of Combos
for sequence in range( 0, len( items[ variant ] ) ):
for combo in range( 0, len( items[ variant ][ sequence ] ) ):
- # Only convert if an integer, otherwise USB Code doesn't need converting
- if isinstance( items[ variant ][ sequence ][ combo ], int ):
- # Use backend capability name and a single argument
- items[ variant ][ sequence ][ combo ] = tuple( [ backend.usbCodeCapability(), tuple( [ items[ variant ][ sequence ][ combo ] ] ) ] )
-
+ if items[ variant ][ sequence ][ combo ][0] in backend.requiredCapabilities.keys():
+ try:
+ # Use backend capability name and a single argument
+ items[ variant ][ sequence ][ combo ] = tuple(
+ [ backend.capabilityLookup( items[ variant ][ sequence ][ combo ][0] ),
+ tuple( [ hid_lookup_dictionary[ items[ variant ][ sequence ][ combo ] ] ] ) ]
+ )
+ except KeyError:
+ print ( "{0} {1} is an invalid HID lookup value".format( ERROR, items[ variant ][ sequence ][ combo ] ) )
+ sys.exit( 1 )
return items
+# Convert tuple of tuples to list of lists
+def listit( t ):
+ return list( map( listit, t ) ) if isinstance( t, ( list, tuple ) ) else t
+
+# Convert list of lists to tuple of tuples
+def tupleit( t ):
+ return tuple( map( tupleit, t ) ) if isinstance( t, ( tuple, list ) ) else t
+
+
## Evaluation Rules
def eval_scanCode( triggers, operator, results ):
triggers = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in triggers )
results = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in results )
+ # Lookup interconnect id (Current file scope)
+ # Default to 0 if not specified
+ if 'ConnectId' not in variables_dict.overallVariables.keys():
+ id_num = 0
+ else:
+ id_num = int( variables_dict.overallVariables['ConnectId'] )
+
# Iterate over all combinations of triggers and results
- for trigger in triggers:
+ for sequence in triggers:
+ # Convert tuple of tuples to list of lists so each element can be modified
+ trigger = listit( sequence )
+
+ # Create ScanCode entries for trigger
+ for seq_index, combo in enumerate( sequence ):
+ for com_index, scancode in enumerate( combo ):
+ trigger[ seq_index ][ com_index ] = macros_map.scanCodeStore.append( ScanCode( scancode, id_num ) )
+
+ # Convert back to a tuple of tuples
+ trigger = tupleit( trigger )
+
for result in results:
# Append Case
if operator == ":+":
usbCode = tokenType('USBCode') >> make_usbCode
scanCode = tokenType('ScanCode') >> make_scanCode
+consCode = tokenType('ConsCode') >> make_consCode
+sysCode = tokenType('SysCode') >> make_sysCode
+none = tokenType('None') >> make_none
name = tokenType('Name')
number = tokenType('Number') >> make_number
comma = tokenType('Comma')
# USB Codes
usbCode_start = tokenType('USBCodeStart')
-usbCode_range = ( number | unString ) + skip( dash ) + ( number | unString ) >> make_usbCode_range
+usbCode_number = number >> make_usbCode_number
+usbCode_range = ( usbCode_number | unString ) + skip( dash ) + ( number | unString ) >> make_usbCode_range
usbCode_listElemTag = unString >> make_usbCode
-usbCode_listElem = ( number | usbCode_listElemTag ) >> listElem
+usbCode_listElem = ( usbCode_number | usbCode_listElemTag ) >> listElem
usbCode_innerList = oneplus( ( usbCode_range | usbCode_listElem ) + skip( maybe( comma ) ) ) >> flatten
usbCode_expanded = skip( usbCode_start ) + usbCode_innerList + skip( code_end )
usbCode_elem = usbCode >> listElem
usbCode_combo = oneplus( ( usbCode_expanded | usbCode_elem ) + skip( maybe( plus ) ) ) >> listElem
usbCode_sequence = oneplus( ( usbCode_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten
+ # Cons Codes
+consCode_start = tokenType('ConsCodeStart')
+consCode_number = number >> make_consCode_number
+consCode_range = ( consCode_number | unString ) + skip( dash ) + ( number | unString ) >> make_consCode_range
+consCode_listElemTag = unString >> make_consCode
+consCode_listElem = ( consCode_number | consCode_listElemTag ) >> listElem
+consCode_innerList = oneplus( ( consCode_range | consCode_listElem ) + skip( maybe( comma ) ) ) >> flatten
+consCode_expanded = skip( consCode_start ) + consCode_innerList + skip( code_end )
+consCode_elem = consCode >> listElem
+
+ # Sys Codes
+sysCode_start = tokenType('SysCodeStart')
+sysCode_number = number >> make_sysCode_number
+sysCode_range = ( sysCode_number | unString ) + skip( dash ) + ( number | unString ) >> make_sysCode_range
+sysCode_listElemTag = unString >> make_sysCode
+sysCode_listElem = ( sysCode_number | sysCode_listElemTag ) >> listElem
+sysCode_innerList = oneplus( ( sysCode_range | sysCode_listElem ) + skip( maybe( comma ) ) ) >> flatten
+sysCode_expanded = skip( sysCode_start ) + sysCode_innerList + skip( code_end )
+sysCode_elem = sysCode >> listElem
+
+ # HID Codes
+hidCode_elem = usbCode_expanded | usbCode_elem | sysCode_expanded | sysCode_elem | consCode_expanded | consCode_elem
+
# Capabilities
capFunc_arguments = many( number + skip( maybe( comma ) ) ) >> listToTuple
capFunc_elem = name + skip( parenthesis('(') ) + capFunc_arguments + skip( parenthesis(')') ) >> capArgExpander >> listElem
-capFunc_combo = oneplus( ( usbCode_expanded | usbCode_elem | capFunc_elem ) + skip( maybe( plus ) ) ) >> listElem
+capFunc_combo = oneplus( ( hidCode_elem | capFunc_elem ) + skip( maybe( plus ) ) ) >> listElem
capFunc_sequence = oneplus( ( capFunc_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten
# Trigger / Result Codes
triggerCode_outerList = scanCode_sequence >> optionExpansion
-triggerUSBCode_outerList = usbCode_sequence >> optionExpansion >> usbCodeToCapability
-resultCode_outerList = capFunc_sequence >> optionExpansion >> usbCodeToCapability
+triggerUSBCode_outerList = usbCode_sequence >> optionExpansion >> hidCodeToCapability
+resultCode_outerList = ( ( capFunc_sequence >> optionExpansion ) | none ) >> hidCodeToCapability
## Main Rules
### Main Entry Point ###
if __name__ == '__main__':
- (baseFiles, defaultFiles, partialFileSets, backend_name, template, defines_template, output, defines_output) = processCommandLineArgs()
+ (baseFiles, defaultFiles, partialFileSets, backend_name, templates, outputs) = processCommandLineArgs()
# Look up git information on the compiler
gitRev, gitChanges = gitRevision( os.path.dirname( os.path.realpath( __file__ ) ) )
# Load backend module
global backend
backend_import = importlib.import_module( "backends.{0}".format( backend_name ) )
- backend = backend_import.Backend( template, defines_template )
+ backend = backend_import.Backend( templates )
# Process base layout files
for filename in baseFiles:
)
# Generate output file using template and backend
- backend.generate( output, defines_output )
+ backend.generate( outputs )
# Successful Execution
sys.exit( 0 )