Mercurial > hg > orthanc-stone
comparison Resources/CodeGeneration/stonegentool.py @ 470:db093eb6b29d bgo-commands-codegen
Ongoing code generation tool
author | bgo-osimis |
---|---|
date | Tue, 12 Feb 2019 16:49:28 +0100 |
parents | 52549faf47ba |
children | 125c19b294e3 |
comparison
equal
deleted
inserted
replaced
469:52549faf47ba | 470:db093eb6b29d |
---|---|
1 from __future__ import print_function | 1 from typing import List,Dict |
2 import sys, json | 2 import sys, json |
3 | 3 |
4 def LoadSchema(file_path : str): | 4 def LoadSchema(file_path : str): |
5 with open(file_path, 'r') as fp: | 5 with open(file_path, 'r') as fp: |
6 obj = json.load(fp) | 6 obj = json.load(fp) |
7 return obj | 7 return obj |
8 | 8 |
9 class Type: | 9 class Type: |
10 def __init__(self, canonicalTypeName:str, dependentTypes:str): | 10 def __init__(self, canonicalTypeName:str, kind:str): |
11 allowedTypeKinds = ["primitive","enum","struct","collection"] | |
11 """dependent type is the list of canonical types this type depends on. | 12 """dependent type is the list of canonical types this type depends on. |
12 For instance, vector<map<string,int32>> depends on map<string,int32> | 13 For instance, vector<map<string,int32>> depends on map<string,int32> |
13 that, in turn, depends on string and int32 that, in turn, depend on | 14 that, in turn, depends on string and int32 that, in turn, depend on |
14 nothing""" | 15 nothing""" |
15 self.canonicalTypeName = canonicalTypeName | 16 self.canonicalTypeName = canonicalTypeName |
17 assert(kind in allowedTypeKinds) | |
18 | |
19 def setDependentTypes(self, dependentTypes:List[Type]) -> None: | |
16 self.dependentTypes = dependentTypes | 20 self.dependentTypes = dependentTypes |
17 def getDependentTypes(self): | 21 |
22 def getDependentTypes(self) -> List[Type]: | |
18 return self.dependentTypes | 23 return self.dependentTypes |
19 def getCppTypeName(self): | 24 |
25 def getCppTypeName(self) -> str: | |
20 # C++: prefix map vector and string with std::map, std::vector and std::string | 26 # C++: prefix map vector and string with std::map, std::vector and std::string |
21 # replace int32 by int32_t | 27 # replace int32 by int32_t |
22 # replace float32 by float | 28 # replace float32 by float |
23 # replace float64 by double | 29 # replace float64 by double |
24 retVal : str = self.canonicalTypeName.replace("map","std::map") | 30 retVal : str = self.canonicalTypeName.replace("map","std::map") |
25 retVal : str = self.canonicalTypeName.replace("vector","std::vector") | 31 retVal : str = self.canonicalTypeName.replace("vector","std::vector") |
26 retVal : str = self.canonicalTypeName.replace("int32","int32_t") | 32 retVal : str = self.canonicalTypeName.replace("int32","int32_t") |
27 retVal : str = self.canonicalTypeName.replace("float32","float") | 33 retVal : str = self.canonicalTypeName.replace("float32","float") |
28 retVal : str = self.canonicalTypeName.replace("float64","double") | 34 retVal : str = self.canonicalTypeName.replace("float64","double") |
29 return retVal | 35 return retVal |
30 def getTypeScriptTypeName(self): | 36 |
37 def getTypeScriptTypeName(self) -> str: | |
31 # TS: replace vector with Array and map with Map | 38 # TS: replace vector with Array and map with Map |
32 # string remains string | 39 # string remains string |
33 # replace int32 by number | 40 # replace int32 by number |
34 # replace float32 by number | 41 # replace float32 by number |
35 # replace float64 by number | 42 # replace float64 by number |
41 retVal : str = self.canonicalTypeName.replace("bool","boolean") | 48 retVal : str = self.canonicalTypeName.replace("bool","boolean") |
42 return retVal | 49 return retVal |
43 | 50 |
44 class Schema: | 51 class Schema: |
45 def __init__(self, root_prefix : str, defined_types : List[Type]): | 52 def __init__(self, root_prefix : str, defined_types : List[Type]): |
46 self.root_prefix : str = root_prefix | 53 self.rootName : str = root_prefix |
47 self.defined_types : str = defined_types | 54 self.definedTypes : str = defined_types |
48 | 55 |
49 def ProcessSchemaPaths(inputSchemas : list[str]) | 56 def CheckTypeSchema(definedType : Dict) -> None: |
57 allowedDefinedTypeKinds = ["enum","struct"] | |
58 if not definedType.has_key('name'): | |
59 raise Exception("type lacks the 'name' key") | |
60 name = definedType['name'] | |
61 if not definedType.has_key('kind'): | |
62 raise Exception(f"type {name} lacks the 'kind' key") | |
63 kind = definedType['kind'] | |
64 if not (kind in allowedDefinedTypeKinds): | |
65 raise Exception(f"type {name} : kind {kind} is not allowed. It must be one of {allowedDefinedTypeKinds}") | |
66 | |
67 if not definedType.has_key('fields'): | |
68 raise Exception("type {name} lacks the 'fields' key") | |
69 | |
70 # generic check on all kinds of types | |
71 fields = definedType['fields'] | |
72 for field in fields: | |
73 fieldName = field['name'] | |
74 if not field.has_key('name'): | |
75 raise Exception("field in type {name} lacks the 'name' key") | |
76 | |
77 # fields in struct must have types | |
78 if kind == 'struct': | |
79 for field in fields: | |
80 fieldName = field['name'] | |
81 if not field.has_key('type'): | |
82 raise Exception(f"field {fieldName} in type {name} lacks the 'type' key") | |
83 | |
84 def CheckSchemaSchema(schema : Dict) -> None: | |
85 if not schema.has_key('root_name'): | |
86 raise Exception("schema lacks the 'root_name' key") | |
87 if not schema.has_key('types'): | |
88 raise Exception("schema lacks the 'types' key") | |
89 for definedType in schema['types']: | |
90 CheckTypeSchema(definedType) | |
91 | |
92 def CreateAndCacheTypeObject(allTypes : Dict[str,Type], typeDict : Dict) -> None: | |
93 """This does not set the dependentTypes field""" | |
94 typeName : str = typeDict['name'] | |
95 if allTypes.has_key(typeName): | |
96 raise Exception(f'Type {name} is defined more than once!') | |
97 else: | |
98 typeObject = Type(typeName, typeDict['kind']) | |
99 allTypes[typeName] = typeObject | |
50 | 100 |
51 | 101 |
52 def ProcessSchemas(schemaPaths): | 102 def ParseTemplateType(typeName) -> (bool,str,str): |
53 schemas = [] | 103 """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", then |
54 for schemaPath in schemaPaths: | 104 it returns (true,"SOMETHING","SOME<THING,EL<SE>>") |
55 schemas.append(LoadSchema(schemaPath)) | 105 otherwise it returns (false,"","")""" |
56 return schemas | 106 |
107 | |
108 | |
109 | |
110 def GetCachedTypeObject(allTypes : Dict[str,Type], typeName : str) -> Type: | |
111 if allTypes.has_key('typeName') | |
112 return allTypes['typeName'] | |
113 # if we reach this point, it means the type is NOT a struct or an enum. | |
114 # it is another (non directly user-defined) type that we must parse and create | |
115 # let's do it | |
116 | |
117 | |
118 def ComputeTopTypeTree(allTypes : Dict[str,Type], typeDict : Dict) -> None: | |
119 """Now that all the types we might be referring to are known (either structs | |
120 and enums already created OR primitive types OR collections), we can create | |
121 the whole dependency tree""" | |
122 | |
123 if typeDict['kind'] == 'struct': | |
124 typeName : str = typeDict['name'] | |
125 typeObject : Type = allTypes[typeName] | |
126 typeFields : List[Dict] = typeDict['fields'] | |
127 dependentTypes = [] | |
128 for typeField : Dict in typeFields: | |
129 typeFieldObject = CreateTypeObject(allTypes, typeField['name']) | |
130 dependentTypes.append(typeFieldObject) | |
131 elif typeDict['kind'] == 'enum': | |
132 # the enum objects are already created and have no dependencies | |
133 else: | |
134 raise Exception("Internal error: ComputeTopTypeTree() can only be called on the defined types (enums and structs)") | |
135 | |
136 | |
137 | |
138 | |
139 def ProcessSchema(schema : dict) -> None: | |
140 CheckSchemaSchema(schema) | |
141 rootName : str = schema['root_name'] | |
142 definedTypes : list = schema['types'] | |
143 | |
144 # gather defined types | |
145 allTypes : Dict[str,Type] = {} | |
146 | |
147 # gather all defined types (first pass) : structs and enums | |
148 # we do not parse the subtypes now as to not mandate | |
149 # a particular declaration order | |
150 for definedType in definedTypes: | |
151 CreateAndCacheTypeObject(allTypes,definedType) | |
152 | |
153 # now we have objects for all the defined types | |
154 # let's build the whole type dependency tree | |
155 for definedType in definedTypes: | |
156 ComputeTypeTree(allTypes,definedType) | |
157 | |
158 | |
159 | |
160 | |
57 | 161 |
58 if __name__ == '__main__': | 162 if __name__ == '__main__': |
59 import argparse | 163 import argparse |
60 parser = argparse.ArgumentParser(usage = """stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas | 164 parser = argparse.ArgumentParser(usage = """stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas |
61 EXAMPLE: python command_gen.py -o "generated_files/" "mainSchema.json,App Specific Commands.json" """) | 165 EXAMPLE: python command_gen.py -o "generated_files/" "mainSchema.json,App Specific Commands.json" """) |
62 parser.add_argument("input_schemas", type=str, | 166 parser.add_argument("input_schema", type=str, |
63 help = "one or more schema files, as a comma-separated list of paths") | 167 help = "path to the schema file") |
64 parser.add_argument("-o", "--out_dir", type=str, default=".", | 168 parser.add_argument("-o", "--out_dir", type=str, default=".", |
65 help = """path of the directory where the files | 169 help = """path of the directory where the files |
66 will be generated. Default is current | 170 will be generated. Default is current |
67 working folder""") | 171 working folder""") |
68 parser.add_argument("-v", "--verbosity", action="count", default=0, | 172 parser.add_argument("-v", "--verbosity", action="count", default=0, |
69 help = """increase output verbosity (0 == errors | 173 help = """increase output verbosity (0 == errors |
70 only, 1 == some verbosity, 2 == nerd | 174 only, 1 == some verbosity, 2 == nerd |
71 mode""") | 175 mode""") |
72 | 176 |
73 args = parser.parse_args() | 177 args = parser.parse_args() |
74 inputSchemas = args.input_schemas.split(",") | 178 inputSchemaFilename = args.input_schema |
75 outDir = args.out_dir | 179 outDir = args.out_dir |
76 | 180 |
77 print("input schemas = " + str(inputSchemas)) | 181 print("input schema = " + str(inputSchemaFilename)) |
78 print("out dir = " + str(outDir)) | 182 print("out dir = " + str(outDir)) |
79 | 183 |
80 ProcessSchemaPaths(inputSchemas) | 184 ProcessSchema(LoadSchema(inputSchemaFilename)) |
81 | 185 |
82 | |
83 | 186 |
84 ################### | 187 ################### |
85 ## ATTIC ## | 188 ## ATTIC ## |
86 ################### | 189 ################### |
87 | 190 |