KLL Compiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

parse.py 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830
  1. #!/usr/bin/env python3
  2. '''
  3. KLL Parsing Expressions
  4. This file contains various parsing rules and processors used by funcparserlib for KLL
  5. REMEMBER: When editing parser BNF-like expressions, order matters. Specifically lexer tokens and parser |
  6. '''
  7. # Parser doesn't play nice with linters, disable some checks
  8. # pylint: disable=no-self-argument, too-many-public-methods, no-self-use, bad-builtin
  9. # Copyright (C) 2016 by Jacob Alexander
  10. #
  11. # This file is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License as published by
  13. # the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # This file is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public License
  22. # along with this file. If not, see <http://www.gnu.org/licenses/>.
  23. ### Imports ###
  24. from common.hid_dict import kll_hid_lookup_dictionary
  25. from common.id import (
  26. AnimationId, AnimationFrameId,
  27. CapArgId, CapId,
  28. HIDId,
  29. NoneId,
  30. PixelId, PixelLayerId,
  31. ScanCodeId
  32. )
  33. from common.modifier import AnimationModifierList
  34. from common.schedule import AnalogScheduleParam, ScheduleParam, Time
  35. from funcparserlib.lexer import Token
  36. from funcparserlib.parser import (some, a, many, oneplus, skip, maybe)
  37. ### Decorators ###
  38. ## Print Decorator Variables
  39. ERROR = '\033[5;1;31mERROR\033[0m:'
  40. WARNING = '\033[5;1;33mWARNING\033[0m:'
  41. ### Classes ###
  42. ## Parsing Functions
  43. class Make:
  44. '''
  45. Collection of parse string interpreters
  46. '''
  47. def scanCode( token ):
  48. '''
  49. Converts a raw scan code string into an ScanCodeId /w integer
  50. S0x10 -> 16
  51. '''
  52. if isinstance( token, int ):
  53. return ScanCodeId( token )
  54. else:
  55. return ScanCodeId( int( token[1:], 0 ) )
  56. def hidCode( type, token ):
  57. '''
  58. Convert a given raw hid token string to an integer /w a type
  59. U"Enter" -> USB, Enter(0x28)
  60. '''
  61. # If already converted to a HIDId, just return
  62. if isinstance( token, HIDId ):
  63. return token
  64. # If first character is a U or I, strip
  65. if token[0] == "U" or token[0] == "I":
  66. token = token[1:]
  67. # CONS specifier
  68. elif 'CONS' in token:
  69. token = token[4:]
  70. # SYS specifier
  71. elif 'SYS' in token:
  72. token = token[3:]
  73. # If using string representation of USB Code, do lookup, case-insensitive
  74. if '"' in token:
  75. try:
  76. hidCode = kll_hid_lookup_dictionary[ type ][ token[1:-1].upper() ][1]
  77. except LookupError as err:
  78. print ( "{0} {1} is an invalid USB HID Code Lookup...".format( ERROR, err ) )
  79. raise
  80. else:
  81. # Already tokenized
  82. if (
  83. type == 'USBCode' and token[0] == 'USB'
  84. or
  85. type == 'SysCode' and token[0] == 'SYS'
  86. or
  87. type == 'ConsCode' and token[0] == 'CONS'
  88. or
  89. type == 'IndCode' and token[0] == 'IND'
  90. ):
  91. hidCode = token[1]
  92. # Convert
  93. else:
  94. hidCode = int( token, 0 )
  95. return HIDId( type, hidCode )
  96. def usbCode( token ):
  97. '''
  98. Convert a given raw USB Keyboard hid token string to an integer /w a type
  99. U"Enter" -> USB, Enter(0x28)
  100. '''
  101. return Make.hidCode( 'USBCode', token )
  102. def consCode( token ):
  103. '''
  104. Convert a given raw Consumer Control hid token string to an integer /w a type
  105. '''
  106. return Make.hidCode( 'ConsCode', token )
  107. def sysCode( token ):
  108. '''
  109. Convert a given raw System Control hid token string to an integer /w a type
  110. '''
  111. return Make.hidCode( 'SysCode', token )
  112. def indCode( token ):
  113. '''
  114. Convert a given raw Indicator hid token string to an integer /w a type
  115. '''
  116. return Make.hidCode( 'IndCode', token )
  117. def animation( name ):
  118. '''
  119. Converts a raw animation value into an AnimationId /w name
  120. A"myname" -> myname
  121. '''
  122. if name[0] == "A":
  123. return AnimationId( name[2:-1] )
  124. else:
  125. return AnimationId( name )
  126. def animationTrigger( animation, frame_indices ):
  127. '''
  128. Generate either an AnimationId or an AnimationFrameId
  129. frame_indices indicate that this is an AnimationFrameId
  130. '''
  131. trigger_list = []
  132. # AnimationFrameId
  133. if len( frame_indices ) > 0:
  134. for index in frame_indices:
  135. trigger_list.append( [ [ AnimationFrameId( animation, index ) ] ] )
  136. # AnimationId
  137. else:
  138. trigger_list.append( [ [ AnimationId( animation ) ] ] )
  139. return trigger_list
  140. def animationCapability( animation, modifiers ):
  141. '''
  142. Apply modifiers to AnimationId
  143. '''
  144. if modifiers is not None:
  145. animation.setModifiers( modifiers )
  146. return [ animation ]
  147. def animationModlist( modifiers ):
  148. '''
  149. Build an AnimationModifierList
  150. Only used for animation data association
  151. '''
  152. modlist = AnimationModifierList()
  153. modlist.setModifiers( modifiers )
  154. return modlist
  155. def pixelCapability( pixels, modifiers ):
  156. '''
  157. Apply modifiers to list of pixels/pixellists
  158. Results in a combination of pixel capabilities
  159. '''
  160. pixelcap_list = []
  161. for pixel in pixels:
  162. pixel.setModifiers( modifiers )
  163. pixelcap_list.append( pixel )
  164. return pixelcap_list
  165. def pixel( token ):
  166. '''
  167. Converts a raw pixel value into a PixelId /w integer
  168. P0x3 -> 3
  169. '''
  170. if isinstance( token, int ):
  171. return PixelId( token )
  172. else:
  173. return PixelId( int( token[1:], 0 ) )
  174. def pixel_list( pixel_list ):
  175. '''
  176. Converts a list a numbers into a list of PixelIds
  177. '''
  178. pixels = []
  179. for pixel in pixel_list:
  180. pixels.append( PixelId( pixel ) )
  181. return pixels
  182. def pixelLayer( token ):
  183. '''
  184. Converts a raw pixel layer value into a PixelLayerId /w integer
  185. PL0x3 -> 3
  186. '''
  187. if isinstance( token, int ):
  188. return PixelLayerId( token )
  189. else:
  190. return PixelLayerId( int( token[2:], 0 ) )
  191. def pixelLayer_list( layer_list ):
  192. '''
  193. Converts a list a numbers into a list of PixelLayerIds
  194. '''
  195. layers = []
  196. for layer in layer_list:
  197. layers.append( PixelLayerId( layer ) )
  198. return layers
  199. def pixelchan( pixel_list, chans ):
  200. '''
  201. Apply channels to PixelId
  202. Only one pixel at a time can be mapped, hence pixel_list[0]
  203. '''
  204. pixel = pixel_list[0]
  205. pixel.setChannels( chans )
  206. return pixel
  207. def pixelmod( pixels, modifiers ):
  208. '''
  209. Apply modifiers to list of pixels/pixellists
  210. Results in a combination of pixel capabilities
  211. '''
  212. pixelcap_list = []
  213. for pixel in pixels:
  214. pixel.setModifiers( modifiers )
  215. pixelcap_list.append( pixel )
  216. return pixelcap_list
  217. def position( token ):
  218. '''
  219. Physical position split
  220. x:20 -> (x, 20)
  221. '''
  222. return token.split(':')
  223. def usbCode_number( token ):
  224. '''
  225. USB Keyboard HID Code lookup
  226. '''
  227. return HIDId( 'USBCode', token )
  228. def consCode_number( token ):
  229. '''
  230. Consumer Control HID Code lookup
  231. '''
  232. return HIDId( 'ConsCode', token )
  233. def sysCode_number( token ):
  234. '''
  235. System Control HID Code lookup
  236. '''
  237. return HIDId( 'SysCode', token )
  238. def indCode_number( token ):
  239. '''
  240. Indicator HID Code lookup
  241. '''
  242. return HIDId( 'IndCode', token )
  243. def none( token ):
  244. '''
  245. Replace key-word with NoneId specifier (which indicates a noneOut capability)
  246. '''
  247. return [[[NoneId()]]]
  248. def seqString( token ):
  249. '''
  250. Converts sequence string to a sequence of combinations
  251. 'Ab' -> U"Shift" + U"A", U"B"
  252. '''
  253. # TODO - Add locale support
  254. # Shifted Characters, and amount to move by to get non-shifted version
  255. # US ANSI
  256. shiftCharacters = (
  257. ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ),
  258. ( "+", 0x12 ),
  259. ( "&(", 0x11 ),
  260. ( "!#$%", 0x10 ),
  261. ( "*", 0x0E ),
  262. ( ")", 0x07 ),
  263. ( '"', 0x05 ),
  264. ( ":", 0x01 ),
  265. ( "@", -0x0E ),
  266. ( "<>?", -0x10 ),
  267. ( "~", -0x1E ),
  268. ( "{}|", -0x20 ),
  269. ( "^", -0x28 ),
  270. ( "_", -0x32 ),
  271. )
  272. listOfLists = []
  273. shiftKey = kll_hid_lookup_dictionary['USBCode']["SHIFT"]
  274. # Creates a list of USB codes from the string: sequence (list) of combos (lists)
  275. for char in token[1:-1]:
  276. processedChar = char
  277. # Whether or not to create a combo for this sequence with a shift
  278. shiftCombo = False
  279. # Depending on the ASCII character, convert to single character or Shift + character
  280. for pair in shiftCharacters:
  281. if char in pair[0]:
  282. shiftCombo = True
  283. processedChar = chr( ord( char ) + pair[1] )
  284. break
  285. # Do KLL HID Lookup on non-shifted character
  286. # NOTE: Case-insensitive, which is why the shift must be pre-computed
  287. usb_code = kll_hid_lookup_dictionary['USBCode'][ processedChar.upper() ]
  288. # Create Combo for this character, add shift key if shifted
  289. charCombo = []
  290. if shiftCombo:
  291. charCombo = [ [ HIDId( 'USBCode', shiftKey[1] ) ] ]
  292. charCombo.append( [ HIDId( 'USBCode', usb_code[1] ) ] )
  293. # Add to list of lists
  294. listOfLists.append( charCombo )
  295. return listOfLists
  296. def string( token ):
  297. '''
  298. Converts a raw string to a Python string
  299. "this string" -> this string
  300. '''
  301. return token[1:-1]
  302. def unseqString( token ):
  303. '''
  304. Converts a raw sequence string to a Python string
  305. 'this string' -> this string
  306. '''
  307. return token[1:-1]
  308. def number( token ):
  309. '''
  310. Convert string number to Python integer
  311. '''
  312. return int( token, 0 )
  313. def timing( token ):
  314. '''
  315. Convert raw timing parameter to integer time and determine units
  316. 1ms -> 1, ms
  317. '''
  318. # Find ms, us, or s
  319. if 'ms' in token:
  320. unit = 'ms'
  321. num = token.split('m')[0]
  322. elif 'us' in token:
  323. unit = 'us'
  324. num = token.split('u')[0]
  325. elif 'ns' in token:
  326. unit = 'ns'
  327. num = token.split('n')[0]
  328. elif 's' in token:
  329. unit = 's'
  330. num = token.split('s')[0]
  331. else:
  332. print ( "{0} cannot find timing unit in token '{1}'".format( ERROR, token ) )
  333. return Time( float( num ), unit )
  334. def specifierTiming( timing ):
  335. '''
  336. When only timing is given, infer state at a later stage from the context of the mapping
  337. '''
  338. return ScheduleParam( None, timing )
  339. def specifierState( state, timing=None ):
  340. '''
  341. Generate a Schedule Parameter
  342. Automatically mutates itself into the correct object type
  343. '''
  344. return ScheduleParam( state, timing )
  345. def specifierAnalog( value ):
  346. '''
  347. Generate an Analog Schedule Parameter
  348. '''
  349. return AnalogScheduleParam( value )
  350. def specifierUnroll( identifier, schedule_params ):
  351. '''
  352. Unroll specifiers into the trigger/result identifier
  353. First, combine all Schedule Parameters into a Schedul
  354. Then attach Schedule to the identifier
  355. If the identifier is a list, then iterate through them
  356. and apply the schedule to each
  357. '''
  358. # Check if this is a list of identifiers
  359. if isinstance( identifier, list ):
  360. for ident in identifier:
  361. ident.setSchedule( schedule_params )
  362. return identifier
  363. else:
  364. identifier.setSchedule( schedule_params )
  365. return [ identifier ]
  366. # Range can go from high to low or low to high
  367. def scanCode_range( rangeVals ):
  368. '''
  369. Scan Code range expansion
  370. S[0x10-0x12] -> S0x10, S0x11, S0x12
  371. '''
  372. start = rangeVals[0]
  373. end = rangeVals[1]
  374. # Swap start, end if start is greater than end
  375. if start > end:
  376. start, end = end, start
  377. # Iterate from start to end, and generate the range
  378. values = list( range( start, end + 1 ) )
  379. # Generate ScanCodeIds
  380. return [ ScanCodeId( v ) for v in values ]
  381. # Range can go from high to low or low to high
  382. # Warn on 0-9 for USBCodes (as this does not do what one would expect) TODO
  383. # Lookup USB HID tags and convert to a number
  384. def hidCode_range( type, rangeVals ):
  385. '''
  386. HID Code range expansion
  387. U["A"-"C"] -> U"A", U"B", U"C"
  388. '''
  389. # Check if already integers
  390. if isinstance( rangeVals[0], int ):
  391. start = rangeVals[0]
  392. else:
  393. start = Make.hidCode( type, rangeVals[0] ).uid
  394. if isinstance( rangeVals[1], int ):
  395. end = rangeVals[1]
  396. else:
  397. end = Make.hidCode( type, rangeVals[1] ).uid
  398. # Swap start, end if start is greater than end
  399. if start > end:
  400. start, end = end, start
  401. # Iterate from start to end, and generate the range
  402. listRange = list( range( start, end + 1 ) )
  403. # Convert each item in the list to a tuple
  404. for item in range( len( listRange ) ):
  405. listRange[ item ] = HIDId( type, listRange[ item ] )
  406. return listRange
  407. def usbCode_range( rangeVals ):
  408. '''
  409. USB Keyboard HID Code range expansion
  410. '''
  411. return Make.hidCode_range( 'USBCode', rangeVals )
  412. def sysCode_range( rangeVals ):
  413. '''
  414. System Control HID Code range expansion
  415. '''
  416. return Make.hidCode_range( 'SysCode', rangeVals )
  417. def consCode_range( rangeVals ):
  418. '''
  419. Consumer Control HID Code range expansion
  420. '''
  421. return Make.hidCode_range( 'ConsCode', rangeVals )
  422. def indCode_range( rangeVals ):
  423. '''
  424. Indicator HID Code range expansion
  425. '''
  426. return Make.hidCode_range( 'IndCode', rangeVals )
  427. def range( start, end ):
  428. '''
  429. Converts a start and end points of a range to a list of numbers
  430. Can go low to high or high to low
  431. '''
  432. # High to low
  433. if end < start:
  434. return list( range( end, start + 1 ) )
  435. # Low to high
  436. return list( range( start, end + 1 ) )
  437. def capArg( argument, width=None ):
  438. '''
  439. Converts a capability argument:width to a CapArgId
  440. If no width is specified, it is ignored
  441. '''
  442. return CapArgId( argument, width )
  443. def capUsage( name, arguments ):
  444. '''
  445. Converts a capability tuple, argument list to a CapId Usage
  446. '''
  447. return CapId( name, 'Usage', arguments )
  448. ### Rules ###
  449. ## Base Rules
  450. const = lambda x: lambda _: x
  451. unarg = lambda f: lambda x: f(*x)
  452. flatten = lambda list: sum( list, [] )
  453. tokenValue = lambda x: x.value
  454. tokenType = lambda t: some( lambda x: x.type == t ) >> tokenValue
  455. operator = lambda s: a( Token( 'Operator', s ) ) >> tokenValue
  456. parenthesis = lambda s: a( Token( 'Parenthesis', s ) ) >> tokenValue
  457. bracket = lambda s: a( Token( 'Bracket', s ) ) >> tokenValue
  458. eol = a( Token( 'EndOfLine', ';' ) )
  459. def maybeFlatten( items ):
  460. '''
  461. Iterate through top-level lists
  462. Flatten, only if the element is also a list
  463. [[1,2],3,[[4,5]]] -> [1,2,3,[4,5]]
  464. '''
  465. new_list = []
  466. for elem in items:
  467. # Flatten only if a list
  468. if isinstance( elem, list ):
  469. new_list.extend( elem )
  470. else:
  471. new_list.append( elem )
  472. return new_list
  473. def listElem( item ):
  474. '''
  475. Convert to a list element
  476. '''
  477. return [ item ]
  478. def listToTuple( items ):
  479. '''
  480. Convert list to a tuple
  481. '''
  482. return tuple( items )
  483. def oneLayerFlatten( items ):
  484. '''
  485. Flatten only the top layer (list of lists of ...)
  486. '''
  487. mainList = []
  488. for sublist in items:
  489. for item in sublist:
  490. mainList.append( item )
  491. return mainList
  492. def optionExpansion( sequences ):
  493. '''
  494. Expand ranges of values in the 3rd dimension of the list, to a list of 2nd lists
  495. i.e. [ sequence, [ combo, [ range ] ] ] --> [ [ sequence, [ combo ] ], <option 2>, <option 3> ]
  496. '''
  497. expandedSequences = []
  498. # Total number of combinations of the sequence of combos that needs to be generated
  499. totalCombinations = 1
  500. # List of leaf lists, with number of leaves
  501. maxLeafList = []
  502. # Traverse to the leaf nodes, and count the items in each leaf list
  503. for sequence in sequences:
  504. for combo in sequence:
  505. rangeLen = len( combo )
  506. totalCombinations *= rangeLen
  507. maxLeafList.append( rangeLen )
  508. # Counter list to keep track of which combination is being generated
  509. curLeafList = [0] * len( maxLeafList )
  510. # Generate a list of permuations of the sequence of combos
  511. for count in range( 0, totalCombinations ):
  512. expandedSequences.append( [] ) # Prepare list for adding the new combination
  513. pos = 0
  514. # Traverse sequence of combos to generate permuation
  515. for sequence in sequences:
  516. expandedSequences[ -1 ].append( [] )
  517. for combo in sequence:
  518. expandedSequences[ -1 ][ -1 ].append( combo[ curLeafList[ pos ] ] )
  519. pos += 1
  520. # Increment combination tracker
  521. for leaf in range( 0, len( curLeafList ) ):
  522. curLeafList[ leaf ] += 1
  523. # Reset this position, increment next position (if it exists), then stop
  524. if curLeafList[ leaf ] >= maxLeafList[ leaf ]:
  525. curLeafList[ leaf ] = 0
  526. if leaf + 1 < len( curLeafList ):
  527. curLeafList[ leaf + 1 ] += 1
  528. return expandedSequences
  529. def listit( t ):
  530. '''
  531. Convert tuple of tuples to list of lists
  532. '''
  533. return list( map( listit, t ) ) if isinstance( t, ( list, tuple ) ) else t
  534. def tupleit( t ):
  535. '''
  536. Convert list of lists to tuple of tuples
  537. '''
  538. return tuple( map( tupleit, t ) ) if isinstance( t, ( tuple, list ) ) else t
  539. ## Sub Rules
  540. usbCode = tokenType('USBCode') >> Make.usbCode
  541. scanCode = tokenType('ScanCode') >> Make.scanCode
  542. consCode = tokenType('ConsCode') >> Make.consCode
  543. sysCode = tokenType('SysCode') >> Make.sysCode
  544. indCode = tokenType('IndCode') >> Make.indCode
  545. animation = tokenType('Animation') >> Make.animation
  546. pixel = tokenType('Pixel') >> Make.pixel
  547. pixelLayer = tokenType('PixelLayer') >> Make.pixelLayer
  548. none = tokenType('None') >> Make.none
  549. position = tokenType('Position') >> Make.position
  550. name = tokenType('Name')
  551. number = tokenType('Number') >> Make.number
  552. timing = tokenType('Timing') >> Make.timing
  553. comma = tokenType('Comma')
  554. dash = tokenType('Dash')
  555. plus = tokenType('Plus')
  556. content = tokenType('VariableContents')
  557. string = tokenType('String') >> Make.string
  558. unString = tokenType('String') # When the double quotes are still needed for internal processing
  559. seqString = tokenType('SequenceString') >> Make.seqString
  560. unseqString = tokenType('SequenceString') >> Make.unseqString # For use with variables
  561. pixelOperator = tokenType('PixelOperator')
  562. # Code variants
  563. code_begin = tokenType('CodeBegin')
  564. code_end = tokenType('CodeEnd')
  565. # Specifier
  566. specifier_basic = ( timing >> Make.specifierTiming ) | ( name >> Make.specifierState )
  567. specifier_complex = ( name + skip( operator(':') ) + timing ) >> unarg( Make.specifierState )
  568. specifier_state = specifier_complex | specifier_basic
  569. specifier_analog = number >> Make.specifierAnalog
  570. specifier_list = skip( parenthesis('(') ) + many( ( specifier_state | specifier_analog ) + skip( maybe( comma ) ) ) + skip( parenthesis(')') )
  571. # Scan Codes
  572. scanCode_start = tokenType('ScanCodeStart')
  573. scanCode_range = number + skip( dash ) + number >> Make.scanCode_range
  574. scanCode_listElem = number >> Make.scanCode
  575. scanCode_specifier = ( scanCode_range | scanCode_listElem ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  576. scanCode_innerList = many( scanCode_specifier + skip( maybe( comma ) ) ) >> flatten
  577. scanCode_expanded = skip( scanCode_start ) + scanCode_innerList + skip( code_end ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  578. scanCode_elem = scanCode + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  579. scanCode_combo = oneplus( ( scanCode_expanded | scanCode_elem ) + skip( maybe( plus ) ) )
  580. scanCode_sequence = oneplus( scanCode_combo + skip( maybe( comma ) ) )
  581. scanCode_single = ( skip( scanCode_start ) + scanCode_listElem + skip( code_end ) ) | scanCode
  582. # Cons Codes
  583. consCode_start = tokenType('ConsCodeStart')
  584. consCode_number = number >> Make.consCode_number
  585. consCode_range = ( consCode_number | unString ) + skip( dash ) + ( number | unString ) >> Make.consCode_range
  586. consCode_listElemTag = unString >> Make.consCode
  587. consCode_listElem = ( consCode_number | consCode_listElemTag )
  588. consCode_specifier = ( consCode_range | consCode_listElem ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  589. consCode_innerList = oneplus( consCode_specifier + skip( maybe( comma ) ) ) >> flatten
  590. consCode_expanded = skip( consCode_start ) + consCode_innerList + skip( code_end ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  591. consCode_elem = consCode + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  592. # Sys Codes
  593. sysCode_start = tokenType('SysCodeStart')
  594. sysCode_number = number >> Make.sysCode_number
  595. sysCode_range = ( sysCode_number | unString ) + skip( dash ) + ( number | unString ) >> Make.sysCode_range
  596. sysCode_listElemTag = unString >> Make.sysCode
  597. sysCode_listElem = ( sysCode_number | sysCode_listElemTag )
  598. sysCode_specifier = ( sysCode_range | sysCode_listElem ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  599. sysCode_innerList = oneplus( sysCode_specifier + skip( maybe( comma ) ) ) >> flatten
  600. sysCode_expanded = skip( sysCode_start ) + sysCode_innerList + skip( code_end ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  601. sysCode_elem = sysCode + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  602. # Indicator Codes
  603. indCode_start = tokenType('IndicatorStart')
  604. indCode_number = number >> Make.indCode_number
  605. indCode_range = ( indCode_number | unString ) + skip( dash ) + ( number | unString ) >> Make.indCode_range
  606. indCode_listElemTag = unString >> Make.indCode
  607. indCode_listElem = ( indCode_number | indCode_listElemTag )
  608. indCode_specifier = ( indCode_range | indCode_listElem ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  609. indCode_innerList = oneplus( indCode_specifier + skip( maybe( comma ) ) ) >> flatten
  610. indCode_expanded = skip( indCode_start ) + indCode_innerList + skip( code_end ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  611. indCode_elem = indCode + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  612. # USB Codes
  613. usbCode_start = tokenType('USBCodeStart')
  614. usbCode_number = number >> Make.usbCode_number
  615. usbCode_range = ( usbCode_number | unString ) + skip( dash ) + ( number | unString ) >> Make.usbCode_range
  616. usbCode_listElemTag = unString >> Make.usbCode
  617. usbCode_listElem = ( usbCode_number | usbCode_listElemTag )
  618. usbCode_specifier = ( usbCode_range | usbCode_listElem ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  619. usbCode_innerList = oneplus( usbCode_specifier + skip( maybe( comma ) ) ) >> flatten
  620. usbCode_expanded = skip( usbCode_start ) + usbCode_innerList + skip( code_end ) + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  621. usbCode_elem = usbCode + maybe( specifier_list ) >> unarg( Make.specifierUnroll )
  622. # HID Codes
  623. hidCode_elem = usbCode_expanded | usbCode_elem | sysCode_expanded | sysCode_elem | consCode_expanded | consCode_elem | indCode_expanded | indCode_elem
  624. usbCode_combo = oneplus( hidCode_elem + skip( maybe( plus ) ) ) >> listElem
  625. usbCode_sequence = oneplus( ( usbCode_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten
  626. # Pixels
  627. pixel_start = tokenType('PixelStart')
  628. pixel_range = ( number ) + skip( dash ) + ( number ) >> unarg( Make.range )
  629. pixel_listElem = number >> listElem
  630. pixel_innerList = many( ( pixel_range | pixel_listElem ) + skip( maybe( comma ) ) ) >> flatten >> Make.pixel_list
  631. pixel_expanded = skip( pixel_start ) + pixel_innerList + skip( code_end )
  632. pixel_elem = pixel >> listElem
  633. # Pixel Layer
  634. pixellayer_start = tokenType('PixelLayerStart')
  635. pixellayer_range = ( number ) + skip( dash ) + ( number ) >> unarg( Make.range )
  636. pixellayer_listElem = number >> listElem
  637. pixellayer_innerList = many( ( pixellayer_range | pixellayer_listElem ) + skip( maybe( comma ) ) ) >> flatten >> Make.pixelLayer_list
  638. pixellayer_expanded = skip( pixellayer_start ) + pixellayer_innerList + skip( code_end )
  639. pixellayer_elem = pixelLayer >> listElem
  640. # Pixel Channels
  641. pixelchan_chans = many( number + skip( operator(':') ) + number + skip( maybe( comma ) ) )
  642. pixelchan_elem = ( pixel_expanded | pixel_elem ) + skip( parenthesis('(') ) + pixelchan_chans + skip( parenthesis(')') ) >> unarg( Make.pixelchan )
  643. # Pixel Mods
  644. pixelmod_mods = many( maybe( pixelOperator | plus | dash ) + number + skip( maybe( comma ) ) )
  645. pixelmod_layer = ( pixellayer_expanded | pixellayer_elem )
  646. pixelmod_elem = ( pixel_expanded | pixel_elem | pixelmod_layer ) + skip( parenthesis('(') ) + pixelmod_mods + skip( parenthesis(')') ) >> unarg( Make.pixelmod )
  647. # Pixel Capability
  648. pixel_capability = pixelmod_elem
  649. # Animations
  650. animation_start = tokenType('AnimationStart')
  651. animation_name = name
  652. animation_frame_range = ( number ) + skip( dash ) + ( number ) >> unarg( Make.range )
  653. animation_name_frame = many( ( animation_frame_range | number ) + skip( maybe( comma ) ) ) >> maybeFlatten
  654. animation_def = skip( animation_start ) + animation_name + skip( code_end ) >> Make.animation
  655. animation_expanded = skip( animation_start ) + animation_name + skip( maybe( comma ) ) + animation_name_frame + skip( code_end ) >> unarg( Make.animationTrigger )
  656. animation_flattened = animation_expanded >> flatten >> flatten
  657. animation_elem = animation
  658. # Animation Modifier
  659. animation_modifier = many( ( name | number ) + maybe( skip( operator(':') ) + number ) + skip( maybe( comma ) ) )
  660. animation_modlist = animation_modifier >> Make.animationModlist
  661. # Animation Capability
  662. animation_capability = ( ( animation_def | animation_elem ) + maybe( skip( parenthesis('(') ) + animation_modifier + skip( parenthesis(')') ) ) ) >> unarg( Make.animationCapability )
  663. # Capabilities
  664. capFunc_argument = number >> Make.capArg # TODO Allow for symbolic arguments, i.e. arrays and variables
  665. capFunc_arguments = many( capFunc_argument + skip( maybe( comma ) ) )
  666. capFunc_elem = name + skip( parenthesis('(') ) + capFunc_arguments + skip( parenthesis(')') ) >> unarg( Make.capUsage ) >> listElem
  667. capFunc_combo = oneplus( ( hidCode_elem | capFunc_elem | animation_capability | pixel_capability ) + skip( maybe( plus ) ) ) >> listElem
  668. capFunc_sequence = oneplus( ( capFunc_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten
  669. # Trigger / Result Codes
  670. triggerCode_outerList = scanCode_sequence >> optionExpansion
  671. triggerUSBCode_outerList = usbCode_sequence >> optionExpansion
  672. resultCode_outerList = ( ( capFunc_sequence >> optionExpansion ) | none )
  673. # Positions
  674. position_list = oneplus( position + skip( maybe( comma ) ) )