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.

organization.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. #!/usr/bin/env python3
  2. '''
  3. KLL Data Organization
  4. '''
  5. # Copyright (C) 2016 by Jacob Alexander
  6. #
  7. # This file is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This file is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this file. If not, see <http://www.gnu.org/licenses/>.
  19. ### Imports ###
  20. import re
  21. ### Decorators ###
  22. ## Print Decorator Variables
  23. ERROR = '\033[5;1;31mERROR\033[0m:'
  24. WARNING = '\033[5;1;33mWARNING\033[0m:'
  25. ansi_escape = re.compile(r'\x1b[^m]*m')
  26. ### Classes ###
  27. class Data:
  28. '''
  29. Base class for KLL datastructures
  30. '''
  31. # Debug output formatters
  32. debug_output = {
  33. 'add' : "\t\033[1;42;37m++\033[0m\033[1mADD KEY\033[1;42;37m++\033[0m \033[1m<==\033[0m {0}",
  34. 'app' : "\t\033[1;45;37m**\033[0m\033[1mAPP KEY\033[1;45;37m**\033[0m \033[1m<==\033[0m {0}",
  35. 'mod' : "\t\033[1;44;37m##\033[0m\033[1mMOD KEY\033[1;44;37m##\033[0m \033[1m<==\033[0m {0}",
  36. 'rem' : "\t\033[1;41;37m--\033[0m\033[1mREM KEY\033[1;41;37m--\033[0m \033[1m<==\033[0m {0}",
  37. 'drp' : "\t\033[1;43;37m@@\033[0m\033[1mDRP KEY\033[1;43;37m@@\033[0m \033[1m<==\033[0m {0}",
  38. 'dup' : "\t\033[1;46;37m!!\033[0m\033[1mDUP KEY\033[1;46;37m!!\033[0m \033[1m<==\033[0m {0}",
  39. }
  40. def __init__( self, parent ):
  41. '''
  42. Initialize datastructure
  43. @param parent: Parent organization, used to query data from other datastructures
  44. '''
  45. self.data = {}
  46. self.parent = parent
  47. def add_expression( self, expression, debug ):
  48. '''
  49. Add expression to data structure
  50. May have multiple keys to add for a given expression
  51. @param expression: KLL Expression (fully tokenized and parsed)
  52. @param debug: Enable debug output
  53. '''
  54. # Lookup unique keys for expression
  55. keys = expression.unique_keys()
  56. # Add/Modify expressions in datastructure
  57. for key, uniq_expr in keys:
  58. # Check which operation we are trying to do, add or modify
  59. if debug[0]:
  60. if key in self.data.keys():
  61. output = self.debug_output['mod'].format( key )
  62. else:
  63. output = self.debug_output['add'].format( key )
  64. print( debug[1] and output or ansi_escape.sub( '', output ) )
  65. self.data[ key ] = uniq_expr
  66. def merge( self, merge_in, debug ):
  67. '''
  68. Merge in the given datastructure to this datastructure
  69. This datastructure serves as the base.
  70. @param merge_in: Data structure from another organization to merge into this one
  71. @param debug: Enable debug out
  72. '''
  73. # The default case is just to add the expression in directly
  74. for key, kll_expression in merge_in.data.items():
  75. # Display key:expression being merged in
  76. if debug[0]:
  77. output = merge_in.elem_str( key, True )
  78. print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
  79. self.add_expression( kll_expression, debug )
  80. def reduction( self ):
  81. '''
  82. Simplifies datastructure
  83. Most of the datastructures don't have a reduction. Just do nothing in this case.
  84. '''
  85. pass
  86. def elem_str( self, key, single=False ):
  87. '''
  88. Debug output for a single element
  89. @param key: Index to datastructure
  90. @param single: Setting to True will bold the key
  91. '''
  92. if single:
  93. return "\033[1;33m{0: <20}\033[0m \033[1;36;41m>\033[0m {1}\n".format( key, self.data[ key ] )
  94. else:
  95. return "{0: <20} \033[1;36;41m>\033[0m {1}\n".format( key, self.data[ key ] )
  96. def __repr__( self ):
  97. output = ""
  98. # Display sorted list of keys, along with the internal value
  99. for key in sorted( self.data ):
  100. output += self.elem_str( key )
  101. return output
  102. class MappingData( Data ):
  103. '''
  104. KLL datastructure for data mapping
  105. ScanCode trigger -> result
  106. USBCode trigger -> result
  107. Animation trigger -> result
  108. '''
  109. def add_expression( self, expression, debug ):
  110. '''
  111. Add expression to data structure
  112. May have multiple keys to add for a given expression
  113. Map expressions insert into the datastructure according to their operator.
  114. +Operators+
  115. : Add/Modify
  116. :+ Append
  117. :- Remove
  118. :: Lazy Add/Modify
  119. i: Add/Modify
  120. i:+ Append
  121. i:- Remove
  122. i:: Lazy Add/Modify
  123. The i or isolation operators are stored separately from the main ones.
  124. Each key is pre-pended with an i
  125. The :: or lazy operators act just like : operators, except that they will be ignore if the evaluation
  126. merge cannot resolve a ScanCode.
  127. @param expression: KLL Expression (fully tokenized and parsed)
  128. @param debug: Enable debug output
  129. '''
  130. # Lookup unique keys for expression
  131. keys = expression.unique_keys()
  132. # Add/Modify expressions in datastructure
  133. for key, uniq_expr in keys:
  134. # Determine which the expression operator
  135. operator = expression.operator
  136. # Except for the : operator, all others have delayed action
  137. # Meaning, they change behaviour depending on how Contexts are merged
  138. # This means we can't simplify yet
  139. # In addition, :+ and :- are stackable, which means each key has a list of expressions
  140. # We append the operator to differentiate between the different types of delayed operations
  141. key = "{0}{1}".format( operator, key )
  142. # Determine if key exists already
  143. exists = key in self.data.keys()
  144. # Add/Modify
  145. if operator in [':', '::', 'i:', 'i::']:
  146. debug_tag = exists and 'mod' or 'add'
  147. # Append/Remove
  148. else:
  149. # Check to make sure we haven't already appended expression
  150. # Use the string representation to do the comparison (general purpose)
  151. if exists and "{0}".format( uniq_expr ) in [ "{0}".format( elem ) for elem in self.data[ key ] ]:
  152. debug_tag = 'dup'
  153. # Append
  154. elif operator in [':+', 'i:+']:
  155. debug_tag = 'app'
  156. # Remove
  157. else:
  158. debug_tag = 'rem'
  159. # Debug output
  160. if debug[0]:
  161. output = self.debug_output[ debug_tag ].format( key )
  162. print( debug[1] and output or ansi_escape.sub( '', output ) )
  163. # Don't append if a duplicate
  164. if debug_tag == 'dup':
  165. continue
  166. # Append, rather than replace
  167. if operator in [':+', ':-', 'i:+', 'i:-']:
  168. if exists:
  169. self.data[ key ].append( uniq_expr )
  170. # Create initial list
  171. else:
  172. self.data[ key ] = [ uniq_expr ]
  173. else:
  174. self.data[ key ] = [ uniq_expr ]
  175. def set_interconnect_id( self, interconnect_id, triggers ):
  176. '''
  177. Traverses the sequence of combo of identifiers to set the interconnect_id
  178. '''
  179. for sequence in triggers:
  180. for combo in sequence:
  181. for identifier in combo:
  182. identifier.interconnect_id = interconnect_id
  183. def merge( self, merge_in, debug ):
  184. '''
  185. Merge in the given datastructure to this datastructure
  186. This datastructure serves as the base.
  187. Map expressions merge differently than insertions.
  188. +Operators+
  189. : Add/Modify - Replace
  190. :+ Append - Add
  191. :- Remove - Remove
  192. :: Lazy Add/Modify - Replace if found, otherwise drop
  193. i: Add/Modify - Replace
  194. i:+ Append - Add
  195. i:- Remove - Remove
  196. i:: Lazy Add/Modify - Replace if found, otherwise drop
  197. @param merge_in: Data structure from another organization to merge into this one
  198. @param debug: Enable debug out
  199. '''
  200. # Check what the current interconnectId is
  201. # If not set, we set to 0 (default)
  202. # We use this to calculate the scancode during the DataAnalysisStage
  203. interconnect_id = 0
  204. if 'interconnectId' in self.parent.variable_data.data.keys():
  205. interconnect_id = self.parent.variable_data.data['interconnectId']
  206. # Sort different types of keys
  207. cur_keys = merge_in.data.keys()
  208. # Lazy Set ::
  209. lazy_keys = [ key for key in cur_keys if key[0:2] == '::' or key[0:3] == 'i::' ]
  210. cur_keys = list( set( cur_keys ) - set( lazy_keys ) )
  211. # Append :+
  212. append_keys = [ key for key in cur_keys if key[0:2] == ':+' or key[0:3] == 'i:+' ]
  213. cur_keys = list( set( cur_keys ) - set( append_keys ) )
  214. # Remove :-
  215. remove_keys = [ key for key in cur_keys if key[0:2] == ':-' or key[0:3] == 'i:-' ]
  216. cur_keys = list( set( cur_keys ) - set( remove_keys ) )
  217. # Set :
  218. # Everything left is just a set
  219. set_keys = cur_keys
  220. # First process the :: (or lazy) operators
  221. # We need to read into this datastructure and apply those first
  222. # Otherwise we may get undesired behaviour
  223. for key in lazy_keys:
  224. # Display key:expression being merged in
  225. if debug[0]:
  226. output = merge_in.elem_str( key, True )
  227. print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
  228. # Construct target key
  229. target_key = key[0] == 'i' and "i{0}".format( key[2:] ) or key[1:]
  230. # If target key exists, replace
  231. if target_key in self.data.keys():
  232. debug_tag = 'mod'
  233. else:
  234. debug_tag = 'drp'
  235. # Debug output
  236. if debug[0]:
  237. output = self.debug_output[ debug_tag ].format( key )
  238. print( debug[1] and output or ansi_escape.sub( '', output ) )
  239. # Only replace
  240. if debug_tag == 'mod':
  241. self.data[ target_key ] = merge_in.data[ key ]
  242. # Then apply : assignment operators
  243. for key in set_keys:
  244. # Display key:expression being merged in
  245. if debug[0]:
  246. output = merge_in.elem_str( key, True )
  247. print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
  248. # Construct target key
  249. target_key = key
  250. # Indicate if add or modify
  251. if target_key in self.data.keys():
  252. debug_tag = 'mod'
  253. else:
  254. debug_tag = 'add'
  255. # Debug output
  256. if debug[0]:
  257. output = self.debug_output[ debug_tag ].format( key )
  258. print( debug[1] and output or ansi_escape.sub( '', output ) )
  259. # Set into new datastructure regardless
  260. self.data[ target_key ] = merge_in.data[ key ]
  261. # Only the : is used to set ScanCodes
  262. # We need to set the interconnect_id just in case the base context has it set
  263. # and in turn influence the new context as well
  264. # This must be done during the merge
  265. for elem in self.data[ target_key ]:
  266. if elem.type == 'ScanCode':
  267. self.set_interconnect_id( interconnect_id, elem.triggers )
  268. # Now apply append operations
  269. for key in append_keys:
  270. # Display key:expression being merged in
  271. if debug[0]:
  272. output = merge_in.elem_str( key, True )
  273. print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
  274. # Construct target key
  275. target_key = key[0] == 'i' and "i:{0}".format( key[3:] ) or ":{0}".format( key[2:] )
  276. # Alwyays appending
  277. debug_tag = 'app'
  278. # Debug output
  279. if debug[0]:
  280. output = self.debug_output[ debug_tag ].format( key )
  281. print( debug[1] and output or ansi_escape.sub( '', output ) )
  282. # Extend list if it exists
  283. if target_key in self.data.keys():
  284. self.data[ target_key ].extend( merge_in.data[ key ] )
  285. else:
  286. self.data[ target_key ] = merge_in.data[ key ]
  287. # Finally apply removal operations to this datastructure
  288. # If the target removal doesn't exist, ignore silently (show debug message)
  289. for key in remove_keys:
  290. # Display key:expression being merged in
  291. if debug[0]:
  292. output = merge_in.elem_str( key, True )
  293. print( debug[1] and output or ansi_escape.sub( '', output ), end="" )
  294. # Construct target key
  295. target_key = key[0] == 'i' and "i:{0}".format( key[3:] ) or ":{0}".format( key[2:] )
  296. # Drop right away if target datastructure doesn't have target key
  297. if target_key not in self.data.keys():
  298. debug_tag = 'drp'
  299. # Debug output
  300. if debug[0]:
  301. output = self.debug_output[ debug_tag ].format( key )
  302. print( debug[1] and output or ansi_escape.sub( '', output ) )
  303. continue
  304. # Compare expressions to be removed with the current set
  305. # Use strings to compare
  306. remove_expressions = [ "{0}".format( expr ) for expr in merge_in.data[ key ] ]
  307. current_expressions = [ ( "{0}".format( expr ), expr ) for expr in self.data[ target_key ] ]
  308. for string, expr in current_expressions:
  309. debug_tag = 'drp'
  310. # Check if an expression matches
  311. if string in remove_expressions:
  312. debug_tag = 'rem'
  313. # Debug output
  314. if debug[0]:
  315. output = self.debug_output[ debug_tag ].format( key )
  316. print( debug[1] and output or ansi_escape.sub( '', output ) )
  317. # Remove if found
  318. if debug_tag == 'rem':
  319. self.data[ target_key ] = [ value for value in self.data.values() if value != expr ]
  320. def reduction( self ):
  321. '''
  322. Simplifies datastructure
  323. Used to replace all trigger HIDCode(USBCode)s with ScanCodes
  324. NOTE: Make sure to create a new MergeContext before calling this as you lose data and prior context
  325. '''
  326. scan_code_lookup = {}
  327. # Build dictionary of single ScanCodes first
  328. for key, expr in self.data.items():
  329. if expr[0].elems()[0] == 1 and expr[0].triggers[0][0][0].type == 'ScanCode':
  330. scan_code_lookup[ key ] = expr
  331. # Using this dictionary, replace all the trigger USB codes
  332. new_data = copy.copy( scan_code_lookup )
  333. # 1) Single USB Codes trigger results will replace the original ScanCode result
  334. # 2)
  335. #TODO
  336. print("YAY")
  337. print( scan_code_lookup )
  338. class AnimationData( Data ):
  339. '''
  340. KLL datastructure for Animation configuration
  341. Animation -> modifiers
  342. '''
  343. class AnimationFrameData( Data ):
  344. '''
  345. KLL datastructure for Animation Frame configuration
  346. Animation -> Pixel Settings
  347. '''
  348. class CapabilityData( Data ):
  349. '''
  350. KLL datastructure for Capability mapping
  351. Capability -> C Function/Identifier
  352. '''
  353. class DefineData( Data ):
  354. '''
  355. KLL datastructure for Define mapping
  356. Variable -> C Define/Identifier
  357. '''
  358. class PixelChannelData( Data ):
  359. '''
  360. KLL datastructure for Pixel Channel mapping
  361. Pixel -> Channels
  362. '''
  363. class PixelPositionData( Data ):
  364. '''
  365. KLL datastructure for Pixel Position mapping
  366. Pixel -> Physical Location
  367. '''
  368. class ScanCodePositionData( Data ):
  369. '''
  370. KLL datastructure for ScanCode Position mapping
  371. ScanCode -> Physical Location
  372. '''
  373. class VariableData( Data ):
  374. '''
  375. KLL datastructure for Variables and Arrays
  376. Variable -> Data
  377. Array -> Data
  378. '''
  379. class Organization:
  380. '''
  381. Container class for KLL datastructures
  382. The purpose of these datastructures is to symbolically store at first, and slowly solve/deduplicate expressions.
  383. Since the order in which the merges occurs matters, this involves a number of intermediate steps.
  384. '''
  385. def __init__( self ):
  386. '''
  387. Intialize data structure
  388. '''
  389. # Setup each of the internal sub-datastructures
  390. self.animation_data = AnimationData( self )
  391. self.animation_frame_data = AnimationFrameData( self )
  392. self.capability_data = CapabilityData( self )
  393. self.define_data = DefineData( self )
  394. self.mapping_data = MappingData( self )
  395. self.pixel_channel_data = PixelChannelData( self )
  396. self.pixel_position_data = PixelPositionData( self )
  397. self.scan_code_position_data = ScanCodePositionData( self )
  398. self.variable_data = VariableData( self )
  399. # Expression to Datastructure mapping
  400. self.data_mapping = {
  401. 'AssignmentExpression' : {
  402. 'Array' : self.variable_data,
  403. 'Variable' : self.variable_data,
  404. },
  405. 'DataAssociationExpression' : {
  406. 'Animation' : self.animation_data,
  407. 'AnimationFrame' : self.animation_frame_data,
  408. 'PixelPosition' : self.pixel_position_data,
  409. 'ScanCodePosition' : self.scan_code_position_data,
  410. },
  411. 'MapExpression' : {
  412. 'ScanCode' : self.mapping_data,
  413. 'USBCode' : self.mapping_data,
  414. 'Animation' : self.mapping_data,
  415. 'PixelChannel' : self.pixel_channel_data,
  416. },
  417. 'NameAssociationExpression' : {
  418. 'Capability' : self.capability_data,
  419. 'Define' : self.define_data,
  420. },
  421. }
  422. def stores( self ):
  423. '''
  424. Returns list of sub-datastructures
  425. '''
  426. return [
  427. self.animation_data,
  428. self.animation_frame_data,
  429. self.capability_data,
  430. self.define_data,
  431. self.mapping_data,
  432. self.pixel_channel_data,
  433. self.pixel_position_data,
  434. self.scan_code_position_data,
  435. self.variable_data,
  436. ]
  437. def add_expression( self, expression, debug ):
  438. '''
  439. Add expression to datastructure
  440. Will automatically determine which type of expression and place in the relevant store
  441. @param expression: KLL Expression (fully tokenized and parsed)
  442. @param debug: Enable debug output
  443. '''
  444. # Determine type of of Expression
  445. expression_type = expression.__class__.__name__
  446. # Determine Expression Subtype
  447. expression_subtype = expression.type
  448. # Locate datastructure
  449. data = self.data_mapping[ expression_type ][ expression_subtype ]
  450. # Debug output
  451. if debug[0]:
  452. output = "\t\033[4m{0}\033[0m".format( data.__class__.__name__ )
  453. print( debug[1] and output or ansi_escape.sub( '', output ) )
  454. # Add expression to determined datastructure
  455. data.add_expression( expression, debug )
  456. def merge( self, merge_in, debug ):
  457. '''
  458. Merge in the given organization to this organization
  459. This organization serves as the base.
  460. @param merge_in: Organization to merge into this one
  461. @param debug: Enable debug out
  462. '''
  463. # Merge each of the sub-datastructures
  464. for this, that in zip( self.stores(), merge_in.stores() ):
  465. this.merge( that, debug )
  466. def reduction( self ):
  467. '''
  468. Simplifies datastructure
  469. NOTE: This will remove data, therefore, context is lost
  470. '''
  471. for store in self.stores():
  472. store.reduction()
  473. def __repr__( self ):
  474. return "{0}".format( self.stores() )