using System.Data; using System.Text.RegularExpressions; namespace etoy { sealed class MetadataTableCsvParser { const int START_ROW = 1; // 正则解析Metadata结构体 Regex _structRegex = new Regex(@"(?""?)(\w+)\s*?{([\w\s:;\[\]]+)}(\k)"); Regex _structFieldRegex = new Regex(@"(\w+)\s*?:\s*?([\w]+?)\s*?(\[\])?\s*?(;|$)"); // 正则解析Metadata枚举 Regex _enumRegex = new Regex(@"(?""?)(\w+)\s*?{([\w\s=,]+)}(\k)"); Regex _enumFieldRegex = new Regex(@"(\w+)\s*?(=\s*?(\d+))?\s*?,?"); public MetadataTable Parser(DataTable dt, string file) { int rowCount = dt.Rows.Count; int colCount = dt.Columns.Count; if (rowCount <= 0 || colCount < 3) throw new Exception($"Metadata表结构不合法, Row: {rowCount}, Column: {colCount}"); Dictionary structs = new Dictionary(); List rows = new List(); for (int i = START_ROW; i < rowCount; i++) { MetadataRow row = new MetadataRow(); row.Row = i; for (int j = 0; j < colCount; j++) { var cellvalue = dt.Rows[i][j].ToString().ToCellValue(); MetadataColumnType type = (MetadataColumnType)j; switch (type) { case MetadataColumnType.Type: row.Type = cellvalue.ToFieldType(); break; case MetadataColumnType.Value: row.Value = cellvalue; break; case MetadataColumnType.Description: row.Description = cellvalue; break; default: throw new Exception(); } } rows.Add(row); } // 解析Struct & Enum for (int i = 0, length = rows.Count; i < length; i++) { var row = rows[i]; if (string.IsNullOrEmpty(row.Type)) throw new Exception($"Metadata 类型为空(行:{i}) {file}"); if (Enum.TryParse(row.Type, true, out var structType)) { if (TryParseMetadata(structType, row.Value, out var @struct)) { if (structs.ContainsKey(@struct.Name)) throw new Exception($"Metadata 类型重复定义了(行:{i}) ({row.Value}) {file}"); @struct.StructType = structType; @struct.Description = row.Description; // 两个地方都存, 方便生成代码使用 row.Struct = @struct; structs[@struct.Name] = @struct; } else throw new Exception($"Metadata 类型解析失败(行:{i}) ({row.Value}) {file}"); } else throw new Exception($"Metadata 不支持类型({row.Type}), 请修改 {file}"); } MetadataTable table = new MetadataTable(); table.Structs = structs; table.Rows = rows.ToArray(); table.Path = file; table.Name = Path.GetFileNameWithoutExtension(file).ToTableName(); return table; } bool TryParseMetadata(MetadataStructType type, string metadata, out MetadataStruct @struct) { @struct = default; bool bRet = false; switch (type) { case MetadataStructType.Struct: bRet = ParseStruct(metadata, out @struct); break; case MetadataStructType.Enum: bRet = ParseEnum(metadata, out @struct); break; } return bRet; } bool Parse(string metadata, Regex regex, out string typeName, out string typeBody) { typeName = typeBody = string.Empty; Match match = regex.Match(metadata); if (!match.Success) return false; typeName = match.Groups[1].Value; typeBody = match.Groups[2].Value; return true; } bool ParseStruct(string metadata, out MetadataStruct @struct) { @struct = new MetadataStruct(); if (!Parse(metadata, _structRegex, out string typeName, out string typeBody)) return false; @struct.Name = typeName; MatchCollection collection = _structFieldRegex.Matches(typeBody); if (null == collection || collection.Count == 0) return false; int length = collection.Count; @struct.Fields = new MetadataField[length]; for (int i = 0; i < length; i++) { var groups = collection[i].Groups; var fieldInfo = new MetadataField(); fieldInfo.Name = groups[1].Value; fieldInfo.FieldType = groups[2].Value; fieldInfo.StructRepeated = !string.IsNullOrEmpty(groups[3].Value); @struct.Fields[i] = fieldInfo; } return true; } bool ParseEnum(string metadata, out MetadataStruct @struct) { @struct = new MetadataStruct(); if (!Parse(metadata, _enumRegex, out string typeName, out string typeBody)) return false; @struct.Name = typeName; MatchCollection collection = _enumFieldRegex.Matches(typeBody); if (null == collection || collection.Count == 0) return false; int length = collection.Count; @struct.Fields = new MetadataField[length]; for (int i = 0; i < length; i++) { var groups = collection[i].Groups; var fieldInfo = new MetadataField(); fieldInfo.Name = groups[1].Value; var enumValue = groups[3].Value; fieldInfo.EnumValue = enumValue; var enumIntVal = 0; if (string.IsNullOrEmpty(enumValue) || int.TryParse(enumValue, out enumIntVal) == false) { if (i == 0) { // enumIntVal = 0; } else { // 上一个枚举值加一 enumIntVal = @struct.Fields[i - 1].EnumIntVal + 1; } } fieldInfo.EnumIntVal = enumIntVal; @struct.Fields[i] = fieldInfo; } return true; } } }