You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebindings/dcoppython/shell/gen_marshal_code.py

277 lines
9.1 KiB

#!/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()