#!/usr/bin/python #-*- coding:UTF-8 -*- import os import re import codecs import shutil current_dir = os.getcwd() generated_dir = current_dir+"/../../u2china_cli_proj/arpg/Assets/Lua/Const/" base_type_map = { "string":"string", "int32":"Int32", "int64":"Int64", "bool":"bool", "uint32":"UInt32", "uint64":"UInt64", "bytes":"byte[]" } base_default_value = { "string":"''", "int32":"0", "int64":"0", "bool":"false", "uint32":"0", "uint64":"0", "bytes":"nil" } class MessageClass: def __init__(self,name,fields,enumnames): self._class_name = name self._fields = fields self._enumnames = enumnames def generate(self): constructor = """ function New{0}() local result = {{}}{1} return result end""" parse_body = "" base_type_line_template = """ result.{0} = msgData.{0}""" base_type_list_line_template = """ result.{0} = {{}} rawList = msgData.{0} for i = 1,rawList:len() do table.insert(result.{0},rawList:get(i)) end""" high_type_line_template = """ result.{0} = Parse{1}(msgData.{0})""" high_type_list_line_template = """ result.{0} = {{}} rawList = msgData.{0} for i = 1,rawList:len() do table.insert(result.{0},Parse{1}(rawList:get(i))) end""" for field in self._fields: type_name = field["type"] origin_field_name = field["name"] isBase = base_type_map.get(type_name) != None if isBase: type_name = base_type_map.get(type_name) if field["isList"]: if isBase: parse_body = parse_body+base_type_list_line_template.format(origin_field_name) else: parse_body = parse_body + high_type_list_line_template.format(origin_field_name,type_name) else: if isBase: parse_body = parse_body+base_type_line_template.format(origin_field_name) else: parse_body = parse_body+high_type_line_template.format(origin_field_name,type_name) new_body = "" base_type_line_template = """ result.{0} = {1}""" base_type_list_line_template = """ result.{0} = {{}}""" high_type_line_template = """ result.{0} = New{1}()""" high_type_list_line_template = """ result.{0} = {{}}""" for field in self._fields: type_name = field['type'] origin_field_name = field['name'] isBase = base_type_map.get(type_name)!=None # if isBase: # type_name = base_type_map.get(type_name) if field['isList']: if isBase: new_body = new_body+base_type_list_line_template.format(origin_field_name) else: new_body = new_body+high_type_list_line_template.format(origin_field_name) else: isEnum = self._enumnames.count(type_name) > 0 if isBase or isEnum: value = base_default_value.get(type_name) if isEnum: value = "0" new_body = new_body+base_type_line_template.format(origin_field_name,value) else: new_body = new_body+high_type_line_template.format(origin_field_name,type_name) constructor = constructor.format(self._class_name,new_body) return constructor def load_file_data(filename): data = None with open(filename, 'r', encoding='UTF-8') as handle: data = handle.read() return data def save_file_data(filename,data): with codecs.open(filename,"w","utf8") as handle: handle.write(data) def delete_comments(content): comment_regex = re.compile(r'//.*\n') content = comment_regex.sub("\n",content) return content def parse_property(property): property = property.lstrip(' ') property = property.lstrip('\t') components = property.split(' ') components = [str.replace(" ","") for str in components] components = [str for str in components if len(str) > 0] result = {"type":components[0],"name":components[1],"isList":False} if components[0] == 'repeated': result["type"] = components[1] result["name"] = components[2] result["isList"] = True return result def parse_field(field): components = field.split(' ') components = [str for str in components if len(str)>0] return {"name":components[0],"value":components[2]} def parse_message(message): linebreak_regex = re.compile(r'[\r|\n]') table_regex = re.compile(r'\t') message = linebreak_regex.sub('',message) message = table_regex.sub(' ',message) fields = message.split(';') results = [] for field in fields: blank_regex = re.compile(' ') blank_match = blank_regex.findall(field) if len(field) > len(blank_match): results.append(parse_property(field)) return results def parse_enum(enum): linebreak_regex = re.compile(r'[\t\n]') enum = linebreak_regex.sub('',enum) fields = enum.split(';') results = [] for field in fields: blank_regex = re.compile(r' ') blank_match = blank_regex.findall(field) if len(field) > len(blank_match): results.append(parse_field(field)) return results def parse_protocol(content): message_regex = re.compile(r'message\s*(\w+)\s*\{([\s\S]*?)\}') enum_regex = re.compile(r'enum\s*(\w+)\s*\{([\s\S]*?)\}') content = delete_comments(content) match = message_regex.findall(content) results = [] enum_match = enum_regex.findall(content) enum_names = [] if enum_match: for enum in enum_match: enum_name = enum[0] # print enum_name enum_names.append(enum_name) if match: for message in match: message_name = message[0] message_body = message[1] message_fields = parse_message(message_body) # print message_name[len(message_name) -3:] if not message_name[len(message_name)-3:] == "Req": results.append(MessageClass(message_name,message_fields,enum_names)) return results if __name__ == "__main__": protocol_name = "GameCs.proto" message_classes = parse_protocol(load_file_data(protocol_name)) content = """local M = {{}} local string = string local NETMSG = NETMSG _ENV = M function Parse(msgId,msgData) local parserName = string.format("Parse%s",NETMSG.MsgList[msgId]) if M[parserName] then return M[parserName](msgData) else LogWarn("NetMsgParser",string.format("no parser for net msg %s",msgId)) end end {0} return M""" content_body = "" for message in message_classes: content_body = content_body + message.generate() content = content.format(content_body) output_name = generated_dir+"NetMsgParser.lua" save_file_data(output_name,content)