changeset 489:f6b7f113cf27 bgo-commands-codegen

Ongoing work on code generation
author bgo-osimis
date Mon, 18 Feb 2019 07:46:59 +0100
parents 8e40355a172b
children 6470248790db
files Resources/CodeGeneration/README.md Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py
diffstat 3 files changed, 230 insertions(+), 162 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/README.md	Mon Feb 18 07:46:59 2019 +0100
@@ -0,0 +1,24 @@
+class TestMessage {
+    s1: string;
+    s2: Array<string>;
+    s3: Array<Array<string>>;
+    s4: Map<string, number>;
+    s5: Map<number, Array<string>>;
+    s6: Color;
+    s7: boolean;
+}
+
+-->
+
+{"s2":["toto","toto2","toto3"],"s4":{"toto":1999,"tatata":1999},"s6":0,"s7":true}
+
+(absent fields weren't specified)
+
+
+type:B
+value:{"someAs":[{...},{},{}]}.........................
+Deserialize
+
+jsonValue
+
+
--- a/Resources/CodeGeneration/stonegentool.py	Fri Feb 15 14:30:26 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool.py	Mon Feb 18 07:46:59 2019 +0100
@@ -2,18 +2,18 @@
 import re
 import sys
 from typing import (
-        Any,
-        Dict,
-        Generator,
-        Iterable,
-        Iterator,
-        List,
-        Match,
-        Optional,
-        Tuple,
-        Union,
-        cast,
-    )
+    Any,
+    Dict,
+    Generator,
+    Iterable,
+    Iterator,
+    List,
+    Match,
+    Optional,
+    Tuple,
+    Union,
+    cast,
+)
 from io import StringIO
 import time
 
@@ -22,22 +22,33 @@
 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 """
 
+
 class GeneratedCode:
-  def __init__(self):
-    self.cppPreamble = StringIO() # file-wide preamble (#include directives, comment...)
-    self.cppEnums = StringIO()
-    self.cppStructs = StringIO()
-    self.cppDispatcher = StringIO()
-    self.cppHandler = StringIO()
+    def __init__(self):
+
+        # file-wide preamble (#include directives, comment...)
+        self.cppPreamble = StringIO()
+
+        self.cppEnums = StringIO()
+        self.cppStructs = StringIO()
+        self.cppDispatcher = StringIO()
+        self.cppHandler = StringIO()
 
-    self.tsPreamble = StringIO() # file-wide preamble (module directives, comment...)
-    self.tsEnums = StringIO()
-    self.tsStructs = StringIO()
-    self.tsDispatcher = StringIO()
-    self.tsHandler = StringIO()
+        # file-wide preamble (module directives, comment...)
+        self.tsPreamble = StringIO()
+
+        self.tsEnums = StringIO()
+        self.tsStructs = StringIO()
+        self.tsDispatcher = StringIO()
+        self.tsHandler = StringIO()
 
-  def FlattenToFiles(self,outputDir: str):
-    raise NotImplementedError()
+    def FlattenToFiles(self, outputDir: str):
+        raise NotImplementedError()
+
+
+raise Exception("""
+$$$$TODO check field names are unique
+""")
 
 class JsonHelpers:
     """A set of utilities to perform JSON operations"""
@@ -105,6 +116,7 @@
     retVal = retVal.replace("float64", "double")
     return retVal
 
+
 def GetTypeScriptTypeNameFromCanonical(canonicalTypeName: str) -> str:
     # TS: replace vector with Array and map with Map
     # string remains string
@@ -170,16 +182,6 @@
         CheckTypeSchema(definedType)
 
 
-# def CreateAndCacheTypeObject(allTypes : Dict[str,Type], typeDict : Dict)  -> None:
-#   """This does not set the dependentTypes field"""
-#   typeName : str = typeDict['name']
-#   if typeName in allTypes:
-#     raise Exception(f'Type {typeName} is defined more than once!')
-#   else:
-#     typeObject = Type(typeName, typeDict['kind'])
-#     allTypes[typeName] = typeObject
-
-
 def EatToken(sentence: str) -> Tuple[str, str]:
     """splits "A,B,C" into "A" and "B,C" where A, B and C are type names
   (including templates) like "int32", "TotoTutu", or 
@@ -241,9 +243,9 @@
         return (False, "", [])
     else:
         m = cast(Match[str], matches)
