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 capabilities dictionary and convenience functions
40 self.capabilities = dict()
42 def __getitem__( self, name ):
43 return self.capabilities[ name ]
45 def __setitem__( self, name, contents ):
46 self.capabilities[ name ] = contents
49 return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) )
52 # Total bytes needed to store arguments
53 def totalArgBytes( self, name ):
56 # Iterate over the arguments, summing the total bytes
57 for arg in self.capabilities[ name ][ 1 ]:
58 totalBytes += int( arg[ 1 ] )
62 # Name of the capability function
63 def funcName( self, name ):
64 return self.capabilities[ name ][ 0 ]
67 # Only valid while dictionary keys are not added/removed
68 def getIndex( self, name ):
69 return sorted( self.capabilities, key = self.capabilities.get ).index( name )
71 def getName( self, index ):
72 return sorted( self.capabilities, key = self.capabilities.get )[ index ]
75 return sorted( self.capabilities, key = self.capabilities.get )
79 # Container for Trigger Macro : Result Macro correlation
80 # Layer selection for generating TriggerLists
82 # Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible)
83 # Two staged list for ResultMacros:
84 # 1) USB Code/Non-converted (may contain capabilities)
91 self.macros = [ dict() ]
94 self.baseLayout = None
95 self.layerLayoutMarkers = []
97 # Correlated Macro Data
98 self.resultsIndex = dict()
99 self.triggersIndex = dict()
100 self.resultsIndexSorted = []
101 self.triggersIndexSorted = []
102 self.triggerList = []
103 self.maxScanCode = []
104 self.firstScanCode = []
106 # USBCode Assignment Cache
107 self.assignmentCache = []
109 def __repr__( self ):
110 return "{0}".format( self.macros )
112 def completeBaseLayout( self ):
113 # Copy base layout for later use when creating partial layers and add marker
114 self.baseLayout = copy.deepcopy( self.macros[ 0 ] )
115 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) ) # Not used for default layer, just simplifies coding
117 def removeUnmarked( self ):
118 # Remove all of the unmarked mappings from the partial layer
119 for trigger in self.layerLayoutMarkers[ self.layer ].keys():
120 del self.macros[ self.layer ][ trigger ]
122 def addLayer( self ):
123 # Increment layer count, and append another macros dictionary
125 self.macros.append( copy.deepcopy( self.baseLayout ) )
127 # Add a layout marker for each layer
128 self.layerLayoutMarkers.append( copy.deepcopy( self.baseLayout ) )
130 # Use for ScanCode trigger macros
131 def appendScanCode( self, trigger, result ):
132 if not trigger in self.macros[ self.layer ]:
133 self.replaceScanCode( trigger, result )
135 self.macros[ self.layer ][ trigger ].append( result )
137 # Remove the given trigger/result pair
138 def removeScanCode( self, trigger, result ):
139 # Remove all instances of the given trigger/result pair
140 while result in self.macros[ self.layer ][ trigger ]:
141 self.macros[ self.layer ][ trigger ].remove( result )
143 # Replaces the given trigger with the given result
144 # If multiple results for a given trigger, clear, then add
145 def replaceScanCode( self, trigger, result ):
146 self.macros[ self.layer ][ trigger ] = [ result ]
148 # Mark layer scan code, so it won't be removed later
149 # Also check to see if it hasn't already been removed before
150 if not self.baseLayout is None and trigger in self.layerLayoutMarkers[ self.layer ]:
151 del self.layerLayoutMarkers[ self.layer ][ trigger ]
153 # Return a list of ScanCode triggers with the given USB Code trigger
154 def lookupUSBCodes( self, usbCode ):
157 # Scan current layer for USB Codes
158 for macro in self.macros[ self.layer ].keys():
159 if usbCode in self.macros[ self.layer ][ macro ]:
160 scanCodeList.append( macro )
162 if len(scanCodeList) == 0:
163 if len(usbCode) > 1 or len(usbCode[0]) > 1:
164 for combo in usbCode:
167 scanCode = self.lookupUSBCodes(((key,),))
168 comboCodes.append(scanCode[0][0][0])
169 scanCodeList.append(tuple(code for code in comboCodes))
170 scanCodeList = [tuple(scanCodeList)]
174 # Cache USBCode Assignment
175 def cacheAssignment( self, operator, scanCode, result ):
176 self.assignmentCache.append( [ operator, scanCode, result ] )
178 # Assign cached USBCode Assignments
179 def replayCachedAssignments( self ):
180 # Iterate over each item in the assignment cache
181 for item in self.assignmentCache:
182 # Check operator, and choose the specified assignment action
185 self.appendScanCode( item[1], item[2] )
188 elif item[0] == ":-":
189 self.removeScanCode( item[1], item[2] )
193 self.replaceScanCode( item[1], item[2] )
195 # Clear assignment cache
196 self.assignmentCache = []
198 # Generate/Correlate Layers
199 def generate( self ):
200 self.generateIndices()
201 self.sortIndexLists()
202 self.generateTriggerLists()
204 # Generates Index of Results and Triggers
205 def generateIndices( self ):
206 # Iterate over every trigger result, and add to the resultsIndex and triggersIndex
207 for layer in range( 0, len( self.macros ) ):
208 for trigger in self.macros[ layer ].keys():
209 # Each trigger has a list of results
210 for result in self.macros[ layer ][ trigger ]:
211 # Only add, with an index, if result hasn't been added yet
212 if not result in self.resultsIndex:
213 self.resultsIndex[ result ] = len( self.resultsIndex )
215 # Then add a trigger for each result, if trigger hasn't been added yet
216 triggerItem = tuple( [ trigger, self.resultsIndex[ result ] ] )
217 if not triggerItem in self.triggersIndex:
218 self.triggersIndex[ triggerItem ] = len( self.triggersIndex )
220 # Sort Index Lists using the indices rather than triggers/results
221 def sortIndexLists( self ):
222 self.resultsIndexSorted = [ None ] * len( self.resultsIndex )
223 # Iterate over the resultsIndex and sort by index
224 for result in self.resultsIndex.keys():
225 self.resultsIndexSorted[ self.resultsIndex[ result ] ] = result
227 self.triggersIndexSorted = [ None ] * len( self.triggersIndex )
228 # Iterate over the triggersIndex and sort by index
229 for trigger in self.triggersIndex.keys():
230 self.triggersIndexSorted[ self.triggersIndex[ trigger ] ] = trigger
232 # Generates Trigger Lists per layer using index lists
233 def generateTriggerLists( self ):
234 for layer in range( 0, len( self.macros ) ):
235 # Set max scancode to 0xFF (255)
236 # But keep track of the actual max scancode and reduce the list size
237 self.triggerList.append( [ [] ] * 0xFF )
238 self.maxScanCode.append( 0x00 )
240 # Iterate through trigger macros to locate necessary ScanCodes and corresponding triggerIndex
241 for trigger in self.macros[ layer ].keys():
242 for variant in range( 0, len( self.macros[ layer ][ trigger ] ) ):
243 # Identify result index
244 resultIndex = self.resultsIndex[ self.macros[ layer ][ trigger ][ variant ] ]
246 # Identify trigger index
247 triggerIndex = self.triggersIndex[ tuple( [ trigger, resultIndex ] ) ]
249 # Iterate over the trigger to locate the ScanCodes
250 for sequence in trigger:
251 for combo in sequence:
252 # Append triggerIndex for each found scanCode of the Trigger List
253 # Do not re-add if triggerIndex is already in the Trigger List
254 if not triggerIndex in self.triggerList[ layer ][ combo ]:
255 # Append is working strangely with list pre-initialization
256 # Doing a 0 check replacement instead -HaaTa
257 if len( self.triggerList[ layer ][ combo ] ) == 0:
258 self.triggerList[ layer ][ combo ] = [ triggerIndex ]
260 self.triggerList[ layer ][ combo ].append( triggerIndex )
262 # Look for max Scan Code
263 if combo > self.maxScanCode[ layer ]:
264 self.maxScanCode[ layer ] = combo
266 # Shrink triggerList to actual max size
267 self.triggerList[ layer ] = self.triggerList[ layer ][ : self.maxScanCode[ layer ] + 1 ]
269 # Calculate first scan code for layer, useful for uC implementations trying to save RAM
271 for triggerList in range( 0, len( self.triggerList[ layer ] ) ):
272 firstScanCode = triggerList
274 # Break if triggerList has items
275 if len( self.triggerList[ layer ][ triggerList ] ) > 0:
277 self.firstScanCode.append( firstScanCode )
279 # Determine overall maxScanCode
280 self.overallMaxScanCode = 0x00
281 for maxVal in self.maxScanCode:
282 if maxVal > self.overallMaxScanCode:
283 self.overallMaxScanCode = maxVal
287 # Container for variables
288 # Stores three sets of variables, the overall combined set, per layer, and per file
289 def __init__( self ):
290 # Dictionaries of variables
291 self.baseLayout = dict()
292 self.fileVariables = dict()
293 self.layerVariables = [ dict() ]
294 self.overallVariables = dict()
295 self.defines = dict()
297 self.currentFile = ""
298 self.currentLayer = 0
299 self.baseLayoutEnabled = True
301 def baseLayoutFinished( self ):
302 self.baseLayoutEnabled = False
304 def setCurrentFile( self, name ):
305 # Store using filename and current layer
306 self.currentFile = name
307 self.fileVariables[ name ] = dict()
309 # If still processing BaseLayout
310 if self.baseLayoutEnabled:
311 if '*LayerFiles' in self.baseLayout.keys():
312 self.baseLayout['*LayerFiles'] += [ name ]
314 self.baseLayout['*LayerFiles'] = [ name ]
315 # Set for the current layer
317 if '*LayerFiles' in self.layerVariables[ self.currentLayer ].keys():
318 self.layerVariables[ self.currentLayer ]['*LayerFiles'] += [ name ]
320 self.layerVariables[ self.currentLayer ]['*LayerFiles'] = [ name ]
322 def incrementLayer( self ):
323 # Store using layer index
324 self.currentLayer += 1
325 self.layerVariables.append( dict() )
327 def assignVariable( self, key, value ):
328 # Overall set of variables
329 self.overallVariables[ key ] = value
331 # The Name variable is a special accumulation case
333 # BaseLayout still being processed
334 if self.baseLayoutEnabled:
335 if '*NameStack' in self.baseLayout.keys():
336 self.baseLayout['*NameStack'] += [ value ]
338 self.baseLayout['*NameStack'] = [ value ]
341 if '*NameStack' in self.layerVariables[ self.currentLayer ].keys():
342 self.layerVariables[ self.currentLayer ]['*NameStack'] += [ value ]
344 self.layerVariables[ self.currentLayer ]['*NameStack'] = [ value ]
346 # If still processing BaseLayout
347 if self.baseLayoutEnabled:
348 self.baseLayout[ key ] = value
349 # Set for the current layer
351 self.layerVariables[ self.currentLayer ][ key ] = value
353 # File context variables
354 self.fileVariables[ self.currentFile ][ key ] = value