#!/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 ['' % self.type, ' %s' % self.demarshal_asme] + \ [' %s' % asme for asme in self.asme ] + \ [' %s' % info for info in self.info ] + \ [''] MARSHAL, DEMARSHAL, TOPYOBJ, FROMPYOBJ = 0,1,2,3 if len(sys.argv)!=4: print("Use: gen_marshal_code.py ") 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('', file=xml_file) print('', file=xml_file) print("", file=xml_file) [ [xml_file.write(x+"\n") for x in doc.xml()] for doc in list(doc_types.values()) ] # silly one-liner print("", file=xml_file) xml_file.close()