2 # KLL Compiler Containers
4 # Copyright (C) 2014-2015 by Jacob Alexander
6 # This file is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This file is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this file. If not, see <http://www.gnu.org/licenses/>.
27 ## Print Decorator Variables
28 ERROR = '\033[5;1;31mERROR\033[0m:'
38 # Container for ScanCodes
40 # scancode - Non-interconnect adjusted scan code
41 # interconnect_id - Unique id for the interconnect node
42 def __init__( self, scancode, interconnect_id ):
43 self.scancode = scancode
44 self.interconnect_id = interconnect_id
46 def __eq__( self, other ):
47 return self.dict() == other.dict()
50 return repr( self.dict() )
54 'ScanCode' : self.scancode,
55 'Id' : self.interconnect_id,
58 # Calculate the actual scancode using the offset list
59 def offset( self, offsetList ):
60 if self.interconnect_id > 0:
61 return self.scancode + offsetList[ self.interconnect_id - 1 ]
67 # Unique lookup for ScanCodes
71 def __getitem__( self, name ):
72 # First check if this is a ScanCode object
73 if isinstance( name, ScanCode ):
75 for idx, scancode in enumerate( self.scancodes ):
79 # Could not find scancode
82 # Return scancode using unique id
83 return self.scancodes[ name ]
85 # Attempt add ScanCode to list, return unique id
86 def append( self, new_scancode ):
87 # Iterate through list to make sure this is a unique ScanCode
88 for idx, scancode in enumerate( self.scancodes ):
89 if new_scancode == scancode:
92 # Unique entry, add to the list
93 self.scancodes.append( new_scancode )
95 return len( self.scancodes ) - 1
99 # Container for capabilities dictionary and convenience functions
100 def __init__( self ):
101 self.capabilities = dict()
103 def __getitem__( self, name ):
104 return self.capabilities[ name ]
106 def __setitem__( self, name, contents ):
107 self.capabilities[ name ] = contents
109 def __repr__( self ):
110 return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) )
113 # Total bytes needed to store arguments
114 def totalArgBytes( self, name ):
117 # Iterate over the arguments, summing the total bytes
118 for arg in self.capabilities[ name ][ 1 ]:
119 totalBytes += int( arg[ 1 ] )
123 # Name of the capability function
124 def funcName( self, name ):
125 return self.capabilities[ name ][ 0 ]
128 # Only valid while dictionary keys are not added/removed
129 def getIndex( self, name ):
130 return sorted( self.capabilities, key = self.capabilities.get ).index( name )
132 def getName( self, index ):
133 return sorted( self.capabilities, key = self.capabilities.get )[ index ]
136 return sorted( self.capabilities, key = self.capabilities.get )
140 # Container for Trigger Macro : Result Macro correlation
141 # Layer selection for generating TriggerLists
143 # Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible)
144 # Two staged list for ResultMacros:
145 # 1) USB Code/Non-converted (may contain capabilities)
147 def __init__( self ):
151 # Unique ScanCode Hash Id Lookup
152 self.scanCodeStore = ScanCodeStore()
155 self.macros = [ dict() ]
157 # Base Layout Storage
158 self.baseLayout = None
159 self.layerLayoutMarkers = []
161 # Correlated Macro Data
162 self.resultsIndex = dict()
163 self.triggersIndex = dict()
164 self.resultsIndexSorted = []
165 self.triggersIndexSorted = []
166 self.triggerList = []
167 self.maxScanCode = []
168 self.firstScanCode = []
169 self.interconnectOffset = []
171 # USBCode Assignment Cache
172 self.assignmentCache = []
174 def __repr__( self ):
175 return "{0}".format( self.macros )
177 def completeBaseLayout( self ):
178 # Copy base layout for later use when creating partial layers and add marker
179 self.baseLayout = copy.deepcopy( self.macros[ 0 ] )
180 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) ) # Not used for default layer, just simplifies coding
182 def removeUnmarked( self ):
183 # Remove all of the unmarked mappings from the partial layer
184 for trigger in self.layerLayoutMarkers[ self.layer ].keys():
185 del self.macros[ self.layer ][ trigger ]
187 def addLayer( self ):
188 # Increment layer count, and append another macros dictionary
190 self.macros.append( copy.deepcopy( self.baseLayout ) )
192 # Add a layout marker for each layer
193 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) )
195 # Use for ScanCode trigger macros
196 def appendScanCode( self, trigger, result ):
197 if not trigger in self.macros[ self.layer ]:
198 self.replaceScanCode( trigger, result )
200 self.macros[ self.layer ][ trigger ].append( result )
202 # Remove the given trigger/result pair
203 def removeScanCode( self, trigger, result ):
204 # Remove all instances of the given trigger/result pair
205 while result in self.macros[ self.layer ][ trigger ]:
206 self.macros[ self.layer ][ trigger ].remove( result )
208 # Replaces the given trigger with the given result
209 # If multiple results for a given trigger, clear, then add
210 def replaceScanCode( self, trigger, result ):
211 self.macros[ self.layer ][ trigger ] = [ result ]
213 # Mark layer scan code, so it won't be removed later
214 # Also check to see if it hasn't already been removed before
215 if not self.baseLayout is None and trigger in self.layerLayoutMarkers[ self.layer ]:
216 del self.layerLayoutMarkers[ self.layer ][ trigger ]
218 # Return a list of ScanCode triggers with the given USB Code trigger
219 def lookupUSBCodes( self, usbCode ):
222 # Scan current layer for USB Codes
223 for macro in self.macros[ self.layer ].keys():
224 if usbCode in self.macros[ self.layer ][ macro ]:
225 scanCodeList.append( macro )
227 if len(scanCodeList) == 0:
228 if len(usbCode) > 1 or len(usbCode[0]) > 1:
229 for combo in usbCode:
232 scanCode = self.lookupUSBCodes(((key,),))
233 comboCodes.append(scanCode[0][0][0])
234 scanCodeList.append(tuple(code for code in comboCodes))
235 scanCodeList = [tuple(scanCodeList)]
239 # Check whether we should do soft replacement
240 def softReplaceCheck( self, scanCode ):
241 # First check if not the default layer
245 # Check if current layer is set the same as the BaseMap
246 if not self.baseLayout is None and scanCode in self.layerLayoutMarkers[ self.layer ]:
249 # Otherwise, allow replacement
252 # Cache USBCode Assignment
253 def cacheAssignment( self, operator, scanCode, result ):
254 self.assignmentCache.append( [ operator, scanCode, result ] )
256 # Assign cached USBCode Assignments
257 def replayCachedAssignments( self ):
258 # Iterate over each item in the assignment cache
259 for item in self.assignmentCache:
260 # Check operator, and choose the specified assignment action
263 self.appendScanCode( item[1], item[2] )
266 elif item[0] == ":-":
267 self.removeScanCode( item[1], item[2] )
270 elif item[0] == ":" or item[0] == "::":
271 self.replaceScanCode( item[1], item[2] )
273 # Clear assignment cache
274 self.assignmentCache = []
276 # Generate/Correlate Layers
277 def generate( self ):
278 self.generateIndices()
279 self.sortIndexLists()
280 self.generateOffsetTable()
281 self.generateTriggerLists()
283 # Generates Index of Results and Triggers
284 def generateIndices( self ):
285 # Iterate over every trigger result, and add to the resultsIndex and triggersIndex
286 for layer in range( 0, len( self.macros ) ):
287 for trigger in self.macros[ layer ].keys():
288 # Each trigger has a list of results
289 for result in self.macros[ layer ][ trigger ]:
290 # Only add, with an index, if result hasn't been added yet
291 if not result in self.resultsIndex:
292 self.resultsIndex[ result ] = len( self.resultsIndex )
294 # Then add a trigger for each result, if trigger hasn't been added yet
295 triggerItem = tuple( [ trigger, self.resultsIndex[ result ] ] )
296 if not triggerItem in self.triggersIndex:
297 self.triggersIndex[ triggerItem ] = len( self.triggersIndex )
299 # Sort Index Lists using the indices rather than triggers/results
300 def sortIndexLists( self ):
301 self.resultsIndexSorted = [ None ] * len( self.resultsIndex )
302 # Iterate over the resultsIndex and sort by index
303 for result in self.resultsIndex.keys():
304 self.resultsIndexSorted[ self.resultsIndex[ result ] ] = result
306 self.triggersIndexSorted = [ None ] * len( self.triggersIndex )
307 # Iterate over the triggersIndex and sort by index
308 for trigger in self.triggersIndex.keys():
309 self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger
311 # Generates list of offsets for each of the interconnect ids
312 def generateOffsetTable( self ):
313 idMaxScanCode = [ 0 ]
315 # Iterate over each layer to get list of max scancodes associated with each interconnect id
316 for layer in range( 0, len( self.macros ) ):
317 # Iterate through each trigger/sequence in the layer
318 for sequence in self.macros[ layer ].keys():
319 # Iterate over the trigger to locate the ScanCodes
320 for combo in sequence:
321 # Iterate over each scancode id in the combo
322 for scancode_id in combo:
324 scancode_obj = self.scanCodeStore[ scancode_id ]
326 # Extend list if not large enough
327 if scancode_obj.interconnect_id >= len( idMaxScanCode ):
328 idMaxScanCode.extend( [ 0 ] * ( scancode_obj.interconnect_id - len( idMaxScanCode ) + 1 ) )
330 # Determine if the max seen id for this interconnect id
331 if scancode_obj.scancode > idMaxScanCode[ scancode_obj.interconnect_id ]:
332 idMaxScanCode[ scancode_obj.interconnect_id ] = scancode_obj.scancode
334 # Generate interconnect offsets
335 self.interconnectOffset = [ idMaxScanCode[0] + 1 ]
336 for index in range( 1, len( idMaxScanCode ) ):
337 self.interconnectOffset.append( self.interconnectOffset[ index - 1 ] + idMaxScanCode[ index ] )
339 # Generates Trigger Lists per layer using index lists
340 def generateTriggerLists( self ):
341 for layer in range( 0, len( self.macros ) ):
342 # Set max scancode to 0xFF (255)
343 # But keep track of the actual max scancode and reduce the list size
344 self.triggerList.append( [ [] ] * 0xFF )
345 self.maxScanCode.append( 0x00 )
347 # Iterate through trigger macros to locate necessary ScanCodes and corresponding triggerIndex
348 for trigger in self.macros[ layer ].keys():
349 for variant in range( 0, len( self.macros[ layer ][ trigger ] ) ):
350 # Identify result index
351 resultIndex = self.resultsIndex[ self.macros[ layer ][ trigger ][ variant ] ]
353 # Identify trigger index
354 triggerIndex = self.triggersIndex[ tuple( [ trigger, resultIndex ] ) ]
356 # Iterate over the trigger to locate the ScanCodes
357 for sequence in trigger:
358 for combo_id in sequence:
359 combo = self.scanCodeStore[ combo_id ].offset( self.interconnectOffset )
360 # Append triggerIndex for each found scanCode of the Trigger List
361 # Do not re-add if triggerIndex is already in the Trigger List
362 if not triggerIndex in self.triggerList[ layer ][ combo ]:
363 # Append is working strangely with list pre-initialization
364 # Doing a 0 check replacement instead -HaaTa
365 if len( self.triggerList[ layer ][ combo ] ) == 0:
366 self.triggerList[ layer ][ combo ] = [ triggerIndex ]
368 self.triggerList[ layer ][ combo ].append( triggerIndex )
370 # Look for max Scan Code
371 if combo > self.maxScanCode[ layer ]:
372 self.maxScanCode[ layer ] = combo
374 # Shrink triggerList to actual max size
375 self.triggerList[ layer ] = self.triggerList[ layer ][ : self.maxScanCode[ layer ] + 1 ]
377 # Calculate first scan code for layer, useful for uC implementations trying to save RAM
379 for triggerList in range( 0, len( self.triggerList[ layer ] ) ):
380 firstScanCode = triggerList
382 # Break if triggerList has items
383 if len( self.triggerList[ layer ][ triggerList ] ) > 0:
385 self.firstScanCode.append( firstScanCode )
387 # Determine overall maxScanCode
388 self.overallMaxScanCode = 0x00
389 for maxVal in self.maxScanCode:
390 if maxVal > self.overallMaxScanCode:
391 self.overallMaxScanCode = maxVal
395 # Container for variables
396 # Stores three sets of variables, the overall combined set, per layer, and per file
397 def __init__( self ):
398 # Dictionaries of variables
399 self.baseLayout = dict()
400 self.fileVariables = dict()
401 self.layerVariables = [ dict() ]
402 self.overallVariables = dict()
403 self.defines = dict()
405 self.currentFile = ""
406 self.currentLayer = 0
407 self.baseLayoutEnabled = True
409 def baseLayoutFinished( self ):
410 self.baseLayoutEnabled = False
412 def setCurrentFile( self, name ):
413 # Store using filename and current layer
414 self.currentFile = name
415 self.fileVariables[ name ] = dict()
417 # If still processing BaseLayout
418 if self.baseLayoutEnabled:
419 if '*LayerFiles' in self.baseLayout.keys():
420 self.baseLayout['*LayerFiles'] += [ name ]
422 self.baseLayout['*LayerFiles'] = [ name ]
423 # Set for the current layer
425 if '*LayerFiles' in self.layerVariables[ self.currentLayer ].keys():
426 self.layerVariables[ self.currentLayer ]['*LayerFiles'] += [ name ]
428 self.layerVariables[ self.currentLayer ]['*LayerFiles'] = [ name ]
430 def incrementLayer( self ):
431 # Store using layer index
432 self.currentLayer += 1
433 self.layerVariables.append( dict() )
435 def assignVariable( self, key, value ):
436 # Overall set of variables
437 self.overallVariables[ key ] = value
439 # The Name variable is a special accumulation case
441 # BaseLayout still being processed
442 if self.baseLayoutEnabled:
443 if '*NameStack' in self.baseLayout.keys():
444 self.baseLayout['*NameStack'] += [ value ]
446 self.baseLayout['*NameStack'] = [ value ]
449 if '*NameStack' in self.layerVariables[ self.currentLayer ].keys():
450 self.layerVariables[ self.currentLayer ]['*NameStack'] += [ value ]
452 self.layerVariables[ self.currentLayer ]['*NameStack'] = [ value ]
454 # If still processing BaseLayout
455 if self.baseLayoutEnabled:
456 self.baseLayout[ key ] = value
457 # Set for the current layer
459 self.layerVariables[ self.currentLayer ][ key ] = value
461 # File context variables
462 self.fileVariables[ self.currentFile ][ key ] = value