[CORD-1440] Moving the generative toolchain in a library
Change-Id: Ifa8e8f930ac34e1f8952099b7e34842a52f4664d
diff --git a/lib/xos-genx/xosgenx/xos2jinja.py b/lib/xos-genx/xosgenx/xos2jinja.py
new file mode 100644
index 0000000..929526d
--- /dev/null
+++ b/lib/xos-genx/xosgenx/xos2jinja.py
@@ -0,0 +1,355 @@
+import plyxproto.model as m
+import pdb
+import argparse
+import plyxproto.parser as plyxproto
+import traceback
+import sys
+import jinja2
+import os
+import copy
+
+
+def dotname_to_fqn(dotname):
+ b_names = [part.pval for part in dotname]
+ package = '.'.join(b_names[:-1])
+ name = b_names[-1]
+ if package:
+ fqn = package + '.' + name
+ else:
+ fqn = name
+ return {'name': name, 'fqn': fqn, 'package': package}
+
+
+def dotname_to_name(dotname):
+ b_names = [part.pval for part in dotname]
+ return '.'.join(b_names)
+
+
+def count_messages(body):
+ count = 0
+ for e in body:
+ if (type(e) == m.MessageDefinition):
+ count += 1
+ return count
+
+
+def count_fields(body):
+ count = 0
+ for e in body:
+ if (type(e) in [m.LinkDefinition, m.FieldDefinition, m.LinkSpec]):
+ count += 1
+ return count
+
+def compute_rlinks(messages, message_dict):
+ rev_links = {}
+
+ link_opposite = {
+ 'manytomany': 'manytomany',
+ 'manytoone': 'onetomany',
+ 'onetoone': 'onetoone',
+ 'onetomany': 'manytoone'
+ }
+
+ for m in messages:
+ for l in m['links']:
+ rlink = copy.deepcopy(l)
+
+ rlink['_type'] = 'rlink' # An implicit link, not declared in the model
+ rlink['src_port'] = l['dst_port']
+ rlink['dst_port'] = l['src_port']
+ rlink['peer'] = {'name': m['name'], 'package': m['package'], 'fqn': m['fqn']}
+ rlink['link_type'] = link_opposite[l['link_type']]
+
+ try:
+ try:
+ rev_links[l['peer']['fqn']].append(rlink)
+ except TypeError:
+ pdb.set_trace()
+ pass
+ except KeyError:
+ rev_links[l['peer']['fqn']] = [rlink]
+
+ for m in messages:
+ try:
+ m['rlinks'] = rev_links[m['name']]
+ message_dict[m['name']]['rlinks'] = m['rlinks']
+ except KeyError:
+ pass
+
+
+def name_to_value(obj):
+ try:
+ value = obj.value.value.pval
+ except AttributeError:
+ try:
+ value = obj.value.value
+ except AttributeError:
+ value = obj.value.pval
+
+ return value
+
+
+class Stack(list):
+ def push(self, x):
+ self.append(x)
+
+
+''' XOS2Jinja overrides the underlying visitor pattern to transform the tree
+ in addition to traversing it '''
+
+
+class XOS2Jinja(m.Visitor):
+
+ def get_stack(self):
+ return self.stack
+
+ def __init__(self):
+ super(XOS2Jinja, self).__init__()
+
+ self.stack = Stack()
+ self.models = {}
+ self.options = {}
+ self.package = None
+ self.message_options = {}
+ self.count_stack = Stack()
+ self.content = ""
+ self.offset = 0
+ self.current_message_name = None
+ self.verbose = 0
+ self.first_field = True
+ self.first_method = True
+
+ def visit_PackageStatement(self, obj):
+ dotlist = obj.name.value
+ dotlist2 = [f.pval for f in dotlist]
+ dotstr = '.'.join(dotlist2)
+ self.package = dotstr
+ return True
+
+ def visit_ImportStatement(self, obj):
+ '''Ignore'''
+ return True
+
+ def visit_OptionStatement(self, obj):
+ if not hasattr(obj, 'mark_for_deletion'):
+ if (self.current_message_name):
+ self.message_options[obj.name.value.pval] = obj.value.value.pval
+ else:
+ self.options[obj.name.value.pval] = obj.value.value.pval
+
+ return True
+
+ def visit_LU(self, obj):
+ return True
+
+ def visit_default(self, obj):
+ return True
+
+ def visit_FieldDirective(self, obj):
+ return True
+
+ def visit_FieldDirective_post(self, obj):
+
+ try:
+ name = obj.name.value.pval
+ except AttributeError:
+ name = obj.name.value
+
+ if type(obj.value) == list:
+ value = dotname_to_name(obj.value)
+ else:
+ value = name_to_value(obj)
+
+ self.stack.push([name, value])
+ return True
+
+ def visit_FieldType(self, obj):
+ '''Field type, if type is name, then it may need refactoring consistent with refactoring rules according to the table'''
+ return True
+
+ def visit_LinkDefinition(self, obj):
+ s = {}
+
+ try:
+ s['link_type'] = obj.link_type.pval
+ except AttributeError:
+ s['link_type'] = obj.link_type
+
+ s['src_port'] = obj.src_port.value.pval
+ s['name'] = obj.src_port.value.pval
+
+ try:
+ s['dst_port'] = obj.dst_port.value.pval
+ except AttributeError:
+ s['dst_port'] = obj.dst_port
+
+ if type(obj.through) == list:
+ s['through'] = dotname_to_fqn(obj.through)
+ else:
+ try:
+ s['through'] = obj.through.pval
+ except AttributeError:
+ s['through'] = obj.through
+
+ if type(obj.name) == list:
+ s['peer'] = dotname_to_fqn(obj.name)
+ else:
+ try:
+ s['peer'] = obj.name.pval
+ except AttributeError:
+ s['peer'] = obj.name
+
+ s['_type'] = 'link'
+ s['options'] = {'modifier': 'optional'}
+
+ self.stack.push(s)
+ return True
+
+ def visit_FieldDefinition(self, obj):
+ self.count_stack.push(len(obj.fieldDirective))
+ return True
+
+ def visit_FieldDefinition_post(self, obj):
+ s = {}
+
+ if isinstance(obj.ftype, m.Name):
+ s['type'] = obj.ftype.value
+ else:
+ s['type'] = obj.ftype.name.pval
+
+ s['name'] = obj.name.value.pval
+ s['modifier'] = obj.field_modifier.pval
+ s['id'] = obj.fieldId.pval
+
+ opts = {'modifier': s['modifier']}
+ n = self.count_stack.pop()
+ for i in range(0, n):
+ k, v = self.stack.pop()
+
+ # The two lines below may be added to eliminate "" around an option.
+ # Right now, this is handled in targets. FIXME
+ #
+ # if (v.startswith('"') and v.endswith('"')):
+ # v = v[1:-1]
+
+ opts[k] = v
+
+ s['options'] = opts
+ try:
+ last_link = self.stack[-1]['_type']
+ if (last_link == 'link'):
+ s['link'] = True
+ except:
+ pass
+ s['_type'] = 'field'
+
+ self.stack.push(s)
+ return True
+
+ def visit_EnumFieldDefinition(self, obj):
+ if self.verbose > 4:
+ print "\tEnumField: name=%s, %s" % (obj.name, obj)
+
+ return True
+
+ def visit_EnumDefinition(self, obj):
+ '''New enum definition, refactor name'''
+ if self.verbose > 3:
+ print "Enum, [%s] body=%s\n\n" % (obj.name, obj.body)
+
+ return True
+
+ def visit_MessageDefinition(self, obj):
+ self.current_message_name = obj.name.value.pval
+ self.message_options = {}
+ self.count_stack.push(count_fields(obj.body))
+ return True
+
+ def visit_MessageDefinition_post(self, obj):
+ stack_num = self.count_stack.pop()
+ fields = []
+ links = []
+ last_field = None
+ try:
+ obj.bases = map(dotname_to_fqn, obj.bases)
+ except AttributeError:
+ pass
+
+ last_field = {}
+ for i in range(0, stack_num):
+ f = self.stack.pop()
+ if (f['_type'] == 'link'):
+ f['options'] = {i: d[i] for d in [f['options'], last_field['options']] for i in d}
+ assert (last_field == fields[0])
+ fields[0].setdefault('options', {})['link_type'] = f['link_type']
+ links.insert(0, f)
+ else:
+ fields.insert(0, f)
+ last_field = f
+
+ if self.package:
+ model_name = '.'.join([self.package, obj.name.value.pval])
+ else:
+ model_name = obj.name.value.pval
+
+ model_def = {'name':obj.name.value.pval,'fields':fields,'links':links, 'bases':obj.bases, 'options':self.message_options, 'package':self.package, 'fqn': model_name, 'rlinks': []}
+ self.stack.push(model_def)
+
+ self.models[model_name] = model_def
+
+ # Set message options
+ for k, v in self.options.iteritems():
+ try:
+ if k not in self.message_options:
+ self.message_options[k] = v
+ except KeyError:
+ pass
+
+ self.current_message_name = None
+ return True
+
+ def visit_MessageExtension(self, obj):
+ return True
+
+ def visit_MethodDefinition(self, obj):
+ return True
+
+ def visit_ServiceDefinition(self, obj):
+ return True
+
+ def visit_ExtensionsDirective(self, obj):
+ return True
+
+ def visit_Literal(self, obj):
+ return True
+
+ def visit_Name(self, obj):
+ return True
+
+ def visit_DotName(self, obj):
+ return True
+
+ def visit_Proto(self, obj):
+ self.count_stack.push(count_messages(obj.body))
+ return True
+
+ def visit_Proto_post(self, obj):
+ count = self.count_stack.pop()
+ messages = []
+ for i in range(0, count):
+ try:
+ m = self.stack.pop()
+ except IndexError:
+ pass
+
+ messages.insert(0, m)
+
+ compute_rlinks(messages, self.models)
+
+ self.messages = messages
+ return True
+
+ def visit_LinkSpec(self, obj):
+ count = self.count_stack.pop()
+ self.count_stack.push(count + 1)
+ return True
\ No newline at end of file