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.
 
 
 
 
 
 

619 lines
19 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
namespace ProtoCSStruct
{
public class ProtoFileParser
{
private ProtoDescriptor pd;
private ProtoEnum currentEnum;
private ProtoMessage currentMessage;
public ProtoFileParser()
{
pd = new ProtoDescriptor();
}
public ProtoDescriptor Parse(string file, string nameSpace, string outpath,
string outfile, string enumOutFile, string classOutFile, string genClassCfg)
{
Console.WriteLine("begin parse proto file {0}", file);
pd.NameSpace = nameSpace;
pd.OutPutPath = outpath;
pd.OutPutFile = outfile;
pd.EnumOutPutFile = enumOutFile;
pd.ClassOutPutFile = classOutFile;
pd.ProtoFile = file;
string[] allline = File.ReadAllLines(file);
bool nameNeedUpper = true;
for(int i=0; i< allline.Length; i++)
{
if (string.IsNullOrEmpty(genClassCfg) && string.Equals(allline[i], "//**battle begin**//"))
{
break;
}
if (string.Equals(allline[i], "//**battle begin**//"))
{
nameNeedUpper = false; //不改变结构,战斗相关首字母不大写
}
ParseLine(i + 1, allline[i], nameNeedUpper);
}
ProtoDescriptorAll.Instance.AllProtoDescriptor.Add(pd);
ProccessOnReadEnd();
// 是否需要生成message的纯数据结构的class描述
ParseGenClassDescCfg(genClassCfg);
return pd;
}
private void ParseLine(int lineNum, string lineContent,bool nameNeedUpper)
{
lineContent = DeleteComment(lineContent);
lineContent = Escape(lineContent);
if (string.IsNullOrEmpty(lineContent))
{
return;
}
List<ProtoFileLine> linelist = GetComplexLineInfo(lineNum, lineContent);
foreach(var lineInfo in linelist)
{
ProcessOneLineInfo(lineInfo, nameNeedUpper);
}
}
private void ProcessOneLineInfo(ProtoFileLine lineInfo, bool nameNeedUpper)
{
if(lineInfo.LineType == LineType.Syntax)
{
if(lineInfo.Fields[1].Contains("proto3") == false)
{
ExceptionUtils.Throw("protocsstruct onlysupport proto3 syntax, at line {0}"
, lineInfo.LineNum);
}
return;
}
if(lineInfo.LineType == LineType.Enum)
{
if(currentEnum != null)
{
ExceptionUtils.Throw("start enum {0} at line {1}, but last enum not end"
, lineInfo.Fields[1], lineInfo.LineNum);
}
if (currentMessage != null)
{
ExceptionUtils.Throw("start enum {0} at line {1}, but last message not end"
, lineInfo.Fields[1], lineInfo.LineNum);
}
currentEnum = new ProtoEnum();
currentEnum.Name = lineInfo.Fields[1];
return;
}
if (lineInfo.LineType == LineType.Message)
{
if (currentEnum != null)
{
ExceptionUtils.Throw("start message {0} at line {1}, but last enum not end"
, lineInfo.Fields[1], lineInfo.LineNum);
}
if (currentMessage != null)
{
ExceptionUtils.Throw("start message {0} at line {1}, but last message not end"
, lineInfo.Fields[1], lineInfo.LineNum);
}
currentMessage = new ProtoMessage();
currentMessage.Name = lineInfo.Fields[1];
return;
}
if(lineInfo.LineType == LineType.FiledContent)
{
if (currentEnum != null)
{
ProtoEnumValue enumValue = new ProtoEnumValue();
enumValue.OriginalName = lineInfo.Fields[0];
enumValue.Number = int.Parse(lineInfo.Fields[1]);
FormatEnumValueName(currentEnum.Name, enumValue);
currentEnum.Values.Add(enumValue);
return;
}
if (currentMessage != null)
{
ProtoMessageField field = new ProtoMessageField();
field.IsRepeated = lineInfo.Fields[0] == "repeated";
int contentIndex = 0;
if(field.IsRepeated)
{
contentIndex++;
}
field.TypeName = lineInfo.Fields[contentIndex];
FieldType type = FieldTypeUtils.GetFieldType(field.TypeName);
field.FieldType = type;
contentIndex++;
field.Name = FormatFieldName(lineInfo.Fields[contentIndex], nameNeedUpper);
contentIndex++;
field.Number = int.Parse(lineInfo.Fields[contentIndex]);
while (lineInfo.Fields.Length > contentIndex + 2)
{
contentIndex++;
string key = lineInfo.Fields[contentIndex];
contentIndex++;
string value = lineInfo.Fields[contentIndex];
if(key == "default")
{
ExceptionUtils.Throw("protocsstruct not support default option in message filed at line {0}", lineInfo.LineNum);
}
if(key == "stringlength")
{
if(type != FieldType.String)
{
ExceptionUtils.Throw("protocsstruct option stringlength need string type filed at line {0}", lineInfo.LineNum);
}
field.StringLength = int.Parse(value);
//string 有长度限制,太长了不支持算了
if(field.StringLength < 8
|| field.StringLength > Define.MaxStringLength)
{
ExceptionUtils.Throw("protocsstruct option stringlength need in range 8 ~ {0} at line {1}"
, Define.MaxStringLength, lineInfo.LineNum);
}
if (FieldTypeUtils.IsValidStringLength(field.StringLength) == false)
{
ExceptionUtils.Throw("protocsstruct option stringlength need times with 8 at line {0}", lineInfo.LineNum);
}
}
if(key == "repeatedcount")
{
if(field.IsRepeated == false && field.FieldType != FieldType.Bytes)
{
ExceptionUtils.Throw("protocsstruct option repeatedcount need repeated at line {0}", lineInfo.LineNum);
}
field.RepeatedCount = int.Parse(value);
if (FieldTypeUtils.IsValidRepeatedCount(field.RepeatedCount) == false)
{
ExceptionUtils.Throw("protocsstruct option repeatedcount need times with 2 at line {0}", lineInfo.LineNum);
}
}
}
//这种情况最后统一处理
if(type == FieldType.String)
{
if(field.StringLength == 0)
{
ExceptionUtils.Throw("protocsstruct option stringlength need by string type at line {0}", lineInfo.LineNum);
}
}
if(field.IsRepeated && field.RepeatedCount == 0)
{
ExceptionUtils.Throw("protocsstruct option repeatedcount need by repeated field type at line {0}", lineInfo.LineNum);
}
currentMessage.Fields.Add(field);
return;
}
}
//结束
if(lineInfo.LineType == LineType.End)
{
if (currentEnum != null)
{
//排序
currentEnum.Values = currentEnum.Values.OrderBy(o => o.Number).ToList();
pd.Enums.Add(currentEnum);
currentEnum = null;
return;
}
if(currentMessage != null)
{
//排序
currentMessage.Fields = currentMessage.Fields.OrderBy(o => o.Number).ToList();
//map
foreach(var field in currentMessage.Fields)
{
currentMessage.FieldsMap.Add(field.Name, field);
}
pd.Messages.Add(currentMessage);
currentMessage = null;
}
}
}
//首字母大写
private string FormatFieldName(string name,bool nameNeedUpper)
{
if (!nameNeedUpper)
{
return name;
}
if(Char.IsUpper(name[0]))
{
return name;
}
StringBuilder sb = new StringBuilder(32);
for (int i = 0; i < name.Length; i++)
{
char c = name[i];
if(i == 0)
{
c = Char.ToUpper(c);
}
sb.Append(c);
}
return sb.ToString();
}
private void FormatEnumValueName(string enumName, ProtoEnumValue enumValue)
{
string name = enumValue.OriginalName;
int indexInEnumName = 0;
bool NextCharNeedUp = true;
bool needCmpNameChar = true;
bool allCharUp = true;
for (int i = 0; i < name.Length; i++)
{
char c = name[i];
if (c == '_')
{
continue;
}
if(Char.IsLower(c))
{
allCharUp = false;
break;
}
}
bool isPrefixClear = false;
StringBuilder sb = new StringBuilder(32);
for(int i=0; i< name.Length; i++)
{
char c = name[i];
if(c == '_')
{
NextCharNeedUp = true;
continue;
}
if (indexInEnumName < enumName.Length && needCmpNameChar)
{
char c2 = enumName[indexInEnumName];
indexInEnumName++;
if (Char.ToLower(c) != Char.ToLower(c2))
{
needCmpNameChar = false;
}
}
else if(! isPrefixClear && indexInEnumName == enumName.Length && needCmpNameChar)
{
// 如果枚举变量开头部分和枚举名一致时去掉前缀
// enum CSMsgID
// {
// CSMsgID_Login = 1;
// CSMsgID_Logout = 2;
// }
// 被转换成
// public enum CSMsgID
// {
// Login = 1,
// Logout = 2,
// }
sb.Clear();
isPrefixClear = true;
}
if(NextCharNeedUp)
{
c = Char.ToUpper(c);
NextCharNeedUp = false;
}
else if(allCharUp == true)
{
c = Char.ToLower(c);
}
sb.Append(c);
}
enumValue.Name = sb.ToString();
}
//删除注释
private string DeleteComment(string lineContent)
{
int commentIndex = lineContent.IndexOf("//");
if(commentIndex != -1)
{
lineContent = lineContent.Substring(0, commentIndex);
if(string.IsNullOrEmpty(lineContent))
{
return string.Empty;
}
}
lineContent = lineContent.Trim();
//去掉最后的;
if (lineContent.Length > 0 && lineContent.IndexOf(';') == lineContent.Length - 1)
{
lineContent = lineContent.Substring(0, lineContent.Length - 1);
}
return lineContent;
}
private string Escape(string lineContent)
{
if(lineContent.Contains('\t'))
{
lineContent = lineContent.Replace('\t', ' ');
}
return lineContent;
}
private List<ProtoFileLine> GetComplexLineInfo(int lineNum, string line)
{
List<ProtoFileLine> lineInfoList = new List<ProtoFileLine>();
if(line.Contains('{'))
{
int index = line.IndexOf('{');
string line1 = line.Substring(0, index);
ProtoFileLine lineinfo = GetSimpleLineInfo(lineNum, line1);
if(lineinfo != null)
{
lineInfoList.Add(lineinfo);
}
ProtoFileLine linebegin = new ProtoFileLine();
linebegin.LineNum = lineNum;
linebegin.LineType = LineType.Begin;
lineInfoList.Add(linebegin);
return lineInfoList;
}
if (line.Contains('}'))
{
int index = line.IndexOf('}');
string line1 = line.Substring(0, index);
ProtoFileLine lineinfo = GetSimpleLineInfo(lineNum, line1);
if (lineinfo != null)
{
lineInfoList.Add(lineinfo);
}
ProtoFileLine lineend = new ProtoFileLine();
lineend.LineNum = lineNum;
lineend.LineType = LineType.End;
lineInfoList.Add(lineend);
return lineInfoList;
}
{
ProtoFileLine lineinfo = GetSimpleLineInfo(lineNum, line);
if (lineinfo != null)
{
lineInfoList.Add(lineinfo);
}
}
return lineInfoList;
}
private ProtoFileLine GetSimpleLineInfo(int lineNum, string line)
{
line = line.Trim();
if (string.IsNullOrEmpty(line))
{
return null;
}
ProtoFileLine lineInfo = new ProtoFileLine();
lineInfo.LineNum = lineNum;
line = line.Replace('=',' ');
line = line.Replace('[', ' ');
line = line.Replace(']', ' ');
line = line.Replace(',', ' ');
while (line.Contains(" "))
{
line = line.Replace(" ", " ");
}
line = line.Trim();
string[] splits = line.Split(' ');
lineInfo.Fields = splits;
if (splits[0] == "syntax")
{
lineInfo.LineType = LineType.Syntax;
return lineInfo;
}
if (splits[0] == "package")
{
lineInfo.LineType = LineType.Package;
return lineInfo;
}
if (splits[0] == "option")
{
lineInfo.LineType = LineType.Option;
return lineInfo;
}
if (splits[0] == "import")
{
lineInfo.LineType = LineType.Import;
return lineInfo;
}
if (splits[0] == "enum")
{
lineInfo.LineType = LineType.Enum;
return lineInfo;
}
if (splits[0] == "message")
{
lineInfo.LineType = LineType.Message;
return lineInfo;
}
//剩下的就是内容
lineInfo.LineType = LineType.FiledContent;
return lineInfo;
}
private void ProccessOnReadEnd()
{
//所有enum加入map
foreach (var oneenum in pd.Enums)
{
pd.EnumsMap.Add(oneenum.Name, oneenum);
}
//所有message加入map
foreach (var onemessage in pd.Messages)
{
pd.MessagesMap.Add(onemessage.Name, onemessage);
}
//遍历所有message,里面有MessageOrEnum类型处理一下
foreach (var onemessage in pd.Messages)
{
foreach (var field in onemessage.Fields)
{
if (field.FieldType == FieldType.MessageOrEnum)
{
ProtoEnum pe = ProtoDescriptorAll.Instance.GetEnumByName(field.TypeName);
if (pe != null)
{
field.FieldType = FieldType.Enum;
field.Enum = pe;
continue;
}
ProtoMessage pm = ProtoDescriptorAll.Instance.GetMessageByName(field.TypeName);
if (pm != null)
{
field.FieldType = FieldType.Message;
field.Message = pm;
continue;
}
ExceptionUtils.Throw("parse error, unknow type {0}"
, field.TypeName);
}
}
}
//遍历所有message,计算一下大小什么的
foreach (var onemessage in pd.Messages)
{
onemessage.Calc();
}
}
private void ParseGenClassDescCfg(string file)
{
if (string.IsNullOrEmpty(file))
{
return;
}
if (! File.Exists(file))
{
Console.WriteLine("genclasscfg file {0} not exist", file);
return;
}
string[] allline = File.ReadAllLines(file);
for (int i = 0; i < allline.Length; i++)
{
// 跳过注释
if (allline[i].Contains("//"))
{
continue;
}
if (pd.MessagesMap.TryGetValue(allline[i], out var message))
{
pd.MessagesNeedClassDesc.Add(message);
}
}
}
}
}