changeset 491:8e7e151ef472 bgo-commands-codegen

Unit tests pass for enum generation
author bgo-osimis
date Wed, 20 Feb 2019 20:51:30 +0100
parents 6470248790db
children 6fbf2eae7c88
files Resources/CodeGeneration/playground.ts Resources/CodeGeneration/playground3.ts Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.ts Resources/CodeGeneration/test_data/test1.yaml
diffstat 6 files changed, 663 insertions(+), 405 deletions(-) [+]
line wrap: on
line diff
--- a/Resources/CodeGeneration/playground.ts	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/playground.ts	Wed Feb 20 20:51:30 2019 +0100
@@ -34,7 +34,6 @@
     printf(`About to serialize value. Type is ${valueType}`)
     printf(`About to serialize value. Type is ${typeof value}`)
     return "Choucroute";
-
   }
 
   function main():number
--- a/Resources/CodeGeneration/playground3.ts	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/playground3.ts	Wed Feb 20 20:51:30 2019 +0100
@@ -3,7 +3,7 @@
 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 */
 
-namespace VsolStuff {
+namespace VsolStuff222 {
   export enum EnumMonth0 {
     January,
     February,
@@ -17,7 +17,7 @@
     d: boolean;
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'Message1';
+      container['type'] = 'VsolStuff.Message1';
       container['value'] = this;
       return JSON.stringify(container);
     }
@@ -32,7 +32,7 @@
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'Message1';
+      container['type'] = 'VsolStuff.Message2';
       container['value'] = this;
       return JSON.stringify(container);
     }
@@ -69,7 +69,7 @@
   msg2_0.tata.push(msg1_0);
   msg2_0.tata.push(msg1_1);
   msg2_0.tutu.push("Mercadet");
-  msg2_0.tutu.push("Poisson");
+  msg2_0.tutu.push("Poisson");ing
   msg2_0.titi["44"] = "key 44";
   msg2_0.titi["45"] = "key 45";
   msg2_0.lulu["54"] = msg1_1;
--- a/Resources/CodeGeneration/stonegentool.py	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool.py	Wed Feb 20 20:51:30 2019 +0100
@@ -1,6 +1,8 @@
 import json
+import yaml
 import re
 import sys
+from jinja2 import Template
 from typing import (
     Any,
     Dict,
@@ -22,7 +24,34 @@
 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 """
 
-class GeneratedCode:
+# see https://stackoverflow.com/a/2504457/2927708
+def trim(docstring):
+    if not docstring:
+        return ''
+    # Convert tabs to spaces (following the normal Python rules)
+    # and split into a list of lines:
+    lines = docstring.expandtabs().splitlines()
+    # Determine minimum indentation (first line doesn't count):
+    indent = sys.maxsize
+    for line in lines[1:]:
+        stripped = line.lstrip()
+        if stripped:
+            indent = min(indent, len(line) - len(stripped))
+    # Remove indentation (first line is special):
+    trimmed = [lines[0].strip()]
+    if indent < sys.maxsize:
+        for line in lines[1:]:
+            trimmed.append(line[indent:].rstrip())
+    # Strip off trailing and leading blank lines:
+    while trimmed and not trimmed[-1]:
+        trimmed.pop()
+    while trimmed and not trimmed[0]:
+        trimmed.pop(0)
+    # Return a single string:
+    return '\n'.join(trimmed)
+
+
+class GenCode:
     def __init__(self):
 
         # file-wide preamble (#include directives, comment...)
@@ -45,10 +74,6 @@
         raise NotImplementedError()
 
 
-raise Exception("""
-$$$$TODO check field names are unique
-""")
-
 class JsonHelpers:
     """A set of utilities to perform JSON operations"""
 
@@ -83,13 +108,13 @@
 def LoadSchemaFromJson(filePath: str):
     return JsonHelpers.loadJsonWithComments(filePath)
 
-def GetCppTypeNameFromCanonical(canonicalTypeName: str) -> str:
+def GetCppTypenameFromCanonical(canonicalTypename: str) -> str:
     # C++: prefix map vector and string with std::map, std::vector and
     # std::string
     # replace int32 by int32_t
     # replace float32 by float
     # replace float64 by double
-    retVal: str = canonicalTypeName
+    retVal: str = canonicalTypename
     retVal = retVal.replace("map", "std::map")
     retVal = retVal.replace("vector", "std::vector")
     retVal = retVal.replace("int32", "int32_t")
@@ -97,13 +122,13 @@
     retVal = retVal.replace("float64", "double")
     return retVal
 
-def GetTypeScriptTypeNameFromCanonical(canonicalTypeName: str) -> str:
+def GetTypeScriptTypenameFromCanonical(canonicalTypename: str) -> str:
     # TS: replace vector with Array and map with Map
     # string remains string
     # replace int32 by number
     # replace float32 by number
     # replace float64 by number
-    retVal: str = canonicalTypeName
+    retVal: str = canonicalTypename
     retVal = retVal.replace("map", "Map")
     retVal = retVal.replace("vector", "Array")
     retVal = retVal.replace("int32", "number")
@@ -112,54 +137,6 @@
     retVal = retVal.replace("bool", "boolean")
     return retVal
 
-# class Schema:
-#   def __init__(self, root_prefix : str, defined_types : List[Type]):
-#     self.rootName : str = root_prefix
-#     self.definedTypes : str = defined_types
-
-def CheckTypeSchema(definedType: Dict) -> None:
-    allowedDefinedTypeKinds = ["enum", "struct"]
-    if not "name" in definedType:
-        raise Exception("type lacks the 'name' key")
-    name = definedType["name"]
-    if not "kind" in definedType:
-        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. "
-            + f"It must be one of {allowedDefinedTypeKinds}"
-        )
-
-    if not "fields" in definedType:
-        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 "name" in field:
-            raise Exception("field in type {name} lacks the 'name' key")
-
-    # fields in struct must have types
-    if kind == "struct":
-        for field in fields:
-            fieldName = field["name"]
-            if not "type" in field:
-                raise Exception(
-                    f"field {fieldName} in type {name} " + "has no 'type' key"
-                )
-
-
-def CheckSchemaSchema(schema: Dict) -> None:
-    if not "root_name" in schema:
-        raise Exception("schema lacks the 'root_name' key")
-    if not "types" in schema:
-        raise Exception("schema lacks the 'types' key")
-    for definedType in schema["types"]:
-        CheckTypeSchema(definedType)
-
-
 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 
@@ -183,7 +160,7 @@
     return (sentence, "")
 
 
-def SplitListOfTypes(typeName: str) -> List[str]:
+def SplitListOfTypes(typename: str) -> List[str]:
     """Splits something like
   vector<string>,int32,map<string,map<string,int32>> 
   in:
@@ -195,7 +172,7 @@
   """
     stillStuffToEat: bool = True
     tokenList = []
-    restOfString = typeName
+    restOfString = typename
     while stillStuffToEat:
         firstToken, restOfString = EatToken(restOfString)
         tokenList.append(firstToken)
@@ -208,16 +185,16 @@
   re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>")
 
 
-def ParseTemplateType(typeName) -> Tuple[bool, str, List[str]]:
-    """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", then 
-  it returns (true,"SOMETHING","SOME<THING,EL<SE>>")
+def ParseTemplateType(typename) -> Tuple[bool, str, List[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,"","")"""
 
     # let's remove all whitespace from the type
     # split without argument uses any whitespace string as separator
     # (space, tab, newline, return or formfeed)
-    typeName = "".join(typeName.split())
-    matches = templateRegex.match(typeName)
+    typename = "".join(typename.split())
+    matches = templateRegex.match(typename)
     if matches == None:
         return (False, "", [])
     else:
@@ -228,234 +205,220 @@
         listOfDependentTypes = SplitListOfTypes(m.group(2))
         return (True, m.group(1), listOfDependentTypes)
 
-def ProcessTypeTree(
-    ancestors: List[str],
-    genOrderQueue: List[str],
-    structTypes: Dict[str, Dict],
-    typeName: str) -> None:
-  if typeName in ancestors:
+
+def ComputeOrderFromTypeTree(
+  ancestors: List[str], 
+  genOrder: List[str], 
+  shortTypename: str, schema: Dict[str, Dict]) -> None:
+
+  if shortTypename in ancestors:
     raise Exception(
       f"Cyclic dependency chain found: the last of {ancestors} "
-      + f"depends on {typeName} that is already in the list."
+      + f"depends on {shortTypename} 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 not (shortTypename in genOrder):
+    (isTemplate, _, dependentTypenames) = ParseTemplateType(shortTypename)
     if isTemplate:
-      for dependentTypeName in dependentTypeNames:
+      # if it is a template, it HAS dependent types... They can be 
+      # anything (primitive, collection, enum, structs..). 
+      # Let's process them!
+      for dependentTypename in dependentTypenames:
         # childAncestors = ancestors.copy()  NO TEMPLATE ANCESTOR!!!
-        # childAncestors.append(typeName)
-        ProcessTypeTree(
-            ancestors, genOrderQueue, structTypes, dependentTypeName
+        # childAncestors.append(typename)
+        ComputeOrderFromTypeTree(
+            ancestors, genOrder, dependentTypename, schema
         )
     else:
-      if typeName in structTypes:
-        ProcessStructType_DepthFirstRecursive(
-            genOrderQueue, structTypes, structTypes[typeName]
-        )
+      # If it is not template, we are only interested if it is a 
+      # dependency that we must take into account in the dep graph,
+      # i.e., a struct.
+      if IsShortStructType(shortTypename, schema):
+        struct:Dict = schema[GetLongTypename(shortTypename, schema)]
+        # The keys in the struct dict are the member names
+        # The values in the struct dict are the member types
+        for field in struct.keys():
+          # we fill the chain of dependent types (starting here)
+          ancestors.append(shortTypename)
+          ComputeOrderFromTypeTree(
+            ancestors, genOrder, struct[field], schema)
+          # don't forget to restore it!
+          ancestors.pop()
+        
+        # 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 shortTypename in genOrder:
+          genOrder.append(shortTypename)
 
-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)
+# +-----------------------+
+# |   Utility functions   |
+# +-----------------------+
+
+def IsShortStructType(typename: str, schema: Dict[str, Dict]) -> bool:
+  fullStructName = "struct " + typename
+  return (fullStructName in schema)
 
-def ProcessEnumerationType(outputStreams: GeneratedCode, typeDict: Dict) -> None:
+def GetLongTypename(shortTypename: str, schema: Dict):
+  if shortTypename.startswith("enum "):
+    raise RuntimeError('shortTypename.startswith("enum "):')
+  enumName: str = "enum " + shortTypename
+  isEnum = enumName in schema
 
-  # enumeration declarations
-  tsDeclText: StringIO = StringIO()
-  tsDeclText.write("enum %s\n" % typeDict["name"])
-  tsDeclText.write("{\n")
+  if shortTypename.startswith("struct "):
+    raise RuntimeError('shortTypename.startswith("struct "):')
+  structName: str = "struct " + shortTypename
+  isStruct = ("struct " + shortTypename) in schema
 
-  cppDeclText: StringIO = StringIO()
-  cppDeclText.write("enum %s\n" % typeDict["name"])
-  cppDeclText.write("{\n")
+  if isEnum and isStruct:
+    raise RuntimeError('Enums and structs cannot have the same name')
 
-  cppToStringText: StringIO = StringIO()
-  cppToStringText.write("enum %s\n" % typeDict["name"])
-  cppToStringText.write("{\n")
+  if isEnum:
+    return enumName
+  if isStruct:
+    return structName
+
+def IsTypename(fullName: str) -> bool:
+  return (fullName.startswith("enum ") or fullName.startswith("struct "))
+
+def IsEnumType(fullName: str) -> bool:
+  return fullName.startswith("enum ")
 
-  cppFromStringText: StringIO = StringIO()
-  cppFromStringText.write("enum %s\n" % typeDict["name"])
-  cppFromStringText.write("{\n")
+def IsStructType(fullName: str) -> bool:
+  return fullName.startswith("struct ")
 
-  for i in range(len(typeDict["fields"])):
-    field = typeDict["fields"][i]
-    name = field["name"]
+def GetShortTypename(fullTypename: str) -> str:
+  if fullTypename.startswith("struct "):
+    return fullTypename[7:] 
+  elif fullTypename.startswith("enum"):
+    return fullTypename[5:] 
+  else:
+    raise RuntimeError \
+      ('fullTypename should start with either "struct " or "enum "')
 
-    tsText.write("    %s" % name)
-    if i < len(typeDict["fields"]) - 1:
-        tsText.write(",")
-    tsText.write("\n")
+def CheckSchemaSchema(schema: Dict) -> None:
+  if not "rootName" in schema:
+      raise Exception("schema lacks the 'rootName' key")
+  for name in schema.keys():
+    if (not IsEnumType(name)) and (not IsStructType(name)) and \
+      (name != 'rootName'):
+      raise RuntimeError \
+        (f'Type "{name}" should start with "enum " or "struct "')
+
+  # TODO: check enum fields are unique (in whole namespace)
+  # TODO: check struct fields are unique (in each struct)
+
+# +-----------------------+
+# | Main processing logic |
+# +-----------------------+
+
+def ComputeRequiredDeclarationOrder(schema: dict) -> List[str]:
+  # sanity check
+  CheckSchemaSchema(schema)
 
-    cppText.write("    %s" % name)
-    if i < len(typeDict["fields"]) - 1:
-        cppText.write(",")
-    cppText.write("\n")
+  # we traverse the type dependency graph and we fill a queue with
+  # the required struct types, in a bottom-up fashion, to compute
+  # the declaration order
+  # The genOrder list contains the struct full names in the order
+  # where they must be defined.
+  # We do not care about the enums here... They do not depend upon
+  # anything and we'll handle them, in their original declaration 
+  # order, at the start
+  genOrder: List = []
+  for fullName in schema.keys():
+    if IsStructType(fullName):
+      realName: str = GetShortTypename(fullName)
+      ancestors: List[str] = []
+      ComputeOrderFromTypeTree(ancestors, genOrder, realName, schema)
+  return genOrder
 
-  tsText.write("};\n\n")
-  cppText.write("};\n\n")
-
-  outputStreams.tsEnums.write(tsText.getvalue())
-  outputStreams.cppEnums.write(cppText.getvalue())
+def ProcessSchema(schema: dict, genOrder: List[str]) -> Dict:
+  # sanity check
+  CheckSchemaSchema(schema)
 
-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  
-      }
-    """
+  # let's doctor the schema to clean it up a bit
+  # order DOES NOT matter for enums, even though it's a list
+  enums: List[Dict] = []
+  for fullName in schema.keys():
+    if IsEnumType(fullName):
+      # convert "enum Toto" to "Toto"
+      typename:str = GetShortTypename(fullName)
+      enum = {}
+      enum['name'] = typename
+      assert(type(schema[fullName]) == list)
+      enum['fields'] = schema[fullName] # must be a list
+      enums.append(enum)
 
-
-
-def ProcessStructType(outputStreams: GeneratedCode, typeDict) -> None:
-  tsText: StringIO = StringIO()
-  cppText: StringIO = StringIO()
+  # now that the order has been established, we actually store\
+  # the structs in the correct order
+  # the structs are like:
+  # example = [
+  #   {
+  #     "name": "Message1",
+  #     "fields": {
+  #       "someMember":"int32",
+  #       "someOtherMember":"vector<string>"
+  #     }
+  #   },
+  #   { 
+  #     "name": "Message2",
+  #     "fields": {
+  #       "someMember":"int32",
+  #       "someOtherMember22":"vector<Message1>"
+  #     }
+  #   }
+  # ]
 
-  tsText.write("class %s\n" % typeDict["name"])
-  tsText.write("{\n")
-
-  cppText.write("struct %s\n" % typeDict["name"])
-  cppText.write("{\n")
-
-  """
+  structs: List[Dict] = []
+  for i in range(len(genOrder)):
+    # this is already the short name
+    typename = genOrder[i]
+    fieldDict = schema["struct " + typename]
+    struct = {}
+    struct['name'] = typename
+    struct['fields'] = fieldDict
+    structs.append(struct)
   
-  GenerateSerializationCode(typeName,valueName)
+  templatingDict = {}
+  templatingDict['enums'] = enums
+  templatingDict['structs'] = structs
+  templatingDict['rootName'] = schema['rootName']
 
-  primitives:
-  -----------
-  int
-    jsonValue val(objectInt);
-    val.setValue("$name")
-    parent.add(("$name",$name)
-  double
-    ...
-  string
-    ...
-
-  collections:
-  -----------
-  dict { }
-
-
-
-
-
-  serializeValue()
-  """
+  return templatingDict
 
-  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"])
-    cppText.write("    %s %s;\n" % (cppType, name))
-
-  tsText.write("};\n\n")
-  cppText.write("};\n\n")
+# +-----------------------+
+# |    Write to files     |
+# +-----------------------+
 
-  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
-#include <cstdint>
-#include <string>
-#include <vector>
-#include <map>
-
-""" % (time.ctime(), rootName))
+# def WriteStreamsToFiles(rootName: str, genc: Dict[str, StringIO]) \
+#   -> None:
+#   pass
 
-  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"]
-
-  # 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] = {}
-
-  outputStreams: GeneratedCode = GeneratedCode()
-
-  WritePreambles(rootName, outputStreams)
+def LoadSchema(fn):
+  with open(fn, 'rb') as f:
+    schema = yaml.load(f)
+  return schema
 
-  # 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
+def GetTemplatingDictFromSchemaFilename(fn):
+  obj = LoadSchema(fn)
+  genOrder: str = ComputeRequiredDeclarationOrder(obj)
+  templatingDict = ProcessSchema(obj, genOrder)
+  return templatingDict
 
-    # 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
-        )
-
-    for i in range(len(genOrderQueue)):
-      typeName = genOrderQueue[i]
-      typeDict = structTypes[typeName]
-      ProcessStructType(outputStreams, typeDict)
-
-  return (rootName, outputStreams, genOrderQueue)
-
-
-def WriteStreamsToFiles(rootName: str, outputStreams: Dict[str, StringIO]) -> None:
-  pass
+# +-----------------------+
+# |      ENTRY POINT      |
+# +-----------------------+
 
 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" """
+    usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schema
+    EXAMPLE: python stonegentool.py -o "generated_files/" """
+      + """ "mainSchema.yaml,App Specific Commands.json" """
   )
-  parser.add_argument("input_schema", type=str, help="path to the schema file")
+  parser.add_argument("input_schema", type=str, \
+    help="path to the schema file")
   parser.add_argument(
     "-o",
     "--out_dir",
@@ -479,6 +442,219 @@
   inputSchemaFilename = args.input_schema
   outDir = args.out_dir
 
-  (rootName, outputStreams, _) = ProcessSchema(LoadSchema(inputSchemaFilename))
-  WriteStreamsToFiles(rootName, outputStreams)
+  schema: Dict = LoadSchema(inputSchemaFilename)
+  genOrder: List[str] = ComputeRequiredDeclarationOrder(schema)
+  processedSchema: Dict = ProcessSchema(schema,genOrder)
+
+
+# def GenEnumDecl(genc: GenCode, fullName: str, schema: Dict) -> None:
+#   """Writes the enumerations in genc"""
+#   enumDict:Dict=schema[fullName]
+#   # jinja2 template
+#   j2cppEnum = Template(trim(
+#   """ {{fullName}}
+#       {
+#           {% for key in enumDict.keys()%}
+#           {{key}},
+#           {%endfor%}
+#       };
+#   """))
+#   j2cppEnumR = j2cppEnum.render(locals())
+#   genc.cppEnums.write(j2cppEnumR)
+
+#   j2tsEnum = Template(trim(
+#   """ export {{fullName}}
+#       {
+#           {% for key in enumDict.keys()%}
+#           {{key}},
+#           {%endfor%}
+#       };
+#   """))
+#   j2cppEnumR = j2cppEnum.render(locals())
+#   genc.tsEnums.write(j2cppEnumR)
+
+  
+
+# def GetSerializationCode(typename: str,valueName: str, tempName: str)
+#   if IsPrimitiveType(typename) or IsTemplateCollection(typename):
+#     # no need to write code for the primitive types or collections.
+#     # It is handled in C++ by the template functions and in TS by 
+#     # the JSON.stringify code.
+#   elif IsStructType(typename):
+#     pass
+
+# def GenStructTypeDeclAndSerialize(genc: GenCode, type, schema) -> None:
+#   ######
+#   # CPP
+#   ######
+#   sampleCpp = """  struct Message1
+#     {
+#       int32_t a;
+#       std::string b;
+#       EnumMonth0 c;
+#       bool d;
+#     };
+
+#     Json::Value StoneSerialize(const Message1& value)
+#     {
+#       Json::Value result(Json::objectValue);
+#       result["a"] = StoneSerialize(value.a);
+#       result["b"] = StoneSerialize(value.b);
+#       result["c"] = StoneSerialize(value.c);
+#       result["d"] = StoneSerialize(value.d);
+#       return result;
+#     }
+#     """
+    
+
+#   ######
+#   # TS
+#   ######
+#   sampleTs = """
+#       {
+#         export class Message1 {
+#           a: number;
+#           b: string;
+#           c: EnumMonth0;
+#           d: boolean;
+#           public StoneSerialize(): string {
+#             let container: object = {};
+#             container['type'] = 'Message1';
+#             container['value'] = this;
+#             return JSON.stringify(container);
+#           }
+#         };
+#       }
+#     """
+
+
+
+
+#   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("{\n")
+
+#   """
+  
+#   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"])
+#     cppText.write("    %s %s;\n" % (cppType, name))
+
+#   tsText.write("};\n\n")
+#   cppText.write("};\n\n")
+
+#   genc.tsStructs.write(tsText.getvalue())
+#   genc.cppStructs.write(cppText.getvalue())
+
+
+# def GenerateCodeFromTsTemplate(genc)
+
+
+# +-----------------------+
+# |    CODE GENERATION    |
+# +-----------------------+
+
+# def GenPreambles(rootName: str, genc: GenCode) -> None:
+#   cppPreambleT = Template(trim(
+#     """// autogenerated by stonegentool on {{time.ctime()}} 
+#     // for module {{rootName}}
+#     #include <cstdint>
+#     #include <string>
+#     #include <vector>
+#     #include <map>
+#     namespace {{rootName}}
+#     {
+#       Json::Value StoneSerialize(int32_t value)
+#       {
+#         Json::Value result(value);
+#         return result;
+#       }
+#       Json::Value StoneSerialize(double value)
+#       {
+#         Json::Value result(value);
+#         return result;
+#       }
+#       Json::Value StoneSerialize(bool value)
+#       {
+#         Json::Value result(value);
+#         return result;
+#       }
+#       Json::Value StoneSerialize(const std::string& value)
+#       {
+#         // the following is better than 
+#         Json::Value result(value.data(),value.data()+value.size());
+#         return result;
+#       }
+#       template<typename T>
+#       Json::Value StoneSerialize(const std::map<std::string,T>& value)
+#       {
+#         Json::Value result(Json::objectValue);
+
+#         for (std::map<std::string, T>::const_iterator it = value.cbegin();
+#           it != value.cend(); ++it)
+#         {
+#           // it->first it->second
+#           result[it->first] = StoneSerialize(it->second);
+#         }
+#         return result;
+#       }
+#       template<typename T>
+#       Json::Value StoneSerialize(const std::vector<T>& value)
+#       {
+#         Json::Value result(Json::arrayValue);
+#         for (size_t i = 0; i < value.size(); ++i)
+#         {
+#           result.append(StoneSerialize(value[i]));
+#         }
+#         return result;
+#       }
+#     """
+#   cppPreambleR = cppPreambleT.render(locals())
+#   genc.cppPreamble.write(cppPreambleR)
+  
+#   tsPreambleT = Template(trim(
+#     """// autogenerated by stonegentool on {{time.ctime()}} 
+#     // for module {{rootName}}
+    
+#     namespace {{rootName}}
+#     {
+#     """
+#   tsPreambleR = tsPreambleT.render(locals())
+#   genc.tsPreamble.write(tsPreambleR)
+
+# def ComputeOrder_ProcessStruct( \
+#   genOrder: List[str], name:str, schema: Dict[str, str]) -> None:
+#   # let's generate the code according to the
+#   struct = schema[name]
+
+#   if not IsStructType(name):
+#     raise Exception(f'{typename} should start with "struct "')
--- a/Resources/CodeGeneration/stonegentool_test.py	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/stonegentool_test.py	Wed Feb 20 20:51:30 2019 +0100
@@ -1,8 +1,46 @@
+#
+#        1         2         3         4         5         6         7         8
+# 345678901234567890123456789012345678901234567890123456789012345678901234567890
+#
+
 from stonegentool import \
-EatToken,SplitListOfTypes,ParseTemplateType,LoadSchema,CheckSchemaSchema,ProcessSchema
+EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
+CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
+GetTemplatingDictFromSchemaFilename
 import unittest
 import os
 import re
+import pprint
+from jinja2 import Template
+
+ymlSchema = trim("""rootName: VsolMessages
+
+struct B:
+  someAs: vector<A>
+  someInts: vector<int32>
+
+struct C:
+  someBs: vector<B>
+  ddd:    vector<string>
+
+struct A:
+  someStrings: vector<string>
+  someInts2: vector<int32>
+  movies: vector<MovieType>
+
+enum MovieType:
+  - RomCom
+  - Horror
+  - ScienceFiction
+  - Vegetables
+
+enum CrispType:
+ - SaltAndPepper
+ - CreamAndChives
+ - Paprika
+ - Barbecue
+)
+""")
 
 def RemoveDateTimeLine(s : str):
   # regex are non-multiline by default, and $ does NOT match the end of the line
@@ -78,129 +116,87 @@
     self.assertEqual(b4,["int","vector<string>"])
     
   def test_ParseSchema(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
     obj = LoadSchema(fn)
-    # we're happy if it does not crash
+    # we're happy if it does not crash :)
     CheckSchemaSchema(obj)
 
-  def test_ParseSchema_bogus_json(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test', 'test1_bogus_json.jsonc')
-    self.assertRaises(Exception,LoadSchema,fn)
+  # def test_ParseSchema_bogus_json(self):
+  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1_bogus_json.jsonc')
+  #   self.assertRaises(Exception,LoadSchema,fn)
+
+  # def test_ParseSchema_bogus_schema(self):
+  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1_bogus_schema.jsonc')
+  #   obj = LoadSchema(fn)
+  #   self.assertRaises(Exception,CheckSchemaSchema,obj) 
 
-  def test_ParseSchema_bogus_schema(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test', 'test1_bogus_schema.jsonc')
+  def test_ComputeRequiredDeclarationOrder(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
     obj = LoadSchema(fn)
-    self.assertRaises(Exception,CheckSchemaSchema,obj) 
+    genOrder: str = ComputeRequiredDeclarationOrder(obj)
+    self.assertEqual(3,len(genOrder))
+    self.assertEqual("A",genOrder[0])
+    self.assertEqual("B",genOrder[1])
+    self.assertEqual("C",genOrder[2])
 
-  def test_GenOrderQueue(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
+  # def test_GeneratePreambleEnumerationAndStructs(self):
+  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
+  #   obj = LoadSchema(fn)
+  #   (_,genc,_) = ProcessSchema(obj)
+
+  def test_genEnums(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
     obj = LoadSchema(fn)
-    genOrderQueue:str
-    _, _, genOrderQueue = ProcessSchema(obj)
-    self.assertEqual(3,len(genOrderQueue))
-    self.assertEqual("A",genOrderQueue[0])
-    self.assertEqual("B",genOrderQueue[1])
-    self.assertEqual("C",genOrderQueue[2])
+    genOrder: str = ComputeRequiredDeclarationOrder(obj)
+    processedSchema = ProcessSchema(obj, genOrder)
+    processedSchemaStr = pprint.pformat(processedSchema,indent=2)
+    processedSchemaStrRef = """{ 'enums': [ { 'fields': ['RomCom', 'Horror', 'ScienceFiction', 'Vegetables'],
+               'name': 'MovieType'},
+             { 'fields': [ 'SaltAndPepper',
+                           'CreamAndChives',
+                           'Paprika',
+                           'Barbecue'],
+               'name': 'CrispType'}],
+  'rootName': 'VsolMessages',
+  'structs': [ { 'fields': { 'movies': 'vector<MovieType>',
+                             'someInts2': 'vector<int32>',
+                             'someStrings': 'vector<string>'},
+                 'name': 'A'},
+               { 'fields': {'someAs': 'vector<A>', 'someInts': 'vector<int32>'},
+                 'name': 'B'},
+               { 'fields': {'ddd': 'vector<string>', 'someBs': 'vector<B>'},
+                 'name': 'C'}]}"""
 
-  def test_GeneratePreambleEnumerationAndStructs(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
-    obj = LoadSchema(fn)
-    (_,outputStreams,_) = ProcessSchema(obj)
+    self.assertEqual(processedSchemaStrRef,processedSchemaStr)
 
-    tsPreambleRef: str = "// autogenerated by stonegentool on Fri Feb 15 07:36:51 2019\n"
-    tsEnumsRef: str = """enum MovieType
-{
-    Romcom,
+  def test_GenerateTypeScriptEnums(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    tdico = GetTemplatingDictFromSchemaFilename(fn)
+    template = Template("""  // end of generic methods
+{% for enum in enums%}  export enum {{enum['name']}} {
+{% for key in enum['fields']%}    {{key}},
+{%endfor%}  };
+
+{%endfor%}""")
+    renderedCode = template.render(**tdico)
+    renderedCodeRef = """  // end of generic methods
+  export enum MovieType {
+    RomCom,
     Horror,
     ScienceFiction,
-    Vegetables
-};
+    Vegetables,
+  };
 
-enum CrispType
-{
+  export enum CrispType {
     SaltAndPepper,
     CreamAndChives,
     Paprika,
-    Barbecue
-};
-
-"""
-
-    tsStructsRef: str = """class A
-{
-    public Array<string> someStrings;
-    public Array<number> someInts2;
-};
-
-class B
-{
-    public Array<A> someAs;
-    public Array<number> someInts;
-};
-
-class C
-{
-    public Array<B> someBs;
-    public Array<D> ddd;
-};
-
-"""
-
-    tsPreambleRefCastrated: str = RemoveDateTimeLine(tsPreambleRef)
-    tsPreambleCastrated: str = RemoveDateTimeLine(outputStreams.tsPreamble.getvalue())
-    self.assertEqual(tsPreambleRefCastrated,tsPreambleCastrated)
-    self.assertEqual(tsEnumsRef,outputStreams.tsEnums.getvalue())
-    self.assertEqual(tsStructsRef,outputStreams.tsStructs.getvalue())
-  
-    cppPreambleRef: str = """// autogenerated by stonegentool on Fri Feb 15 07:36:51 2019
-#include <cstdint>
-#include <string>
-#include <vector>
-#include <map>
+    Barbecue,
+  };
 
 """
-
-    cppEnumsRef: str = """enum MovieType
-{
-    Romcom,
-    Horror,
-    ScienceFiction,
-    Vegetables
-};
-
-enum CrispType
-{
-    SaltAndPepper,
-    CreamAndChives,
-    Paprika,
-    Barbecue
-};
+    self.assertEqual(renderedCodeRef,renderedCode)
 
-"""
-    cppStructsRef: str = """struct A
-{
-    std::vector<string> someStrings;
-    std::vector<int32_t> someInts2;
-};
-
-struct B
-{
-    std::vector<A> someAs;
-    std::vector<int32_t> someInts;
-};
-
-struct C
-{
-    std::vector<B> someBs;
-    std::vector<D> ddd;
-};
-
-"""  
-    cppPreambleRefCastrated: str = RemoveDateTimeLine(cppPreambleRef)
-    cppPreambleCastrated: str = RemoveDateTimeLine(outputStreams.cppPreamble.getvalue())
-    self.assertEqual(cppPreambleRefCastrated,cppPreambleCastrated)
-    self.assertEqual(cppEnumsRef,outputStreams.cppEnums.getvalue())
-    self.assertEqual(cppStructsRef,outputStreams.cppStructs.getvalue())
 
   def test_GenerateTypeScriptHandlerInterface(self):
     pass
--- a/Resources/CodeGeneration/template.in.ts	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/template.in.ts	Wed Feb 20 20:51:30 2019 +0100
@@ -1,38 +1,125 @@
-class Greeter {
-    greeting: string;
-    constructor(message: string) {
-        this.greeting = message;
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+*/
+
+namespace {{rootName}}
+{
+  function StoneCheckSerializedValueType(value: any, typeStr: string)
+  {
+    StoneCheckSerializedValueTypeGeneric(value);
+
+    if (value['type'] != typeStr)
+    {
+      throw new Error(
+        `Cannot deserialize type ${value['type']} into ${typeStr}`);
     }
-    greet() {
-        return "Hello, " + this.greeting;
+  }
+
+  function isString(val: any) :boolean
+  {
+    return ((typeof val === 'string') || (val instanceof String));
+  }
+  
+  function StoneCheckSerializedValueTypeGeneric(value: any)
+  {
+    if ( (!('type' in value)) || (!isString(value)) )
+    {
+      throw new Error(
+        "Cannot deserialize value ('type' key invalid)");
     }
-}
-enum Color { Red, Green, Blue };
+  }
+
+// end of generic methods
+{% for enum in enums%}
+  export enum {{enum['name']}} {
+    {% for key in enumDict.keys()%}
+    {{key}},
+    {%endfor%}
+  };
+{%endfor%}
+
+  export class Message1 {
+    a: number;
+    b: string;
+    c: EnumMonth0;
+    d: boolean;
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'VsolStuff.Message1';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
 
-class TestMessage {
-    s1: string;
-    s2: Array<string>;
-    s3: Array<Array<string>>;
-    s4: Map<string, number>;
-    s5: Map<number, Array<string>>;
-    s6: Color;
-    s7: boolean;
-}
+  export class Message2 {
+    constructor()
+    {
+      this.tata = new Array<Message1>();
+      this.tutu = new Array<string>();
+      this.titi = new Map<string, string>();
+      this.lulu = new Map<string, Message1>();  
+    }
+    toto: string;
+    tata: Message1[];
+    tutu: string[];
+    titi: Map<string, string>;
+    lulu: Map<string, Message1>;
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'VsolStuff.Message2';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+    public static StoneDeserialize(valueStr: string) : Message2
+    {
+      let value: any = JSON.parse(valueStr);
+      StoneCheckSerializedValueType(value, "VsolStuff.Message2");
+      let result: Message2 = value['value'] as Message2;
+      return result;
+    }
+    };
+
+  export interface IDispatcher
+  {
+    HandleMessage1(value: Message1): boolean;
+    HandleMessage2(value: Message2): boolean;
+  };
 
-let tm = new TestMessage();
-tm.s2 = new Array<string>()
-tm.s2.push("toto");
-tm.s2.push("toto2");
-tm.s2.push("toto3");
-tm.s4 = new Map<string, number>();
-tm.s4["toto"] = 42;
-tm.s4["toto"] = 1999;
-tm.s4["tatata"] = 1999;
-tm.s6 = Color.Red;
-tm.s7 = true
+  /** Service function for StoneDispatchToHandler */
+  export function StoneDispatchJsonToHandler(
+    jsonValueStr: string, dispatcher: IDispatcher): boolean
+  {
+    let jsonValue: any = JSON.parse(jsonValueStr);
+    StoneCheckSerializedValueTypeGeneric(jsonValue);
+    let type: string = jsonValue["type"];
+    if (type == "")
+    {
+      // this should never ever happen
+      throw new Error("Caught empty type while dispatching");
+    }
+    else if (type == "VsolStuff.Message1")
+    {
+      let value = jsonValue["value"] as Message1;
+      return dispatcher.HandleMessage1(value);
+    }
+    else if (type == "VsolStuff.Message2")
+    {
+      let value = jsonValue["value"] as Message2;
+      return dispatcher.HandleMessage2(value);
+    }
+    else
+    {
+      return false;
+    }
+  }
 
-let txt = JSON.stringify(tm)
-console.log(txt);
-
-let greeter = new Greeter("world");
-
+  /** Takes a serialized type and passes this to the dispatcher */
+  export function StoneDispatchToHandler(
+    strValue: string, dispatcher: IDispatcher): boolean
+  {
+    let jsonValue: any = JSON.parse(strValue)
+    return StoneDispatchJsonToHandler(jsonValue, dispatcher);
+  }
+}
--- a/Resources/CodeGeneration/test_data/test1.yaml	Mon Feb 18 15:38:05 2019 +0100
+++ b/Resources/CodeGeneration/test_data/test1.yaml	Wed Feb 20 20:51:30 2019 +0100
@@ -10,7 +10,7 @@
 
 struct C:
   someBs: vector<B>
-  ddd:    vector<D>
+  ddd:    vector<string>
 
 struct A:
   someStrings: vector<string>