changeset 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
files Resources/CodeGeneration/stonegentool.py
diffstat 1 files changed, 123 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/Resources/CodeGeneration/stonegentool.py	Tue Feb 12 13:06:24 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool.py	Tue Feb 12 16:49:28 2019 +0100
@@ -1,4 +1,4 @@
-from __future__ import print_function
+from typing import List,Dict
 import sys, json
 
 def LoadSchema(file_path : str):
@@ -7,16 +7,22 @@
     return obj
 
 class Type:
-  def __init__(self, canonicalTypeName:str, dependentTypes:str):
+  def __init__(self, canonicalTypeName:str, kind:str):
+    allowedTypeKinds = ["primitive","enum","struct","collection"]
     """dependent type is the list of canonical types this type depends on.
        For instance, vector<map<string,int32>> depends on map<string,int32>
        that, in turn, depends on string and int32 that, in turn, depend on
        nothing"""
     self.canonicalTypeName = canonicalTypeName
+    assert(kind in allowedTypeKinds)
+
+  def setDependentTypes(self, dependentTypes:List[Type]) -> None:
     self.dependentTypes = dependentTypes
-  def getDependentTypes(self):
+
+  def getDependentTypes(self) -> List[Type]:
     return self.dependentTypes
-  def getCppTypeName(self):
+
+  def getCppTypeName(self) -> str:
     # C++: prefix map vector and string with std::map, std::vector and std::string
     # replace int32 by int32_t
     # replace float32 by float
@@ -27,7 +33,8 @@
     retVal : str = self.canonicalTypeName.replace("float32","float")
     retVal : str = self.canonicalTypeName.replace("float64","double")
     return retVal
-  def getTypeScriptTypeName(self):
+
+  def getTypeScriptTypeName(self) -> str:
     # TS: replace vector with Array and map with Map
     # string remains string
     # replace int32 by number
@@ -43,24 +50,121 @@
 
 class Schema:
   def __init__(self, root_prefix : str, defined_types : List[Type]):
-    self.root_prefix : str = root_prefix
-    self.defined_types : str = defined_types
+    self.rootName : str = root_prefix
+    self.definedTypes : str = defined_types
+
+def CheckTypeSchema(definedType : Dict) -> None:
+  allowedDefinedTypeKinds = ["enum","struct"]
+  if not definedType.has_key('name'):
+    raise Exception("type lacks the 'name' key")
+  name = definedType['name']
+  if not definedType.has_key('kind'):
+    raise Exception(f"type {name} lacks the 'kind' key")
+  kind = definedType['kind']
+  if not (kind in allowedDefinedTypeKinds):
+    raise Exception(f"type {name} : kind {kind} is not allowed. It must be one of {allowedDefinedTypeKinds}")
+  
+  if not definedType.has_key('fields'):
+    raise Exception("type {name} lacks the 'fields' key")
+
+  # generic check on all kinds of types
+  fields = definedType['fields']
+  for field in fields:
+    fieldName = field['name']
+    if not field.has_key('name'):
+      raise Exception("field in type {name} lacks the 'name' key")
 
-def ProcessSchemaPaths(inputSchemas : list[str])
+  # fields in struct must have types
+  if kind == 'struct':  
+    for field in fields:
+      fieldName = field['name']
+      if not field.has_key('type'):
+        raise Exception(f"field {fieldName} in type {name} lacks the 'type' key")
+
+def CheckSchemaSchema(schema : Dict) -> None:
+  if not schema.has_key('root_name'):
+    raise Exception("schema lacks the 'root_name' key")
+  if not schema.has_key('types'):
+    raise Exception("schema lacks the 'types' key")
+  for definedType in schema['types']:
+    CheckTypeSchema(definedType)
+
+def CreateAndCacheTypeObject(allTypes : Dict[str,Type], typeDict : Dict) -> None:
+  """This does not set the dependentTypes field"""
+  typeName : str = typeDict['name']
+  if allTypes.has_key(typeName):
+    raise Exception(f'Type {name} is defined more than once!')
+  else:
+    typeObject = Type(typeName, typeDict['kind'])
+    allTypes[typeName] = typeObject
 
 
-def ProcessSchemas(schemaPaths):
-  schemas = []
-  for schemaPath in schemaPaths:
-    schemas.append(LoadSchema(schemaPath))
-  return schemas
+def ParseTemplateType(typeName) -> (bool,str,str):
+  """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", then 
+  it returns (true,"SOMETHING","SOME<THING,EL<SE>>")
+  otherwise it returns (false,"","")"""
+  
+
+
+
+def GetCachedTypeObject(allTypes : Dict[str,Type], typeName : str) -> Type:
+  if allTypes.has_key('typeName')
+    return allTypes['typeName']
+  # if we reach this point, it means the type is NOT a struct or an enum.
+  # it is another (non directly user-defined) type that we must parse and create
+  # let's do it
+
+
+def ComputeTopTypeTree(allTypes : Dict[str,Type], typeDict : Dict) -> None:
+  """Now that all the types we might be referring to are known (either structs
+  and enums already created OR primitive types OR collections), we can create
+  the whole dependency tree"""
+  
+  if typeDict['kind'] == 'struct':
+    typeName : str = typeDict['name']
+    typeObject : Type = allTypes[typeName]
+    typeFields : List[Dict] = typeDict['fields']
+    dependentTypes = []
+    for typeField : Dict in typeFields:
+      typeFieldObject = CreateTypeObject(allTypes, typeField['name'])
+      dependentTypes.append(typeFieldObject)
+  elif typeDict['kind'] == 'enum':
+    # the enum objects are already created and have no dependencies
+  else:
+    raise Exception("Internal error: ComputeTopTypeTree() can only be called on the defined types (enums and structs)")
+
+  
+
+
+def ProcessSchema(schema : dict) -> None:
+  CheckSchemaSchema(schema)
+  rootName : str = schema['root_name']
+  definedTypes : list = schema['types']
+
+  # gather defined types
+  allTypes : Dict[str,Type] = {}
+
+  # gather all defined types (first pass) : structs and enums
+  # we do not parse the subtypes now as to not mandate
+  # a particular declaration order
+  for definedType in definedTypes:
+    CreateAndCacheTypeObject(allTypes,definedType)
+
+  # now we have objects for all the defined types
+  # let's build the whole type dependency tree
+  for definedType in definedTypes:
+    ComputeTypeTree(allTypes,definedType)
+
+
+
+
 
 if __name__ == '__main__':
   import argparse
   parser = argparse.ArgumentParser(usage = """stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas
        EXAMPLE: python command_gen.py -o "generated_files/" "mainSchema.json,App Specific Commands.json" """)
-  parser.add_argument("input_schemas", type=str,
-                      help = "one or more schema files, as a comma-separated list of paths")
+  parser.add_argument("input_schema", type=str,
+                      help = "path to the schema file")
   parser.add_argument("-o", "--out_dir", type=str, default=".", 
                       help = """path of the directory where the files 
                                 will be generated. Default is current
@@ -71,15 +175,14 @@
                                 mode""")
 
   args = parser.parse_args()
-  inputSchemas = args.input_schemas.split(",")
+  inputSchemaFilename = args.input_schema
   outDir = args.out_dir
 
-  print("input schemas = " + str(inputSchemas))
+  print("input schema = " + str(inputSchemaFilename))
   print("out dir = " + str(outDir))
 
-  ProcessSchemaPaths(inputSchemas)
-
-
+  ProcessSchema(LoadSchema(inputSchemaFilename))
+  
 
 ###################
 ##     ATTIC     ##