comparison Resources/CodeGeneration/stonegentool.py @ 472:3db3289e1c25 bgo-commands-codegen

Ongoing codegen work
author bgo-osimis
date Wed, 13 Feb 2019 06:46:36 +0100
parents 125c19b294e3
children 628941d63b8c
comparison
equal deleted inserted replaced
471:125c19b294e3 472:3db3289e1c25
1 from typing import List,Dict 1 from typing import List,Dict,Set
2 import sys 2 import sys
3 import json 3 import json
4 import re 4 import re
5
6 """
7 1 2 3 4 5 6 7
8 12345678901234567890123456789012345678901234567890123456789012345678901234567890
9 """
5 10
6 def LoadSchema(file_path : str): 11 def LoadSchema(file_path : str):
7 with open(file_path, 'r') as fp: 12 with open(file_path, 'r') as fp:
8 obj = json.load(fp) 13 obj = json.load(fp)
9 return obj 14 return obj
10 15
11 class Type: 16 # class Type:
12 def __init__(self, canonicalTypeName:str, kind:str): 17 # def __init__(self, canonicalTypeName:str, kind:str):
13 allowedTypeKinds = ["primitive","enum","struct","collection"] 18 # allowedTypeKinds = ["primitive","enum","struct","collection"]
14 """dependent type is the list of canonical types this type depends on. 19 # """dependent type is the list of canonical types this type depends on.
15 For instance, vector<map<string,int32>> depends on map<string,int32> 20 # For instance, vector<map<string,int32>> depends on map<string,int32>
16 that, in turn, depends on string and int32 that, in turn, depend on 21 # that, in turn, depends on string and int32 that, in turn, depend on
17 nothing""" 22 # nothing"""
18 self.canonicalTypeName = canonicalTypeName 23 # self.canonicalTypeName = canonicalTypeName
19 assert(kind in allowedTypeKinds) 24 # assert(kind in allowedTypeKinds)
20 25
21 def setDependentTypes(self, dependentTypes:List[Type]) -> None: 26 # def setDependentTypes(self, dependentTypes:List[Type]) -> None:
22 self.dependentTypes = dependentTypes 27 # self.dependentTypes = dependentTypes
23 28
24 def getDependentTypes(self) -> List[Type]: 29 # def getDependentTypes(self) -> List[Type]:
25 return self.dependentTypes 30 # return self.dependentTypes
26 31
27 def getCppTypeName(self) -> str: 32 def GetCppTypeNameFromCanonical(canonicalTypeName : str) -> str:
28 # C++: prefix map vector and string with std::map, std::vector and std::string 33 # C++: prefix map vector and string with std::map, std::vector and
34 # std::string
29 # replace int32 by int32_t 35 # replace int32 by int32_t
30 # replace float32 by float 36 # replace float32 by float
31 # replace float64 by double 37 # replace float64 by double
32 retVal : str = self.canonicalTypeName.replace("map","std::map") 38 retVal : str = canonicalTypeName.replace("map","std::map")
33 retVal : str = self.canonicalTypeName.replace("vector","std::vector") 39 retVal : str = canonicalTypeName.replace("vector","std::vector")
34 retVal : str = self.canonicalTypeName.replace("int32","int32_t") 40 retVal : str = canonicalTypeName.replace("int32","int32_t")
35 retVal : str = self.canonicalTypeName.replace("float32","float") 41 retVal : str = canonicalTypeName.replace("float32","float")
36 retVal : str = self.canonicalTypeName.replace("float64","double") 42 retVal : str = canonicalTypeName.replace("float64","double")
37 return retVal 43 return retVal
38 44
39 def getTypeScriptTypeName(self) -> str: 45 def GetTypeScriptTypeNameFromCanonical(canonicalTypeName : str) -> str:
40 # TS: replace vector with Array and map with Map 46 # TS: replace vector with Array and map with Map
41 # string remains string 47 # string remains string
42 # replace int32 by number 48 # replace int32 by number
43 # replace float32 by number 49 # replace float32 by number
44 # replace float64 by number 50 # replace float64 by number
45 retVal : str = self.canonicalTypeName.replace("map","Map") 51 retVal : str = canonicalTypeName.replace("map","Map")
46 retVal : str = self.canonicalTypeName.replace("vector","Array") 52 retVal : str = canonicalTypeName.replace("vector","Array")
47 retVal : str = self.canonicalTypeName.replace("int32","number") 53 retVal : str = canonicalTypeName.replace("int32","number")
48 retVal : str = self.canonicalTypeName.replace("float32","number") 54 retVal : str = canonicalTypeName.replace("float32","number")
49 retVal : str = self.canonicalTypeName.replace("float64","number") 55 retVal : str = canonicalTypeName.replace("float64","number")
50 retVal : str = self.canonicalTypeName.replace("bool","boolean") 56 retVal : str = canonicalTypeName.replace("bool","boolean")
51 return retVal 57 return retVal
52 58
53 class Schema: 59 # class Schema:
54 def __init__(self, root_prefix : str, defined_types : List[Type]): 60 # def __init__(self, root_prefix : str, defined_types : List[Type]):
55 self.rootName : str = root_prefix 61 # self.rootName : str = root_prefix
56 self.definedTypes : str = defined_types 62 # self.definedTypes : str = defined_types
57 63
58 def CheckTypeSchema(definedType : Dict) -> None: 64 def CheckTypeSchema(definedType : Dict) -> None:
59 allowedDefinedTypeKinds = ["enum","struct"] 65 allowedDefinedTypeKinds = ["enum","struct"]
60 if not definedType.has_key('name'): 66 if not definedType.has_key('name'):
61 raise Exception("type lacks the 'name' key") 67 raise Exception("type lacks the 'name' key")
62 name = definedType['name'] 68 name = definedType['name']
63 if not definedType.has_key('kind'): 69 if not definedType.has_key('kind'):
64 raise Exception(f"type {name} lacks the 'kind' key") 70 raise Exception(f"type {name} lacks the 'kind' key")
65 kind = definedType['kind'] 71 kind = definedType['kind']
66 if not (kind in allowedDefinedTypeKinds): 72 if not (kind in allowedDefinedTypeKinds):
67 raise Exception(f"type {name} : kind {kind} is not allowed. It must be one of {allowedDefinedTypeKinds}") 73 raise Exception(f"type {name} : kind {kind} is not allowed. " +
74 f"It must be one of {allowedDefinedTypeKinds}")
68 75
69 if not definedType.has_key('fields'): 76 if not definedType.has_key('fields'):
70 raise Exception("type {name} lacks the 'fields' key") 77 raise Exception("type {name} lacks the 'fields' key")
71 78
72 # generic check on all kinds of types 79 # generic check on all kinds of types
79 # fields in struct must have types 86 # fields in struct must have types
80 if kind == 'struct': 87 if kind == 'struct':
81 for field in fields: 88 for field in fields:
82 fieldName = field['name'] 89 fieldName = field['name']
83 if not field.has_key('type'): 90 if not field.has_key('type'):
84 raise Exception(f"field {fieldName} in type {name} lacks the 'type' key") 91 raise Exception(f"field {fieldName} in type {name} "
92 + "lacks the 'type' key")
85 93
86 def CheckSchemaSchema(schema : Dict) -> None: 94 def CheckSchemaSchema(schema : Dict) -> None:
87 if not schema.has_key('root_name'): 95 if not schema.has_key('root_name'):
88 raise Exception("schema lacks the 'root_name' key") 96 raise Exception("schema lacks the 'root_name' key")
89 if not schema.has_key('types'): 97 if not schema.has_key('types'):
90 raise Exception("schema lacks the 'types' key") 98 raise Exception("schema lacks the 'types' key")
91 for definedType in schema['types']: 99 for definedType in schema['types']:
92 CheckTypeSchema(definedType) 100 CheckTypeSchema(definedType)
93 101
94 def CreateAndCacheTypeObject(allTypes : Dict[str,Type], typeDict : Dict) -> None: 102 # def CreateAndCacheTypeObject(allTypes : Dict[str,Type], typeDict : Dict) -> None:
95 """This does not set the dependentTypes field""" 103 # """This does not set the dependentTypes field"""
96 typeName : str = typeDict['name'] 104 # typeName : str = typeDict['name']
97 if allTypes.has_key(typeName): 105 # if allTypes.has_key(typeName):
98 raise Exception(f'Type {typeName} is defined more than once!') 106 # raise Exception(f'Type {typeName} is defined more than once!')
99 else: 107 # else:
100 typeObject = Type(typeName, typeDict['kind']) 108 # typeObject = Type(typeName, typeDict['kind'])
101 allTypes[typeName] = typeObject 109 # allTypes[typeName] = typeObject
102
103
104 110
105 def EatToken(sentence : str) -> (str,str): 111 def EatToken(sentence : str) -> (str,str):
106 """splits "A,B,C" into "A" and "B,C" where A, B and C are type names 112 """splits "A,B,C" into "A" and "B,C" where A, B and C are type names
107 (including templates) like "int32", "TotoTutu", or 113 (including templates) like "int32", "TotoTutu", or
108 "map<map<int32,vector<string>>,map<string,int32>>" """ 114 "map<map<int32,vector<string>>,map<string,int32>>" """
109 token = [] 115
110 if sentence.count('<') != sentence.count('>'): 116 if sentence.count('<') != sentence.count('>'):
111 raise Exception(f"Error in the partial template type list {sentence}. The number of < and > do not match!") 117 raise Exception(f"Error in the partial template type list {sentence}."
118 + " The number of < and > do not match!")
112 119
113 # the template level we're currently in 120 # the template level we're currently in
114 templateLevel = 0 121 templateLevel = 0
115 for i in len(sentence): 122 for i in len(sentence):
116 if (sentence[i] == ",") and (templateLevel == 0): 123 if (sentence[i] == ",") and (templateLevel == 0):
137 while stillStuffToEat: 144 while stillStuffToEat:
138 firstToken,restOfString = EatToken(restOfString) 145 firstToken,restOfString = EatToken(restOfString)
139 tokenList.append(firstToken) 146 tokenList.append(firstToken)
140 return tokenList 147 return tokenList
141 148
142 templateRegex = re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>") 149 templateRegex = \
150 re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>")
151
143 def ParseTemplateType(typeName) -> (bool,str,List[str]): 152 def ParseTemplateType(typeName) -> (bool,str,List[str]):
144 """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", then 153 """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", then
145 it returns (true,"SOMETHING","SOME<THING,EL<SE>>") 154 it returns (true,"SOMETHING","SOME<THING,EL<SE>>")
146 otherwise it returns (false,"","")""" 155 otherwise it returns (false,"","")"""
147 156
156 # we need to split with the commas that are outside of the defined types 165 # we need to split with the commas that are outside of the defined types
157 # simply splitting at commas won't work 166 # simply splitting at commas won't work
158 listOfDependentTypes = SplitListOfTypes(matches.group(2)) 167 listOfDependentTypes = SplitListOfTypes(matches.group(2))
159 return (True,matches.group(1),listOfDependentTypes) 168 return (True,matches.group(1),listOfDependentTypes)
160 169
161 def GetPrimitiveType(typeName : str) -> Type: 170
162 if allTypes.has_key(typeName): 171 # def GetPrimitiveType(typeName : str) -> Type:
163 return allTypes[typeName] 172 # if allTypes.has_key(typeName):
164 else: 173 # return allTypes[typeName]
165 primitiveTypes = ['int32', 'float32', 'float64', 'string'] 174 # else:
166 if not (typeName in primitiveTypes): 175 # primitiveTypes = ['int32', 'float32', 'float64', 'string']
167 raise Exception(f"Type {typeName} is unknown.") 176 # if not (typeName in primitiveTypes):
168 typeObject = Type(typeName,'primitive') 177 # raise Exception(f"Type {typeName} is unknown.")
169 # there are no dependent types in a primitive type --> Type object 178 # typeObject = Type(typeName,'primitive')
170 # constrution is finished at this point 179 # # there are no dependent types in a primitive type --> Type object
171 allTypes[typeName] = typeObject 180 # # constrution is finished at this point
172 return typeObject 181 # allTypes[typeName] = typeObject
182 # return typeObject
173 183
174 def ProcessTypeTree( 184 def ProcessTypeTree(
175 ancestors : List[str] 185 ancestors : List[str]
176 , generationQueue : List[str] 186 , genOrderQueue : List[str]
177 , structTypes : Dict[str,Dict], typeName : str) -> None: 187 , structTypes : Dict[str,Dict], typeName : str) -> None:
178 if typeName in ancestors: 188 if typeName in ancestors:
179 raise Exception(f"Cyclic dependency chain found: the last of {ancestors} depends on {typeName} that is already in the list.") 189 raise Exception(f"Cyclic dependency chain found: the last of {ancestors} "
180 190 + f"depends on {typeName} that is already in the list.")
181 if not (typeName in generationQueue): 191
192 if not (typeName in genOrderQueue):
182 # if we reach this point, it means the type is NOT a struct or an enum. 193 # if we reach this point, it means the type is NOT a struct or an enum.
183 # it is another (non directly user-defined) type that we must parse and create 194 # it is another (non directly user-defined) type that we must parse and
184 # let's do it 195 # create. Let's do it!
185 dependentTypes = [] 196 (isTemplate,_,parameters) = ParseTemplateType(typeName)
186 (isTemplate,templateType,parameters) = ParseTemplateType(typeName)
187 if isTemplate: 197 if isTemplate:
188 dependentTypeNames : List[str] = SplitListOfTypes(parameters) 198 dependentTypeNames : List[str] = SplitListOfTypes(parameters)
189 for dependentTypeName in dependentTypeNames: 199 for dependentTypeName in dependentTypeNames:
190 # childAncestors = ancestors.copy() NO TEMPLATE ANCESTOR!!! 200 # childAncestors = ancestors.copy() NO TEMPLATE ANCESTOR!!!
191 # childAncestors.append(typeName) 201 # childAncestors.append(typeName)
192 ProcessTypeTree(ancestors, processedTypes, structTypes, dependentTypeName) 202 ProcessTypeTree(ancestors, genOrderQueue,
203 structTypes, dependentTypeName)
193 else: 204 else:
194 if structTypes.has_key(typeName): 205 if structTypes.has_key(typeName):
195 ProcesStructType_DepthFirstRecursive(generationQueue, structTypes, 206 ProcessStructType_DepthFirstRecursive(genOrderQueue, structTypes,
196 structTypes[typeName]) 207 structTypes[typeName])
197 208
198 def ProcesStructType_DepthFirstRecursive( 209 def ProcessStructType_DepthFirstRecursive(
199 generationQueue : List[str], structTypes : Dict[str,Dict] 210 genOrderQueue : List[str], structTypes : Dict[str,Dict]
200 , typeDict : Dict) -> None: 211 , typeDict : Dict) -> None:
201 # let's generate the code according to the 212 # let's generate the code according to the
202 typeName : str = typeDict['name'] 213 typeName : str = typeDict['name']
203 if typeDict['kind'] != 'struct': 214 if typeDict['kind'] != 'struct':
204 raise Exception(f"Unexpected kind '{typeDict['kind']}' for " + 215 raise Exception(f"Unexpected kind '{typeDict['kind']}' for " +
205 "type '{typeName}'") 216 "type '{typeName}'")
206 typeFields : List[Dict] = typeDict['fields'] 217 typeFields : List[Dict] = typeDict['fields']
207 for typeField in typeFields: 218 for typeField in typeFields:
208 ancestors = [typeName] 219 ancestors = [typeName]
209 ProcessTypeTree(ancestors, generationQueue 220 ProcessTypeTree(ancestors, genOrderQueue
210 , structTypes, typeField['name']) 221 , structTypes, typeField['name'])
211 # now we're pretty sure our dependencies have been processed, 222 # now we're pretty sure our dependencies have been processed,
212 # we can start marking our code for generation 223 # we can start marking our code for generation
213 generationQueue.append(typeName) 224 genOrderQueue.append(typeName)
225
226 def ProcessEnumerationType(processedTypes, definedType) -> None:
227 print(f"About to process enumeration: {definedType['name']}")
214 228
215 def ProcessSchema(schema : dict) -> None: 229 def ProcessSchema(schema : dict) -> None:
216 CheckSchemaSchema(schema) 230 CheckSchemaSchema(schema)
217 rootName : str = schema['root_name'] 231 rootName : str = schema['root_name']
218 definedTypes : list = schema['types'] 232 definedTypes : list = schema['types']
219 233
220 # mark already processed types 234 print(f"Processing schema. rootName = f{rootName}")
221 processedTypes : set[str] = set() 235 # this will be filled with the generation queue. That is, the type
236 # names in the order where they must be defined.
237 genOrderQueue : Set = set()
222 238
223 # the struct names are mapped to their JSON dictionary 239 # the struct names are mapped to their JSON dictionary
224 structTypes : Dict[str,Dict] = {} 240 structTypes : Dict[str,Dict] = {}
225 241
226 # the order here is the generation order 242 # the order here is the generation order
227 for definedType in definedTypes: 243 for definedType in definedTypes:
228 if definedType['kind'] == 'enum': 244 if definedType['kind'] == 'enum':
229 ProcessEnumerationType(processedTypes, definedType); 245 ProcessEnumerationType(genOrderQueue, definedType)
230 246
231 # the order here is NOT the generation order: the types 247 # the order here is NOT the generation order: the types
232 # will be processed according to their dependency graph 248 # will be processed according to their dependency graph
233 for definedType in definedTypes: 249 for definedType in definedTypes:
234 if definedType['kind'] == 'struct': 250 if definedType['kind'] == 'struct':
235 structTypes[definedType['name']] = definedType 251 structTypes[definedType['name']] = definedType
236 ProcesStructType_DepthFirstRecursive(processedTypes,structTypes,definedType) 252 ProcessStructType_DepthFirstRecursive(genOrderQueue,structTypes,
253 definedType)
254
255 print(f"genOrderQueue = {genOrderQueue}")
237 256
238 if __name__ == '__main__': 257 if __name__ == '__main__':
239 import argparse 258 import argparse
240 parser = argparse.ArgumentParser(usage = """stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas 259 parser = argparse.ArgumentParser(
241 EXAMPLE: python command_gen.py -o "generated_files/" "mainSchema.json,App Specific Commands.json" """) 260 usage = """stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas
261 EXAMPLE: python command_gen.py -o "generated_files/" """
262 + """ "mainSchema.json,App Specific Commands.json" """)
242 parser.add_argument("input_schema", type=str, 263 parser.add_argument("input_schema", type=str,
243 help = "path to the schema file") 264 help = "path to the schema file")
244 parser.add_argument("-o", "--out_dir", type=str, default=".", 265 parser.add_argument("-o", "--out_dir", type=str, default=".",
245 help = """path of the directory where the files 266 help = """path of the directory where the files
246 will be generated. Default is current 267 will be generated. Default is current