|
|
|
#!/usr/bin/env python
|
|
|
|
# Julian Rockey 2003
|
|
|
|
# Generate marshall/demarshal functions from marshal_funcs.data file
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import re
|
|
|
|
|
|
|
|
def cap_first(str):
|
|
|
|
"""Capitalise first letter of string."""
|
|
|
|
return str[0].upper() + str[1:]
|
|
|
|
|
|
|
|
def set_method(attr):
|
|
|
|
"""Return the name for a QT class setter method for an attribute."""
|
|
|
|
return "set" + cap_first(attr)
|
|
|
|
|
|
|
|
class DictMaker:
|
|
|
|
"""Generate code for marshalling/demarshalling types using Python dictionaries."""
|
|
|
|
|
|
|
|
supported_types = ['string']
|
|
|
|
re_dictmap = re.compile("%dict\-map(.*)")
|
|
|
|
re_dictmap_constructor = re.compile("%constructor (.+)")
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.attr_list = []
|
|
|
|
self.current_type = None
|
|
|
|
self.operation = None
|
|
|
|
self.constructor = None
|
|
|
|
|
|
|
|
self.type_handlers = {}
|
|
|
|
for type in self.supported_types:
|
|
|
|
self.type_handlers[type] = (eval('self.handle_%s_marsh' % type),
|
|
|
|
eval('self.handle_%s_demarsh' % type))
|
|
|
|
|
|
|
|
def handle_string_marsh(self, attribute):
|
|
|
|
"""Handle marshalling of string item from the dictionary."""
|
|
|
|
return ["if (%s && !PyBytes_Check(%s)) return false;" % (attribute, attribute),
|
|
|
|
"if (%s) { qobj.%s(TQString::fromUtf8(PyBytes_AS_STRING(%s)));" % (attribute, set_method(attribute), attribute),
|
|
|
|
"PyDict_DelItemString(dict,(char*)\"%s\"); } " % (attribute)]
|
|
|
|
|
|
|
|
def handle_string_demarsh(self, attribute):
|
|
|
|
"""Handle demarshalling of string items into the dictionary."""
|
|
|
|
return ["PyObject *%s = PyBytes_FromString(qobj.%s().utf8().data() );" % (attribute ,attribute),
|
|
|
|
"PyDict_SetItemString(dict, (char*)\"%s\", %s);" % (attribute, attribute)
|
|
|
|
]
|
|
|
|
|
|
|
|
def pre_code_for(self, operation, attribute):
|
|
|
|
|
|
|
|
if operation==MARSHAL:
|
|
|
|
return ["PyObject *%s = PyDict_GetItemString(dict,(char*)\"%s\");" % (attribute, attribute) ]
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
def post_code_for(self, operation, attribute):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def code_for(self, operation, type, attribute):
|
|
|
|
if operation!=None and (type in self.type_handlers):
|
|
|
|
return self.pre_code_for(operation, attribute) + \
|
|
|
|
self.type_handlers[type][not not operation](attribute) + \
|
|
|
|
self.post_code_for(operation, attribute)
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
def set_current_type(self, current_type):
|
|
|
|
self.current_type = current_type
|
|
|
|
self.constructor = "";
|
|
|
|
|
|
|
|
def set_operation(self, operation):
|
|
|
|
if operation in [None, MARSHAL, DEMARSHAL]:
|
|
|
|
self.operation = operation
|
|
|
|
|
|
|
|
def check_dictmap(self, line):
|
|
|
|
|
|
|
|
if self.operation not in [MARSHAL,DEMARSHAL]: return []
|
|
|
|
|
|
|
|
m=self.re_dictmap_constructor.match(line)
|
|
|
|
if m:
|
|
|
|
self.constructor = m.groups()[0]
|
|
|
|
return ['']
|
|
|
|
|
|
|
|
m=self.re_dictmap.match(line)
|
|
|
|
if not m: return []
|
|
|
|
|
|
|
|
if self.operation==MARSHAL:
|
|
|
|
result = ["{",
|
|
|
|
"if (!PyDict_Check(obj)) return false;",
|
|
|
|
"%s qobj%s;" % (self.current_type,self.constructor),
|
|
|
|
"PyObject *dict = PyDict_Copy(obj);"
|
|
|
|
]
|
|
|
|
if self.operation==DEMARSHAL:
|
|
|
|
result = ["{",
|
|
|
|
"PyObject *dict = PyDict_New();",
|
|
|
|
"if (!dict) return NULL;",
|
|
|
|
"%s qobj%s;" % (self.current_type,self.constructor),
|
|
|
|
"(*str) >> qobj;"
|
|
|
|
]
|
|
|
|
|
|
|
|
if m.groups()[0].strip():
|
|
|
|
self.attr_list = [tuple(x.split(':')) for x in m.groups()[0].strip().split(',') ]
|
|
|
|
|
|
|
|
for attribute, type in self.attr_list:
|
|
|
|
result += self.code_for(self.operation, type, attribute)
|
|
|
|
|
|
|
|
if self.operation==MARSHAL:
|
|
|
|
result += ["if (str) (*str) << qobj;",
|
|
|
|
"Py_DECREF(dict);",
|
|
|
|
"return true;",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
if self.operation==DEMARSHAL:
|
|
|
|
result += ["return dict;",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
class DocType:
|
|
|
|
"""A class to hold documentation information for each type."""
|
|
|
|
|
|
|
|
def __init__(self, type):
|
|
|
|
self.type = type
|
|
|
|
self.demarshal_asme = None
|
|
|
|
self.asme = []
|
|
|
|
self.info = []
|
|
|
|
|
|
|
|
def add_asme(self, asme):
|
|
|
|
if self.demarshal_asme == None: self.demarshal_asme = asme
|
|
|
|
self.asme += [asme]
|
|
|
|
|
|
|
|
def add_info(self,info):
|
|
|
|
self.info += [info]
|
|
|
|
|
|
|
|
def xml(self):
|
|
|
|
return ['<type dcoptype="%s">' % self.type,
|
|
|
|
' <demarshal-asme>%s</demarshal-asme>' % self.demarshal_asme] + \
|
|
|
|
[' <marshal-asme>%s</marshal-asme>' % asme for asme in self.asme ] + \
|
|
|
|
[' <info>%s</info>' % info for info in self.info ] + \
|
|
|
|
['</type>']
|
|
|
|
|
|
|
|
|
|
|
|
MARSHAL, DEMARSHAL, TOPYOBJ, FROMPYOBJ = 0,1,2,3
|
|
|
|
|
|
|
|
if len(sys.argv)!=4:
|
|
|
|
print("Use: gen_marshal_code.py <input file> <output file> <doc-xml-output file>")
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
nowt, in_name, code_name, doc_xml_name = tuple(sys.argv)
|
|
|
|
|
|
|
|
##in_name, code_name, doc_xml_name = "marshal_funcs.data", "marshal_funcs.h", "marshal_funcs_doc.xml"
|
|
|
|
|
|
|
|
gen_code_comments = ['/*',
|
|
|
|
' * This code was generated by gen_marshal_code.py',
|
|
|
|
' * Please do not modify, or it\'ll be overwritten!',
|
|
|
|
' */',
|
|
|
|
' ',
|
|
|
|
]
|
|
|
|
|
|
|
|
re_type = re.compile(r"type\: *([^\s]+).*")
|
|
|
|
re_marshDemarsh = re.compile("%% *(de)?marshal *.*")
|
|
|
|
re_tofromPyobj = re.compile("%% *(to|from)_pyobj *.*")
|
|
|
|
re_defaultCode = re.compile("%defaultcode *.*")
|
|
|
|
re_docInfo = re.compile("%doc *([^ ]+) *(.*)")
|
|
|
|
|
|
|
|
in_file = open(in_name,"r")
|
|
|
|
code = []
|
|
|
|
|
|
|
|
types = {}
|
|
|
|
doc_types = {}
|
|
|
|
current_operation = None
|
|
|
|
|
|
|
|
dict_maker = DictMaker()
|
|
|
|
|
|
|
|
for l in in_file.readlines():
|
|
|
|
l=l[:-1]
|
|
|
|
|
|
|
|
# match a "type:" line
|
|
|
|
m=re_type.match(l)
|
|
|
|
if m:
|
|
|
|
current_type = m.groups()[0]
|
|
|
|
types[current_type]={}
|
|
|
|
doc_types[current_type] = DocType(current_type)
|
|
|
|
dict_maker.set_current_type(current_type)
|
|
|
|
continue
|
|
|
|
|
|
|
|
m=re_docInfo.match(l)
|
|
|
|
if m:
|
|
|
|
doc_cmd, rest = m.groups()
|
|
|
|
if doc_cmd=="as":
|
|
|
|
doc_types[current_type].add_asme(rest)
|
|
|
|
if doc_cmd=="info":
|
|
|
|
doc_types[current_type].add_info(rest)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# match a "%% marshal" or "%% demarshal" line
|
|
|
|
m=re_marshDemarsh.match(l)
|
|
|
|
if m:
|
|
|
|
if m.groups()[0]:
|
|
|
|
current_operation = DEMARSHAL
|
|
|
|
code.append("PyObject *demarshal_" + current_type + \
|
|
|
|
"(TQDataStream *str)")
|
|
|
|
else:
|
|
|
|
current_operation = MARSHAL
|
|
|
|
code.append("bool marshal_" + current_type + \
|
|
|
|
"(PyObject *obj, TQDataStream *str)")
|
|
|
|
dict_maker.set_operation(current_operation)
|
|
|
|
continue
|
|
|
|
|
|
|
|
m=re_tofromPyobj.match(l)
|
|
|
|
if m:
|
|
|
|
if m.groups()[0]=='to':
|
|
|
|
current_operation = TOPYOBJ
|
|
|
|
code += ["PyObject *toPyObject_%s(%s val)" % (current_type,current_type)]
|
|
|
|
elif m.groups()[0]=='from':
|
|
|
|
current_operation = FROMPYOBJ
|
|
|
|
code += ["%s fromPyObject_%s(PyObject *obj, bool *ok)" % (current_type,current_type)]
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
if l.strip()=='%%':
|
|
|
|
current_operation = None
|
|
|
|
dict_maker.set_operation(current_operation)
|
|
|
|
|
|
|
|
if current_operation!=None:
|
|
|
|
types[current_type][current_operation]=1
|
|
|
|
|
|
|
|
dict_code = dict_maker.check_dictmap(l)
|
|
|
|
if dict_code:
|
|
|
|
code += dict_code
|
|
|
|
continue
|
|
|
|
|
|
|
|
m=re_defaultCode.match(l)
|
|
|
|
if m:
|
|
|
|
if current_operation==MARSHAL:
|
|
|
|
code += [
|
|
|
|
"{",
|
|
|
|
" bool ok;",
|
|
|
|
" %s qobj=fromPyObject_%s(obj,&ok);" % (current_type,current_type),
|
|
|
|
" if (ok && str) (*str) << qobj;",
|
|
|
|
" return ok;",
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
continue
|
|
|
|
if current_operation==DEMARSHAL:
|
|
|
|
code += [
|
|
|
|
"{",
|
|
|
|
" %s qobj;" % current_type,
|
|
|
|
" (*str) >> qobj;",
|
|
|
|
" return toPyObject_%s(qobj);" % current_type,
|
|
|
|
"}"
|
|
|
|
]
|
|
|
|
continue
|
|
|
|
|
|
|
|
code.append(l)
|
|
|
|
|
|
|
|
in_file.close()
|
|
|
|
|
|
|
|
code.append("void Marshaller::initFuncs() {")
|
|
|
|
for t in types:
|
|
|
|
if MARSHAL in types[t]:
|
|
|
|
code.append("m_marsh_funcs[\"" + t + "\"]=marshal_" + t + ";")
|
|
|
|
if DEMARSHAL in types[t]:
|
|
|
|
code.append("m_demarsh_funcs[\"" + t + "\"]=demarshal_" + t + ";")
|
|
|
|
code.append("}")
|
|
|
|
|
|
|
|
out_file = open(code_name,"w")
|
|
|
|
out_file.writelines([x + '\n' for x in gen_code_comments])
|
|
|
|
out_file.writelines([x + '\n' for x in code])
|
|
|
|
out_file.close()
|
|
|
|
|
|
|
|
xml_file = open(doc_xml_name,"w")
|
|
|
|
print('<?xml version="1.0" ?>', file=xml_file)
|
|
|
|
print('<!-- This file was auto-generated by gen_marshal_code.py. Changes will be lost! -->', file=xml_file)
|
|
|
|
print("<types>", file=xml_file)
|
|
|
|
[ [xml_file.write(x+"\n") for x in doc.xml()] for doc in list(doc_types.values()) ] # silly one-liner
|
|
|
|
print("</types>", file=xml_file)
|
|
|
|
xml_file.close()
|