public ParserResult Parse(VirtualFile file) { log.LogVerbose($"Parse file {file.FullName}."); List <CodeSpecificException> codeSpecificExceptions = new List <CodeSpecificException>(); List <ParserMessage> messages = new List <ParserMessage>(); IDictionary <IType, CodePosition> types = new Dictionary <IType, CodePosition>(); CppStreamParser parser = new CppStreamParser(); ParseNode root = parser.Parse(file.OpenRead()); if (!parser.Succeeded) { messages.Add(parser.Exception == null ? new ParserMessage("CPP0004", 1, 1) : new ParserMessage("CPP0005", parser.Exception.Row, parser.Exception.Col, $"{Environment.NewLine}{parser.Exception.Message}")); codeSpecificExceptions.AddRange(messages.Select(m => m.ToException(file))); return(new ParserResult(codeSpecificExceptions)); } string[] usings = GetUsings(); foreach (ParseNode typeDeclaration in GetTypeDeclarations(root)) { ParseNode content = GetDeclarationContentParent(typeDeclaration); if (content != null && IsValidDecalration(content, typeDeclaration)) { string name = GetName(typeDeclaration); string ns = GetNamespace(typeDeclaration); switch (typeDeclaration[1].RuleName) { case "struct_decl": CppStructure structure = new CppStructure(ns, name, usings, content, messages, typeDeclaration, settingsProvider.Settings.AttributePrefix); types.Add(structure, new CodePosition(typeDeclaration.Position.line, typeDeclaration.Position.column)); break; case "class_decl": CppClass cppClass = new CppClass(ns, name, usings, content, typeDeclaration[1], messages, settingsProvider.Settings.AttributePrefix); types.Add(cppClass, new CodePosition(typeDeclaration.Position.line, typeDeclaration.Position.column)); break; case "enum_decl": CppEnum cppEnum = new CppEnum(ns, name, usings, content, messages, typeDeclaration[1], settingsProvider.Settings.AttributePrefix); types.Add(cppEnum, new CodePosition(typeDeclaration.Position.line, typeDeclaration.Position.column)); break; default: //do nothing break; } } } codeSpecificExceptions.AddRange(messages.Select(m => m.ToException(file))); string[] includes = GetIncludes(); return(new ParserResult(codeSpecificExceptions, types, includes)); bool IsValidDecalration(ParseNode content, ParseNode typeDeclaration) { return(content.Count >= 2 && content.Any(c => c.GetHierarchy().Contains(typeDeclaration)) && content.SkipWhile(c => !c.GetHierarchy().Contains(typeDeclaration)) .Skip(1).Any(c => c.GetHierarchy().Any(n => n.RuleName == "brace_group"))); } string GetName(ParseNode typeDeclaration) { ParseNode leaf = typeDeclaration.GetHierarchy() .FirstOrDefault(n => n.RuleName == "identifier" || n.RuleName == "generic"); if (leaf == null) { return(Guid.NewGuid().ToByteString()); } return(leaf.ToString()); } ParseNode GetDeclarationContentParent(ParseNode current) { while (current != null && current.RuleType != "plus" && current.RuleName != "declaration_content") { current = current.GetParent(); } return(current); } IEnumerable <ParseNode> GetTypeDeclarations(ParseNode current) { foreach (ParseNode node in current) { if (node.RuleType == "sequence" && node.RuleName == "type_decl") { yield return(node); } else { foreach (ParseNode child in GetTypeDeclarations(node)) { yield return(child); } } } } string[] GetIncludes() { List <string> result = new List <string>(); foreach (ParseNode includeNode in root.GetHierarchy().Where(n => n.RuleType == "sequence" && n.RuleName == "pp_directive" && n.Any(c => c.ToString().Equals("include", StringComparison.OrdinalIgnoreCase)))) { ParseNode include = includeNode.FirstOrDefault(n => n.RuleName == "until_eol"); if (include != null) { result.Add(include.ToString().Trim('\"')); } } return(result.ToArray()); } string[] GetUsings() { List <string> result = new List <string>(); foreach (ParseNode usingNode in root.GetHierarchy().Where(n => n.RuleType == "leaf" && n.RuleName == "identifier" && n.ToString() == "using")) { ParseNode declarationParent = GetDeclarationContentParent(usingNode); string[] identifier = declarationParent.ChildrenSkipUnnamed() .Select(Identifier) .Where(i => i != null) .Select(i => i.ToString()) .ToArray(); if (identifier.Length > 2 && identifier[0] == "using" && identifier[1] == "namespace") { result.Add(identifier.Skip(2).Aggregate(string.Empty, (s, s1) => s + s1)); } } return(result.ToArray()); } ParseNode Identifier(ParseNode parent) { if (parent.RuleType == "choice" && parent.RuleName == "node") { ParseNode result = parent.FirstOrDefault(); if (result?.RuleType == "leaf" && result.RuleName == "identifier") { return(result); } } return(null); } string GetNamespace(ParseNode typeDeclaration) { ParseNode declarationParent; typeDeclaration = GetDeclarationContentParent(typeDeclaration).GetParent(); string result = string.Empty; while ((declarationParent = GetDeclarationContentParent(typeDeclaration)) != null) { string[] identifier = declarationParent.ChildrenSkipUnnamed() .Where(r => r.RuleName != "comment_set") .Select(Identifier) .TakeWhile(i => i != null) .Select(i => i.ToString()) .ToArray(); if (identifier.Length > 1 && identifier[0] == "namespace") { result = $"{identifier.Skip(1).Aggregate(string.Empty, (s, s1) => s + s1)}::{result}"; } else if (identifier.Length == 0) { ParseNode parentTypeDeclaration = declarationParent.ChildrenSkipUnnamed() .Where(c => c.RuleType == "choice" && c.RuleName == "node") .SelectMany(c => c.ChildrenSkipUnnamed()) .FirstOrDefault( c => c.RuleType == "sequence" && c.RuleName == "type_decl"); ParseNode name = parentTypeDeclaration?.GetHierarchy() .FirstOrDefault(n => n.RuleType == "leaf" && n.RuleName == "identifier"); if (name != null) { result = $"{name}::{result}"; } } typeDeclaration = declarationParent.GetParent(); } if (!string.IsNullOrEmpty(result)) { result = result.Substring(0, result.Length - 2); } return(result); } }