/// <summary> /// Tries to parse method definition line. /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="info">[out] Parsed <see cref="MethodInfo"/> (only succeeded in parsing)</param> /// <param name="depth">[out] Depth of method definition line (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> private bool TryParseDefinitionLine(SourceCodeReader reader, out MethodInfo info, out int depth) { if (!reader.TryRead(out var text)) { info = null; depth = 0; return(false); } depth = text.Depth; var match = MethodRegex.Match(text.Text); if (!match.Success) { reader.Position--; info = null; depth = 0; return(false); } var mod = this.ParseModifiers(match.Groups[1].Value); var returnType = string.IsNullOrEmpty(match.Groups[2].Value) ? null : ParseType(match.Groups[2].Value); var name = match.Groups[3].Value; var args = ParseArguments(match.Groups[4].Value); info = new MethodInfo(mod, name, returnType, args); return(true); }
public IEnumerable <ClassInfo> Parse(string code) { if (code == null) { throw new ArgumentNullException(); } var reader = new SourceCodeReader(code); var classList = new List <ClassInfo>(); var classParser = new ClassParser(string.Empty, Modifier.Package); while (!reader.IsEndOfLines) { if (TryParsePackage(reader, out var package)) { classParser = new ClassParser(package, Modifier.Package); continue; } if (classParser.TryParse(reader, out var classInfo)) { classList.Add(classInfo); continue; } reader.TryRead(out var _); } return(classList); }
/// <summary> /// Tries to parse class definition line. /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="parentClassName">Parent class name added to a parsed class</param> /// <param name="classInfo">[out] Parsed <see cref="ClassInfo"/> (only succeeded in parsing)</param> /// <param name="depth">[out] Depth of class definition line (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> private bool TryParseDefinitionLine(SourceCodeReader reader, string parentClassName, out ClassInfo classInfo, out int depth) { if (!reader.TryRead(out var text)) { classInfo = null; depth = 0; return(false); } depth = text.Depth; var match = ClassRegex.Match(text.Text); if (!match.Success) { reader.Position--; classInfo = null; depth = 0; return(false); } var parentName = string.IsNullOrEmpty(parentClassName) ? string.Empty : parentClassName + "."; var mod = this.ParseModifiers(match.Groups[1].Value); var category = ParseClassCategory(match.Groups[2].Value); var type = ParseType(parentName + match.Groups[3].Value); var inheriteds = ParseInheritance(match.Groups[4].Value); classInfo = new ClassInfo(mod, category, this.package, type, inheriteds); return(true); }
public override bool TryParse(SourceCodeReader reader, out FieldInfo info) { if (!reader.TryRead(out var text)) { info = null; return(false); } var depth = text.Depth; var match = FieldRegex.Match(text.Text); if (!match.Success) { reader.Position--; info = null; return(false); } var idxrArgs = match.Groups[4].Value; var mod = this.ParseModifiers(match.Groups[1].Value); var type = string.IsNullOrWhiteSpace(match.Groups[2].Value) ? null : ParseType(match.Groups[2].Value); var name = match.Groups[3].Value; var args = ParseArguments(Regex.Replace(idxrArgs, "(\\s*\\[\\s*|\\s*\\]\\s*)", string.Empty)); // Parsing must be treated as failure if parsed type name matches modifier, // because field regex pattern matches invalid pattern below. // ex) "public int" -> Type : public, FiledName : int if (AllModifiers.Contains(type.Name)) { reader.Position--; info = null; return(false); } this.ParseImplementationLines(reader, depth, out var propTypeFromImpl); var propType = PropertyType.None; var isIndexer = !string.IsNullOrEmpty(idxrArgs); var hasGetter = !string.IsNullOrEmpty(match.Groups[5].Value); var hasDefault = !string.IsNullOrEmpty(match.Groups[6].Value); if (isIndexer) { propType |= PropertyType.Indexer; } if (hasGetter) { propType |= PropertyType.Get; } if (!hasDefault) { // If definition line contains a default value assignment expression, this field is not a property. // If this field is a property, adds a flag parsed from implementation lines. propType |= propTypeFromImpl; } info = new FieldInfo(mod, name, type, propType, args); return(true); }
/// <summary> /// Tries to parsing a class. /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="parentClassName">Parent class name added to a parsed class</param> /// <param name="info">[out] Parsed class (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> public bool TryParseInternal(SourceCodeReader reader, string parentClassName, out ClassInfo info) { if (!this.TryParseDefinitionLine(reader, parentClassName, out info, out var depth)) { return(false); } this.ParseImplementationLines(reader, info, info.Name, depth); return(true); }
public override bool TryParse(SourceCodeReader reader, out MethodInfo info) { if (!this.TryParseDefinitionLine(reader, out info, out var depth)) { return(false); } this.ParseImplementationLines(reader, info, depth); return(true); }
/// <summary> /// Checks whether the depth of next line of <see cref="SourceCodeReader"/> is <paramref name="depth"/> or not. /// <para>If failed to read, returns false.</para> /// <para>This processing does not change position of <see cref="SourceCodeReader"/>.</para> /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/> to be checked.</param> /// <param name="depth">Expected depth</param> /// <returns>Whether the depth of next line is <paramref name="depth"/> or not</returns> private static bool IsNextLineDepth(SourceCodeReader reader, int depth) { if (!reader.TryRead(out var text)) { return(false); } reader.Position--; return(text.Depth == depth); }
/// <summary> /// Parses implementation lines. /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="info"><see cref="MethodInfo"/> to hold implementation contents</param> /// <param name="definitionDepth">Depth of method definition line</param> private void ParseImplementationLines(SourceCodeReader reader, MethodInfo info, int definitionDepth) { var subLines = GetMoreDeepLineCount(reader, definitionDepth); for (var i = 0; i < subLines; i++) { reader.TryRead(out var sub); // Skip implementation lines // TODO Gets used types in implementation lines } }
/// <summary> /// Parses implementation lines. /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="classInfo"><see cref="ClassInfo"/> to hold implementation contents</param> /// <param name="parentClassName">Parent class name added to a parsed class</param> /// <param name="definitionDepth">Depth of class definition line</param> private void ParseImplementationLines(SourceCodeReader reader, ClassInfo classInfo, string parentClassName, int definitionDepth) { var endOfClass = reader.Position + GetMoreDeepLineCount(reader, definitionDepth); var methodParser = new MethodParser(classInfo, this.DefaultAccessLevel); var fieldParser = new FieldParser(classInfo, this.DefaultAccessLevel); var enumParser = new EnumValuesParser(classInfo, definitionDepth); var isFirstLine = true; while (reader.Position < endOfClass) { if (!IsNextLineDepth(reader, definitionDepth + 1)) { reader.TryRead(out var _); continue; } if (isFirstLine && (classInfo.Category == ClassCategory.Enum) && enumParser.TryParse(reader, out var valuesFL)) { // Parsing enum values of first line is only executed at first classInfo.Fields.AddRange(valuesFL); } else if (this.TryParseInternal(reader, parentClassName, out var innerInfo)) { classInfo.InnerClasses.Add(innerInfo); } else if (methodParser.TryParse(reader, out var methodInfo)) { classInfo.Methods.Add(methodInfo); } else if (fieldParser.TryParse(reader, out var fieldInfo)) { // Parsing filed is executed after trying to parse method because field pattern also matches method classInfo.Fields.Add(fieldInfo); } else if (!isFirstLine && (classInfo.Category == ClassCategory.Enum) && enumParser.TryParse(reader, out var values)) { // Parsing enum values is executed after trying to parse method and field just as with field parsing (except for first line) classInfo.Fields.AddRange(values); } else { // Skip a line if it did not match any pattern reader.TryRead(out var _); } isFirstLine = false; } }
/// <summary> /// Gets a line count from current position of <paramref name="reader"/> to end of line that has more deep <paramref name="depth"/> continuously. /// <para>The position of <paramref name="reader"/> after this processing is the same as that of before this processing.</para> /// </summary> /// <param name="reader">Reader</param> /// <param name="depth">Depth threshold (excluding self)</param> /// <returns>A line count coutinuous more deep lines</returns> /// <exception cref="ArgumentNullException">If <paramref name="reader"/> is null</exception> protected static int GetMoreDeepLineCount(SourceCodeReader reader, int depth) { if (reader == null) { throw new ArgumentNullException(); } var start = reader.Position; var deepLines = 0; while (reader.TryRead(out var t) && (t.Depth > depth)) { deepLines++; } reader.Position = start; return(deepLines); }
/// <summary> /// Tries to parse a package. /// <para>If failed to parse, the position of <paramref name="reader"/> after this processing is the same as that of before this processing.</para> /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="package">[out] Package name (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> private static bool TryParsePackage(SourceCodeReader reader, out string package) { if (!reader.TryRead(out var text)) { package = null; return(false); } var match = PackageRegex.Match(text.Text); if (!match.Success) { reader.Position--; package = null; return(false); } package = match.Groups[1].Value; return(true); }
public IEnumerable <ClassInfo> Parse(string code) { if (code == null) { throw new ArgumentNullException(); } var reader = new SourceCodeReader(code); var classList = new List <ClassInfo>(); var classParser = new ClassParser(string.Empty, Modifier.Internal); var prevNS = new Dictionary <int, string>(); while (!reader.IsEndOfLines) { if (TryParseNameSpace(reader, out var nameSpace, out var depth)) { // Already existed parent namespace, Add it before a nameSpace if (prevNS.ContainsKey(depth - 1)) { nameSpace = prevNS[depth - 1] + "." + nameSpace; } prevNS[depth] = nameSpace; classParser = new ClassParser(nameSpace, Modifier.Internal); continue; } if (classParser.TryParse(reader, out var classInfo)) { classList.Add(classInfo); continue; } reader.TryRead(out var _); } return(classList); }
public override bool TryParse(SourceCodeReader reader, out IEnumerable <FieldInfo> values) { if (!reader.TryRead(out var text)) { values = null; return(false); } if (text.Depth != this.definitionDepth + 1) { reader.Position--; values = null; return(false); } values = text.Text.Split(',') .Select(s => EnumValueRegex.Match(s)) .Where(m => m.Success) .Select(m => m.Groups[1].Value) .Select(this.CreateFieldFromValueName); return(true); }
/// <summary> /// Tries to parse a namespace. /// <para>If failed to parse, the position of <paramref name="reader"/> after this processing is the same as that of before this processing.</para> /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="nameSpace">[out] Namespace (only succeeded in parsing)</param> /// <param name="depth">[out] Depth of namespace (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> private static bool TryParseNameSpace(SourceCodeReader reader, out string nameSpace, out int depth) { if (!reader.TryRead(out var text)) { nameSpace = null; depth = 0; return(false); } var match = NameSpaceRegex.Match(text.Text); if (!match.Success) { reader.Position--; nameSpace = null; depth = 0; return(false); } nameSpace = match.Groups[1].Value; depth = text.Depth; return(true); }
/// <summary> /// Parses implementation lines. /// <para>If declarations of getter or setter appears, adds <see cref="PropertyType"/> to <paramref name="propType"/>.</para> /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="definitionDepth">Depth of field definition line</param> /// <param name="propType"><see cref="PropertyType"/> to be added getter or setter flags</param> private void ParseImplementationLines(SourceCodeReader reader, int definitionDepth, out PropertyType propType) { var subLines = GetMoreDeepLineCount(reader, definitionDepth); propType = PropertyType.None; // Skips all implementation lines, and checks getter or setter declarations. for (var i = 0; i < subLines; i++) { if (!reader.TryRead(out var sub) || sub.Depth != definitionDepth + 1) { continue; } if (GetterRegex.IsMatch(sub.Text)) { propType |= PropertyType.Get; } if (SetterRegex.IsMatch(sub.Text)) { propType |= PropertyType.Set; } } }
/// <summary> /// Tries to parse component of source code. /// <para>Tries read a line from <paramref name="reader"/>, and if succeeded in parsing, output parsed component.</para> /// <para>If succeeded in parsing, the position of the <paramref name="reader"/> is seeked by the number of the read lines. /// Otherwise, the position of <paramref name="reader"/> is not changed.</para> /// </summary> /// <param name="reader"><see cref="SourceCodeReader"/></param> /// <param name="obj">[out] Parsed component (only succeeded in parsing)</param> /// <returns>Whether succeeded in parsing or not</returns> public abstract bool TryParse(SourceCodeReader reader, out T obj);
public override bool TryParse(SourceCodeReader reader, out ClassInfo info) { return(this.TryParseInternal(reader, string.Empty, out info)); }