# HG changeset patch # User Jacob Alexander # Date 1453366290 28800 # Node ID a5107b668c4b6ccadc85ecf926f64ae5368bdfb2 # Parent 1f35f83485e1ca479e698b0462c2c77aa44d7cf9 First pass at parsing rules for KLL 0.5 diff -r 1f35f83485e1 -r a5107b668c4b examples/leds.kll --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/leds.kll Thu Jan 21 00:51:30 2016 -0800 @@ -0,0 +1,50 @@ +Name = leds; +Author = "HaaTa (Jacob Alexander) 2016"; +KLL = 0.5; +mydefine = "stuffs here"; +mydefine2 = '"stuffs here"'; # For outputting c define strings +mynumber = 414; + +# Array variables +myarray[] = elem0 elem1 "elem 2"; +myarray[4] = test; + +# Key Positioning +S120 <= x:20, rx:15; +S121 <= x:20, y:10, z:2, rx:15, ry:12, rz:39; + +# Pixel Positioning +P[20] <= x:20, rx:15; +P[21] <= x:20, y:10, z:2, rx:15, ry:12, rz:39; + +# Pixel Channel Mapping +P[5](4:8, 5:8, 12:8) : None; +P[4](3:8) : S0x31; +P[12](40:8, 50:8, 120:8) : S59; + +# Animation +A[BLEEdsing] <= loop:3,frame:2; +A[BLEEdsing2] <= loop,div:2; + +# Animation Frames +A[BLEEdsing, 0] <= P[4](+32); +A[BLEEdsing, 1] <= P[4](42); +A[BLEEdsing, 2] <= P[4](-12); +A[BLEEdsing, 3] <= P[4](-:32); +A[BLEEdsing, 4] <= P[4](+:400); +A[BLEEdsing, 5] <= P[4](<<2); +A[BLEEdsing, 6] <= P[4](>>1); + +A[BLEEdsing2, 0] <= PL[0](127, 30, 40), P[5](20, 30, 40); +A[BLEEdsing2, 1] <= P[1-20,40](40,50,0x60); + +# Animation Triggers +myCapability => myFunc( myArg1 : 1, myArg2 : 4 ); +A[BLEEdsing, 3] : myCapability( 0x8, 0x25 ); +A[BLEEdsing2] : myCapability( 0x8, 0x25 ); + +# Animation Results +U0x40 : A[BLEEdsing]; +S0x41 : A[BLEEdsing](loop:2); +S0x43 : PL[0](0xFF,0xFF,244), P[1-3](20,40,60); + diff -r 1f35f83485e1 -r a5107b668c4b kll.py --- a/kll.py Sat Oct 17 15:00:17 2015 -0700 +++ b/kll.py Thu Jan 21 00:51:30 2016 -0800 @@ -2,7 +2,7 @@ # KLL Compiler # Keyboard Layout Langauge # -# Copyright (C) 2014-2015 by Jacob Alexander +# Copyright (C) 2014-2016 by Jacob Alexander # # This file is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -84,7 +84,7 @@ pArgs.add_argument( '-b', '--backend', type=str, default="kiibohd", help="Specify target backend for the KLL compiler.\n" "Default: kiibohd\n" - "Options: kiibohd, json" ) + "Options: kiibohd" ) 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', @@ -142,14 +142,23 @@ ( '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\[', ) ), + ( 'Indicator', ( r'I(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ), + ( 'IndicatorStart', ( r'I\[', ) ), + ( 'Pixel', ( r'P"[^"]+"', ) ), + ( 'PixelStart', ( r'P\[', ) ), + ( 'PixelLayer', ( r'PL"[^"]+"', ) ), + ( 'PixelLayerStart', ( r'PL\[', ) ), + ( 'Animation', ( r'A"[^"]+"', ) ), + ( 'AnimationStart', ( r'A\[', ) ), + ( 'CodeBegin', ( r'\[', ) ), ( 'CodeEnd', ( r'\]', ) ), + ( 'Position', ( r'r?[xyz]:[0-9]+(.[0-9]+)?', ) ), ( 'String', ( r'"[^"]*"', ) ), ( 'SequenceString', ( r"'[^']*'", ) ), - ( 'Operator', ( r'=>|:\+|:-|::|:|=', ) ), + ( 'PixelOperator', ( r'(\+:|-:|>>|<<)', ) ), + ( 'Operator', ( r'=>|<=|:\+|:-|::|:|=', ) ), ( 'Comma', ( r',', ) ), ( 'Dash', ( r'-', ) ), ( 'Plus', ( r'\+', ) ), @@ -179,196 +188,258 @@ ## Parsing Functions -def make_scanCode( token ): - scanCode = int( token[1:], 0 ) - # Check size, to make sure it's valid - # 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 +class Make: + def scanCode( token ): + scanCode = int( token[1:], 0 ) + # Check size, to make sure it's valid + # 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_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:] + def 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: - hidCode = kll_hid_lookup_dictionary[ type ][ token[1:-1].upper() ][1] - except LookupError as err: - print ( "{0} {1} is an invalid USB HID Code Lookup...".format( ERROR, err ) ) - raise - else: - # 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 + # If using string representation of USB Code, do lookup, case-insensitive + if '"' in token: + try: + hidCode = kll_hid_lookup_dictionary[ type ][ token[1:-1].upper() ][1] + except LookupError as err: + print ( "{0} {1} is an invalid USB HID Code Lookup...".format( ERROR, err ) ) + raise else: - hidCode = 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 - # 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 + + def usbCode( token ): + return Make.hidCode( 'USBCode', token ) + + def consCode( token ): + return Make.hidCode( 'ConsCode', token ) + + def sysCode( token ): + return Make.hidCode( 'SysCode', token ) - # 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 ) + def animation( token ): + # TODO + print( token ) + return "NULL" + + def animationCapability( token ): + # TODO + print( token ) + return "DIS" - print ( "{0} Unknown HID Specifier '{1}'".format( ERROR, type ) ) - raise - -def make_usbCode( token ): - return make_hidCode( 'USBCode', token ) + def pixelCapability( token ): + # TODO + print( token ) + return "DAT" -def make_consCode( token ): - return make_hidCode( 'ConsCode', token ) + def pixel( token ): + # TODO + print( token ) + return "PNULL" -def make_sysCode( token ): - return make_hidCode( 'SysCode', token ) + def pixelLayer( token ): + # TODO + print( token ) + return "PLNULL" -def make_hidCode_number( type, token ): - lookup = { - 'ConsCode' : 'CONS', - 'SysCode' : 'SYS', - 'USBCode' : 'USB', - } - return ( lookup[ type ], token ) + def pixelchans( token ): + # Create dictionary list + channel_widths = [] + for elem in token: + channel_widths.append( { + 'chan' : elem[0], + 'width' : elem[1], + } ) + print(channel_widths) + return channel_widths + + def pixelchan_elem( token ): + channel_config = { + 'pixels' : token[0], + 'chans' : token[1], + } + return channel_config -def make_usbCode_number( token ): - return make_hidCode_number( 'USBCode', token ) + def pixelmods( token ): + # TODO + print( token ) + return "PMOD" -def make_consCode_number( token ): - return make_hidCode_number( 'ConsCode', token ) + def pixellayer( token ): + # TODO + print( token ) + return "PL" -def make_sysCode_number( token ): - return make_hidCode_number( 'SysCode', token ) + def position( token ): + return token.split(':') - # Replace key-word with None specifier (which indicates a noneOut capability) -def make_none( token ): - return [[[('NONE', 0)]]] + def hidCode_number( type, token ): + lookup = { + 'ConsCode' : 'CONS', + 'SysCode' : 'SYS', + 'USBCode' : 'USB', + } + return ( lookup[ type ], token ) -def make_seqString( token ): - # Shifted Characters, and amount to move by to get non-shifted version - # US ANSI - shiftCharacters = ( - ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ), - ( "+", 0x12 ), - ( "&(", 0x11 ), - ( "!#$%", 0x10 ), - ( "*", 0x0E ), - ( ")", 0x07 ), - ( '"', 0x05 ), - ( ":", 0x01 ), - ( "@", -0x0E ), - ( "<>?", -0x10 ), - ( "~", -0x1E ), - ( "{}|", -0x20 ), - ( "^", -0x28 ), - ( "_", -0x32 ), - ) + def usbCode_number( token ): + return Make.hidCode_number( 'USBCode', token ) + + def consCode_number( token ): + return Make.hidCode_number( 'ConsCode', token ) - listOfLists = [] - shiftKey = kll_hid_lookup_dictionary['USBCode']["SHIFT"] + def sysCode_number( token ): + return Make.hidCode_number( 'SysCode', token ) + + # Replace key-word with None specifier (which indicates a noneOut capability) + def none( token ): + return [[[('NONE', 0)]]] - # Creates a list of USB codes from the string: sequence (list) of combos (lists) - for char in token[1:-1]: - processedChar = char - - # Whether or not to create a combo for this sequence with a shift - shiftCombo = False + def seqString( token ): + # Shifted Characters, and amount to move by to get non-shifted version + # US ANSI + shiftCharacters = ( + ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ), + ( "+", 0x12 ), + ( "&(", 0x11 ), + ( "!#$%", 0x10 ), + ( "*", 0x0E ), + ( ")", 0x07 ), + ( '"', 0x05 ), + ( ":", 0x01 ), + ( "@", -0x0E ), + ( "<>?", -0x10 ), + ( "~", -0x1E ), + ( "{}|", -0x20 ), + ( "^", -0x28 ), + ( "_", -0x32 ), + ) - # Depending on the ASCII character, convert to single character or Shift + character - for pair in shiftCharacters: - if char in pair[0]: - shiftCombo = True - processedChar = chr( ord( char ) + pair[1] ) - break + listOfLists = [] + shiftKey = kll_hid_lookup_dictionary['USBCode']["SHIFT"] - # 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['USBCode'][ processedChar.upper() ] + # Creates a list of USB codes from the string: sequence (list) of combos (lists) + for char in token[1:-1]: + processedChar = char + + # Whether or not to create a combo for this sequence with a shift + shiftCombo = False - # Create Combo for this character, add shift key if shifted - charCombo = [] - if shiftCombo: - charCombo = [ [ shiftKey ] ] - charCombo.append( [ usbCode ] ) + # Depending on the ASCII character, convert to single character or Shift + character + for pair in shiftCharacters: + if char in pair[0]: + shiftCombo = True + processedChar = chr( ord( char ) + pair[1] ) + break - # Add to list of lists - listOfLists.append( charCombo ) + # 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['USBCode'][ processedChar.upper() ] - return listOfLists + # Create Combo for this character, add shift key if shifted + charCombo = [] + if shiftCombo: + charCombo = [ [ shiftKey ] ] + charCombo.append( [ usbCode ] ) + + # Add to list of lists + listOfLists.append( charCombo ) -def make_string( token ): - return token[1:-1] - -def make_unseqString( token ): - return token[1:-1] + return listOfLists -def make_number( token ): - return int( token, 0 ) - - # Range can go from high to low or low to high -def make_scanCode_range( rangeVals ): - start = rangeVals[0] - end = rangeVals[1] + def string( token ): + return token[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 ) ) + def unseqString( token ): + return token[1:-1] - # Range can go from high to low or low to high - # 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_hidCode_range( type, rangeVals ): - # Check if already integers - if isinstance( rangeVals[0], int ): + def number( token ): + return int( token, 0 ) + + # Range can go from high to low or low to high + def scanCode_range( rangeVals ): start = rangeVals[0] - else: - start = make_hidCode( type, rangeVals[0] )[1] + end = rangeVals[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 ) ) - if isinstance( rangeVals[1], int ): - end = rangeVals[1] - else: - end = make_hidCode( type, rangeVals[1] )[1] + # Range can go from high to low or low to high + # 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 hidCode_range( type, rangeVals ): + # Check if already integers + if isinstance( rangeVals[0], int ): + start = rangeVals[0] + else: + start = Make.hidCode( type, rangeVals[0] )[1] - # Swap start, end if start is greater than end - if start > end: - start, end = end, start + if isinstance( rangeVals[1], int ): + end = rangeVals[1] + else: + end = Make.hidCode( type, rangeVals[1] )[1] - # Iterate from start to end, and generate the range - listRange = list( range( start, end + 1 ) ) + # Swap start, end if start is greater than end + if start > end: + start, end = end, start - # 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 + # Iterate from start to end, and generate the range + 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 usbCode_range( rangeVals ): + return Make.hidCode_range( 'USBCode', rangeVals ) + + def sysCode_range( rangeVals ): + return Make.hidCode_range( 'SysCode', rangeVals ) -def make_sysCode_range( rangeVals ): - return make_hidCode_range( 'SysCode', rangeVals ) + def consCode_range( rangeVals ): + return Make.hidCode_range( 'ConsCode', rangeVals ) -def make_consCode_range( rangeVals ): - return make_hidCode_range( 'ConsCode', rangeVals ) + def range( rangeVals ): + # TODO + print (rangeVals) + return "" ## Base Rules @@ -381,6 +452,7 @@ tokenType = lambda t: some( lambda x: x.type == t ) >> tokenValue operator = lambda s: a( Token( 'Operator', s ) ) >> tokenValue parenthesis = lambda s: a( Token( 'Parenthesis', s ) ) >> tokenValue +bracket = lambda s: a( Token( 'Bracket', s ) ) >> tokenValue eol = a( Token( 'EndOfLine', ';' ) ) def listElem( item ): @@ -491,110 +563,165 @@ ## Evaluation Rules -def eval_scanCode( triggers, operator, results ): - # Convert to lists of lists of lists to tuples of tuples of tuples - # Tuples are non-mutable, and can be used has index items - 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 ) +class Eval: + def scanCode( triggers, operator, results ): + # Convert to lists of lists of lists to tuples of tuples of tuples + # Tuples are non-mutable, and can be used has index items + 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'] ) + # 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 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 ) - # Iterate over all combinations of triggers and results - for sequence in triggers: - # Convert tuple of tuples to list of lists so each element can be modified - trigger = listit( sequence ) + for result in results: + # Append Case + if operator == ":+": + macros_map.appendScanCode( trigger, result ) - # 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 ) ) + # Remove Case + elif operator == ":-": + macros_map.removeScanCode( trigger, result ) + + # Replace Case + # Soft Replace Case is the same for Scan Codes + elif operator == ":" or operator == "::": + macros_map.replaceScanCode( trigger, result ) - # Convert back to a tuple of tuples - trigger = tupleit( trigger ) + def usbCode( triggers, operator, results ): + # Convert to lists of lists of lists to tuples of tuples of tuples + # Tuples are non-mutable, and can be used has index items + 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 ) - for result in results: - # Append Case - if operator == ":+": - macros_map.appendScanCode( trigger, result ) + # Iterate over all combinations of triggers and results + for trigger in triggers: + scanCodes = macros_map.lookupUSBCodes( trigger ) + for scanCode in scanCodes: + for result in results: + # Soft Replace needs additional checking to see if replacement is necessary + if operator == "::" and not macros_map.softReplaceCheck( scanCode ): + continue - # Remove Case - elif operator == ":-": - macros_map.removeScanCode( trigger, result ) + # Cache assignment until file finishes processing + macros_map.cacheAssignment( operator, scanCode, result ) - # Replace Case - # Soft Replace Case is the same for Scan Codes - elif operator == ":" or operator == "::": - macros_map.replaceScanCode( trigger, result ) + def variable( name, content ): + # Content might be a concatenation of multiple data types, convert everything into a single string + assigned_content = "" + for item in content: + assigned_content += str( item ) + + variables_dict.assignVariable( name, assigned_content ) + + def capability( name, function, args ): + capabilities_dict[ name ] = [ function, args ] + + def define( name, cdefine_name ): + variables_dict.defines[ name ] = cdefine_name -def eval_usbCode( triggers, operator, results ): - # Convert to lists of lists of lists to tuples of tuples of tuples - # Tuples are non-mutable, and can be used has index items - 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 ) + def scanCodePosition( scanCode, positions ): + print (scanCode) + # Re-organize position lists into a dictionary first + pos_dict = dict() + for pos in positions: + pos_dict[ pos[0] ] = pos[1] + print (pos_dict) - # Iterate over all combinations of triggers and results - for trigger in triggers: - scanCodes = macros_map.lookupUSBCodes( trigger ) - for scanCode in scanCodes: - for result in results: - # Soft Replace needs additional checking to see if replacement is necessary - if operator == "::" and not macros_map.softReplaceCheck( scanCode ): - continue + # TODO Create datastructure for positions + + def pixelPosition( pixel, position ): + # TODO + print (pixel, position) + + def pixelChannels( channel, scanCode ): + # TODO + print (channel, scanCode ) - # Cache assignment until file finishes processing - macros_map.cacheAssignment( operator, scanCode, result ) + def animation( animation, modifiers ): + # TODO + print( animation, modifiers ) + + def animationFrame( animation, frame, modifiers ): + # TODO + print( frame, modifiers ) -def eval_variable( name, content ): - # Content might be a concatenation of multiple data types, convert everything into a single string - assigned_content = "" - for item in content: - assigned_content += str( item ) + def animationTrigger( animation, operator, results ): + # TODO + print ( animation, operator, results ) - variables_dict.assignVariable( name, assigned_content ) + def animationTriggerFrame( animation, frame, operator, results ): + # TODO + print ( animation, frame, operator, results ) -def eval_capability( name, function, args ): - capabilities_dict[ name ] = [ function, args ] + def array( name, index, content ): + # TODO + print (name, index, content) -def eval_define( name, cdefine_name ): - variables_dict.defines[ name ] = cdefine_name +class Map: + scanCode = unarg( Eval.scanCode ) + usbCode = unarg( Eval.usbCode ) + animationTrigger = unarg( Eval.animationTrigger ) + animationTriggerFrame = unarg( Eval.animationTriggerFrame ) -map_scanCode = unarg( eval_scanCode ) -map_usbCode = unarg( eval_usbCode ) - -set_variable = unarg( eval_variable ) -set_capability = unarg( eval_capability ) -set_define = unarg( eval_define ) +class Set: + animation = unarg( Eval.animation ) + animationFrame = unarg( Eval.animationFrame ) + array = unarg( Eval.array ) + capability = unarg( Eval.capability ) + define = unarg( Eval.define ) + pixelChannels = unarg( Eval.pixelChannels ) + pixelPosition = unarg( Eval.pixelPosition ) + scanCodePosition = unarg( Eval.scanCodePosition ) + variable = unarg( Eval.variable ) ## Sub Rules -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') -dash = tokenType('Dash') -plus = tokenType('Plus') -content = tokenType('VariableContents') -string = tokenType('String') >> make_string -unString = tokenType('String') # When the double quotes are still needed for internal processing -seqString = tokenType('SequenceString') >> make_seqString -unseqString = tokenType('SequenceString') >> make_unseqString # For use with variables +usbCode = tokenType('USBCode') >> Make.usbCode +scanCode = tokenType('ScanCode') >> Make.scanCode +consCode = tokenType('ConsCode') >> Make.consCode +sysCode = tokenType('SysCode') >> Make.sysCode +animation = tokenType('Animation') >> Make.animation +pixel = tokenType('Pixel') >> Make.pixel +pixelLayer = tokenType('PixelLayer') >> Make.pixelLayer +none = tokenType('None') >> Make.none +position = tokenType('Position') >> Make.position +name = tokenType('Name') +number = tokenType('Number') >> Make.number +comma = tokenType('Comma') +dash = tokenType('Dash') +plus = tokenType('Plus') +content = tokenType('VariableContents') +string = tokenType('String') >> Make.string +unString = tokenType('String') # When the double quotes are still needed for internal processing +seqString = tokenType('SequenceString') >> Make.seqString +unseqString = tokenType('SequenceString') >> Make.unseqString # For use with variables +pixelOperator = tokenType('PixelOperator') # Code variants -code_end = tokenType('CodeEnd') +code_begin = tokenType('CodeBegin') +code_end = tokenType('CodeEnd') # Scan Codes scanCode_start = tokenType('ScanCodeStart') -scanCode_range = number + skip( dash ) + number >> make_scanCode_range +scanCode_range = number + skip( dash ) + number >> Make.scanCode_range scanCode_listElem = number >> listElem scanCode_innerList = oneplus( ( scanCode_range | scanCode_listElem ) + skip( maybe( comma ) ) ) >> flatten scanCode_expanded = skip( scanCode_start ) + scanCode_innerList + skip( code_end ) @@ -604,9 +731,9 @@ # USB Codes usbCode_start = tokenType('USBCodeStart') -usbCode_number = number >> make_usbCode_number -usbCode_range = ( usbCode_number | unString ) + skip( dash ) + ( number | unString ) >> make_usbCode_range -usbCode_listElemTag = unString >> make_usbCode +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 = ( 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 ) @@ -616,9 +743,9 @@ # 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_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 ) @@ -626,9 +753,9 @@ # 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_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 ) @@ -637,10 +764,52 @@ # HID Codes hidCode_elem = usbCode_expanded | usbCode_elem | sysCode_expanded | sysCode_elem | consCode_expanded | consCode_elem + # Pixels +pixel_start = tokenType('PixelStart') +pixel_number = number +pixel_range = ( pixel_number ) + skip( dash ) + ( number ) >> Make.range +pixel_listElem = pixel_number >> listElem +pixel_innerList = many( ( pixel_range | pixel_listElem ) + skip( maybe( comma ) ) ) +pixel_expanded = skip( pixel_start ) + pixel_innerList + skip( code_end ) +pixel_elem = pixel >> listElem + + # Pixel Layer +pixellayer_start = tokenType('PixelLayerStart') +pixellayer_number = number +pixellayer_expanded = skip( pixellayer_start ) + pixellayer_number + skip( code_end ) +pixellayer_elem = pixelLayer >> listElem + + # Pixel Channels +pixelchan_chans = many( number + skip( operator(':') ) + number + skip( maybe( comma ) ) ) >> Make.pixelchans +pixelchan_elem = ( pixel_expanded | pixel_elem ) + skip( parenthesis('(') ) + pixelchan_chans + skip( parenthesis(')') ) >> Make.pixelchan_elem + + # Pixel Mods +pixelmod_mods = many( maybe( pixelOperator | plus | dash ) + number + skip( maybe( comma ) ) ) >> Make.pixelmods +pixelmod_layer = ( pixellayer_expanded | pixellayer_elem ) >> Make.pixellayer +pixelmod_elem = ( pixel_expanded | pixel_elem | pixelmod_layer ) + skip( parenthesis('(') ) + pixelmod_mods + skip( parenthesis(')') ) + + # Pixel Capability +pixel_capability = pixelmod_elem >> Make.pixelCapability + + # Animations +animation_start = tokenType('AnimationStart') +animation_name = name +animation_frame_range = ( number ) + skip( dash ) + ( number ) >> Make.range +animation_name_frame = many( ( number | animation_frame_range ) + skip( maybe( comma ) ) ) +animation_def = skip( animation_start ) + animation_name + skip( code_end ) +animation_expanded = skip( animation_start ) + animation_name + skip( comma ) + animation_name_frame + skip( code_end ) +animation_elem = animation >> listElem + + # Animation Modifier +animation_modifier = many( ( name | number ) + maybe( skip( operator(':') ) + number ) + skip( maybe( comma ) ) ) + + # Animation Capability +animation_capability = ( animation_def | animation_elem ) + maybe( skip( parenthesis('(') + animation_modifier + skip( parenthesis(')') ) ) ) >> Make.animationCapability + # Capabilities capFunc_arguments = many( number + skip( maybe( comma ) ) ) >> listToTuple capFunc_elem = name + skip( parenthesis('(') ) + capFunc_arguments + skip( parenthesis(')') ) >> capArgExpander >> listElem -capFunc_combo = oneplus( ( hidCode_elem | capFunc_elem ) + skip( maybe( plus ) ) ) >> listElem +capFunc_combo = oneplus( ( hidCode_elem | capFunc_elem | animation_capability | pixel_capability ) + skip( maybe( plus ) ) ) >> listElem capFunc_sequence = oneplus( ( capFunc_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten # Trigger / Result Codes @@ -648,30 +817,76 @@ triggerUSBCode_outerList = usbCode_sequence >> optionExpansion >> hidCodeToCapability resultCode_outerList = ( ( capFunc_sequence >> optionExpansion ) | none ) >> hidCodeToCapability + # Positions +position_list = oneplus( position + skip( maybe( comma ) ) ) + ## Main Rules +#| Assignment #| = ; variable_contents = name | content | string | number | comma | dash | unseqString -variable_expression = name + skip( operator('=') ) + oneplus( variable_contents ) + skip( eol ) >> set_variable +variable_expression = name + skip( operator('=') ) + oneplus( variable_contents ) + skip( eol ) >> Set.variable +#| Array Assignment +#| [] = ; +#| [] = ; +array_expression = name + skip( code_begin ) + maybe( number ) + skip( code_end ) + skip( operator('=') ) + oneplus( variable_contents ) + skip( eol ) >> Set.array + +#| Name Association #| => ; capability_arguments = name + skip( operator(':') ) + number + skip( maybe( comma ) ) -capability_expression = name + skip( operator('=>') ) + name + skip( parenthesis('(') ) + many( capability_arguments ) + skip( parenthesis(')') ) + skip( eol ) >> set_capability +capability_expression = name + skip( operator('=>') ) + name + skip( parenthesis('(') ) + many( capability_arguments ) + skip( parenthesis(')') ) + skip( eol ) >> Set.capability + +#| Name Association +#| => ; +define_expression = name + skip( operator('=>') ) + name + skip( eol ) >> Set.define -#| => ; -define_expression = name + skip( operator('=>') ) + name + skip( eol ) >> set_define +#| Data Association +#| <= ; +#| <= ; +animation_expression = ( animation_elem | animation_def ) + skip( operator('<=') ) + animation_modifier + skip( eol ) >> Set.animation +animationFrame_expression = animation_expanded + skip( operator('<=') ) + many( pixelmod_elem + skip( maybe( comma ) ) ) + skip( eol ) >> Set.animationFrame + +#| Data Association +#| <= ; +pixelPosition_expression = ( pixel_expanded | pixel_elem ) + skip( operator('<=') ) + position_list + skip( eol ) >> Set.pixelPosition +#| Data Association +#| <= ; +pixelChan_expression = pixelchan_elem + skip( operator(':') ) + ( scanCode_expanded | scanCode_elem | none ) + skip( eol ) >> Set.pixelChannels + +#| Data Association +#| <= ; +scanCodePosition_expression = triggerCode_outerList + skip( operator('<=') ) + position_list + skip( eol ) >> Set.scanCodePosition + +#| Mapping #| : ; -operatorTriggerResult = operator(':') | operator(':+') | operator(':-') | operator('::') -scanCode_expression = triggerCode_outerList + operatorTriggerResult + resultCode_outerList + skip( eol ) >> map_scanCode -usbCode_expression = triggerUSBCode_outerList + operatorTriggerResult + resultCode_outerList + skip( eol ) >> map_usbCode +operatorTriggerResult = operator(':') | operator(':+') | operator(':-') | operator('::') +scanCode_expression = triggerCode_outerList + operatorTriggerResult + resultCode_outerList + skip( eol ) >> Map.scanCode +usbCode_expression = triggerUSBCode_outerList + operatorTriggerResult + resultCode_outerList + skip( eol ) >> Map.usbCode +animation_trigger = ( animation_elem | animation_def ) + operatorTriggerResult + resultCode_outerList + skip( eol ) >> Map.animationTrigger +animation_triggerFrame = animation_expanded + operatorTriggerResult + resultCode_outerList + skip( eol ) >> Map.animationTriggerFrame def parse( tokenSequence ): """Sequence(Token) -> object""" # Top-level Parser - expression = scanCode_expression | usbCode_expression | variable_expression | capability_expression | define_expression + expression = ( + scanCode_expression | + scanCodePosition_expression | + usbCode_expression | + animation_trigger | + animation_triggerFrame | + pixelPosition_expression | + pixelChan_expression | + animation_expression | + animationFrame_expression | + array_expression | + variable_expression | + capability_expression | + define_expression + ) kll_text = many( expression ) kll_file = maybe( kll_text ) + skip( finished )