-        assert(len(m.groups()) == 2)
-        # we need to split with the commas that are outside of the defined types
-        # simply splitting at commas won't work
+        assert len(m.groups()) == 2
+        # we need to split with the commas that are outside of the
+        # defined types. Simply splitting at commas won't work
         listOfDependentTypes = SplitListOfTypes(m.group(2))
         return (True, m.group(1), listOfDependentTypes)
 
@@ -268,156 +270,207 @@
     structTypes: Dict[str, Dict],
     typeName: str,
 ) -> None:
-    if typeName in ancestors:
-        raise Exception(
-            f"Cyclic dependency chain found: the last of {ancestors} "
-            + f"depends on {typeName} that is already in the list."
+  if typeName in ancestors:
+    raise Exception(
+      f"Cyclic dependency chain found: the last of {ancestors} "
+      + f"depends on {typeName} that is already in the list."
+    )
+
+  if not (typeName in genOrderQueue):
+    # 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!
+    (isTemplate, _, dependentTypeNames) = ParseTemplateType(typeName)
+    if isTemplate:
+      for dependentTypeName in dependentTypeNames:
+        # childAncestors = ancestors.copy()  NO TEMPLATE ANCESTOR!!!
+        # childAncestors.append(typeName)
+        ProcessTypeTree(
+            ancestors, genOrderQueue, structTypes, dependentTypeName
+        )
+    else:
+      if typeName in structTypes:
+        ProcessStructType_DepthFirstRecursive(
+            genOrderQueue, structTypes, structTypes[typeName]
         )
 
-    if not (typeName in genOrderQueue):
-        # 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!
-        (isTemplate, _, dependentTypeNames) = ParseTemplateType(typeName)
-        if isTemplate:
-            for dependentTypeName in dependentTypeNames:
-                # childAncestors = ancestors.copy()  NO TEMPLATE ANCESTOR!!!
-                # childAncestors.append(typeName)
-                ProcessTypeTree(
-                    ancestors, genOrderQueue, structTypes, dependentTypeName
-                )
-        else:
-            if typeName in structTypes:
-                ProcessStructType_DepthFirstRecursive(
-                    genOrderQueue, structTypes, structTypes[typeName]
-                )
+def ProcessStructType_DepthFirstRecursive(genOrderQueue: List[str], \
+  structTypes: Dict[str, Dict], typeDict: Dict) -> None:
+  # let's generate the code according to the
+  typeName: str = typeDict["name"]
+  if typeDict["kind"] != "struct":
+    raise Exception(
+        f"Unexpected kind '{typeDict['kind']}' for " + "type '{typeName}'"
+    )
+  typeFields: List[Dict] = typeDict["fields"]
+  for typeField in typeFields:
+    ancestors = [typeName]
+    ProcessTypeTree(ancestors, genOrderQueue, structTypes, typeField["type"])
+  # now we're pretty sure our dependencies have been processed,
+  # we can start marking our code for generation (it might already have
+  # been done if someone referenced us earlier)
+  if not typeName in genOrderQueue:
+    genOrderQueue.append(typeName)
 
-def ProcessStructType_DepthFirstRecursive(
-    genOrderQueue: List[str], structTypes: Dict[str, Dict], typeDict: Dict) -> None:
-    # let's generate the code according to the
-    typeName: str = typeDict["name"]
-    if typeDict["kind"] != "struct":
-        raise Exception(
-            f"Unexpected kind '{typeDict['kind']}' for " + "type '{typeName}'"
-        )
-    typeFields: List[Dict] = typeDict["fields"]
-    for typeField in typeFields:
-        ancestors = [typeName]
-        ProcessTypeTree(ancestors, genOrderQueue, structTypes, typeField["type"])
-    # now we're pretty sure our dependencies have been processed,
-    # we can start marking our code for generation (it might already have
-    # been done if someone referenced us earlier)
-    if not typeName in genOrderQueue:
-      genOrderQueue.append(typeName)
+def ProcessEnumerationType(outputStreams: GeneratedCode, typeDict: Dict) -> None:
+  tsText: StringIO = StringIO()
+  cppText: StringIO = StringIO()
 
-def ProcessEnumerationType(
-  outputStreams: GeneratedCode, typeDict: Dict) -> None:
-  tsText : StringIO = StringIO()
-  cppText : StringIO = StringIO()
-  
-  tsText.write("enum %s\n" % typeDict['name'])
+  tsText.write("enum %s\n" % typeDict["name"])
   tsText.write("{\n")
 
-  cppText.write("enum %s\n" % typeDict['name'])
+  cppText.write("enum %s\n" % typeDict["name"])
   cppText.write("{\n")
 
-  for i in range(len(typeDict['fields'])):
-    field = typeDict['fields'][i]
-    name = field['name']
+  for i in range(len(typeDict["fields"])):
+    field = typeDict["fields"][i]
+    name = field["name"]
 
     tsText.write("    %s" % name)
-    if i < len(typeDict['fields'])-1:
-      tsText.write(",")
+    if i < len(typeDict["fields"]) - 1:
+        tsText.write(",")
     tsText.write("\n")
 
     cppText.write("    %s" % name)
-    if i < len(typeDict['fields'])-1:
-      cppText.write(",")
+    if i < len(typeDict["fields"]) - 1:
+        cppText.write(",")
     cppText.write("\n")
-  
-  tsText.write("};\n\n")
-  cppText.write("};\n\n")
+
+    tsText.write("};\n\n")
+    cppText.write("};\n\n")
 
   outputStreams.tsEnums.write(tsText.getvalue())
   outputStreams.cppEnums.write(cppText.getvalue())
 
-def ProcessStructType(
-  outputStreams: GeneratedCode, typeDict) -> None:
-  tsText : StringIO = StringIO()
-  cppText : StringIO = StringIO()
-  
-  tsText.write("class %s\n" % typeDict['name'])
+def GetSerializationCode(typeName: str,valueName: str, tempName: str)
+  if IsPrimitiveType(typeName):
+    """
+        json::Value val(objectTypeInt...)
+        val.setValue(valueName) <--- val
+    """
+  elif IsArray(typeName)
+    """
+      {
+        json::Value val(objectTypeArray...)
+        for(size_t i = 0; i < {fieldName}.size(); ++i)
+        {
+          json::Value val(objectTypeArray...)
+        }
+        val.setValue(valueName)
+        // <--- the calling code will insert collection/field writing here,
+        // like "parent.set("{fieldName}",val) or parent.append(val)
+        $collectValue  
+      }
+    """
+
+
+
+
+def ProcessStructType(outputStreams: GeneratedCode, typeDict) -> None:
+  tsText: StringIO = StringIO()
+  cppText: StringIO = StringIO()
+
+  tsText.write("class %s\n" % typeDict["name"])
   tsText.write("{\n")
 
-  cppText.write("struct %s\n" % typeDict['name'])
+  cppText.write("struct %s\n" % typeDict["name"])
   cppText.write("{\n")
 
-  for i in range(len(typeDict['fields'])):
-    field = typeDict['fields'][i]
-    name = field['name']
-    tsType = GetTypeScriptTypeNameFromCanonical(field['type'])
+  """
+  
+  GenerateSerializationCode(typeName,valueName)
+
+  primitives:
+  -----------
+  int
+    jsonValue val(objectInt);
+    val.setValue("$name")
+    parent.add(("$name",$name)
+  double
+    ...
+  string
+    ...
+
+  collections:
+  -----------
+  dict { }
+
+
+
+
+
+  serializeValue()
+  """
+
+  for i in range(len(typeDict["fields"])):
+    field = typeDict["fields"][i]
+    name = field["name"]
+    tsType = GetTypeScriptTypeNameFromCanonical(field["type"])
     tsText.write("    public %s %s;\n" % (tsType, name))
-    cppType = GetCppTypeNameFromCanonical(field['type'])
+    cppType = GetCppTypeNameFromCanonical(field["type"])
     cppText.write("    %s %s;\n" % (cppType, name))
-  
+
   tsText.write("};\n\n")
   cppText.write("};\n\n")
 
   outputStreams.tsStructs.write(tsText.getvalue())
   outputStreams.cppStructs.write(cppText.getvalue())
 
+
 def WritePreambles(rootName: str, outputStreams: GeneratedCode) -> None:
-    outputStreams.cppPreamble.write("""// autogenerated by stonegentool on %s for module %s
+  outputStreams.cppPreamble.write(
+"""// autogenerated by stonegentool on %s for module %s
 #include <cstdint>
 #include <string>
 #include <vector>
 #include <map>
 
-""" % (time.ctime(),rootName))
+""" % (time.ctime(), rootName))
 
-    outputStreams.tsPreamble.write("""// autogenerated by stonegentool on %s for module %s
-""" % (time.ctime(),rootName))
+  outputStreams.tsPreamble.write(
+        """// autogenerated by stonegentool on %s for module %s
+""" % (time.ctime(), rootName))
+
 
 def ProcessSchema(schema: dict) -> Tuple[str, GeneratedCode, List[str]]:
-    CheckSchemaSchema(schema)
-    rootName: str = schema["root_name"]
-    definedTypes: list = schema["types"]
+  CheckSchemaSchema(schema)
+  rootName: str = schema["root_name"]
+  definedTypes: list = schema["types"]
 
-    # this will be filled with the generation queue. That is, the type
-    # names in the order where they must be defined.
-    genOrderQueue: List = []
+  # this will be filled with the generation queue. That is, the type
+  # names in the order where they must be defined.
+  genOrderQueue: List = []
 
-    # the struct names are mapped to their JSON dictionary
-    structTypes: Dict[str, Dict] = {}
+  # the struct names are mapped to their JSON dictionary
+  structTypes: Dict[str, Dict] = {}
 
-    outputStreams : GeneratedCode = GeneratedCode()
+  outputStreams: GeneratedCode = GeneratedCode()
 
-    WritePreambles(rootName, outputStreams)
+  WritePreambles(rootName, outputStreams)
 
-    # the order here is the generation order
-    for definedType in definedTypes:
-        if definedType["kind"] == "enum":
-            ProcessEnumerationType(outputStreams, definedType)
+  # the order here is the generation order
+  for definedType in definedTypes:
+    if definedType["kind"] == "enum":
+      ProcessEnumerationType(outputStreams, definedType)
 
-    for definedType in definedTypes:
-        if definedType["kind"] == "struct":
-            structTypes[definedType["name"]] = definedType
+  for definedType in definedTypes:
+    if definedType["kind"] == "struct":
+      structTypes[definedType["name"]] = definedType
 
     # the order here is NOT the generation order: the types
     # will be processed according to their dependency graph
     for definedType in definedTypes:
-        if definedType["kind"] == "struct":
-            ProcessStructType_DepthFirstRecursive(
-                genOrderQueue, structTypes, definedType
-            )
+      if definedType["kind"] == "struct":
+        ProcessStructType_DepthFirstRecursive(
+          genOrderQueue, structTypes, definedType
+        )
 
     for i in range(len(genOrderQueue)):
-        typeName = genOrderQueue[i]
-        typeDict = structTypes[typeName]
-        ProcessStructType(outputStreams, typeDict)
+      typeName = genOrderQueue[i]
+      typeDict = structTypes[typeName]
+      ProcessStructType(outputStreams, typeDict)
 
-    return (rootName, outputStreams, genOrderQueue)
-
+  return (rootName, outputStreams, genOrderQueue)
 
 
 def WriteStreamsToFiles(rootName: str, outputStreams: Dict[str, StringIO]) -> None:
@@ -427,28 +480,28 @@
   import argparse
 
   parser = argparse.ArgumentParser(
-      usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schemas
-      EXAMPLE: python command_gen.py -o "generated_files/" """
+    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_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
-                              working folder""",
+    "-o",
+    "--out_dir",
+    type=str,
+    default=".",
+    help="""path of the directory where the files 
+                          will be generated. Default is current
+                          working folder""",
   )
   parser.add_argument(
-      "-v",
-      "--verbosity",
-      action="count",
-      default=0,
-      help="""increase output verbosity (0 == errors 
-                              only, 1 == some verbosity, 2 == nerd
-                              mode""",
+    "-v",
+    "--verbosity",
+    action="count",
+    default=0,
+    help="""increase output verbosity (0 == errors 
+                          only, 1 == some verbosity, 2 == nerd
+                          mode""",
   )
 
   args = parser.parse_args()
--- a/Resources/CodeGeneration/stonegentool_test.py	Fri Feb 15 14:30:26 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool_test.py	Mon Feb 18 07:46:59 2019 +0100
@@ -102,7 +102,7 @@
     self.assertEqual("B",genOrderQueue[1])
     self.assertEqual("C",genOrderQueue[2])
 
-  def test_GenerateTypeScriptEnumeration(self):
+  def test_GeneratePreambleEnumerationAndStructs(self):
     fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
     obj = LoadSchema(fn)
     (_,outputStreams,_) = ProcessSchema(obj)
@@ -202,15 +202,6 @@
     self.assertEqual(cppEnumsRef,outputStreams.cppEnums.getvalue())
     self.assertEqual(cppStructsRef,outputStreams.cppStructs.getvalue())
 
-  def test_GenerateCppEnumeration(self):
-    pass
-
-  def test_GenerateTypeScriptClasses(self):
-    pass
-
-  def test_GenerateCppClasses(self):
-    pass
-
   def test_GenerateTypeScriptHandlerInterface(self):
     pass