private bool ParseFunction(LinkedListStream <IBaseToken> stream) { var functionIdentNode = stream.CurrentNode; CppToken functionIdentToken = functionIdentNode.Value as CppToken; Debug.Assert(functionIdentToken.Kind == CppTokenKind.IdentLiteral); var functionName = functionIdentToken.Value; CppTokenKind[] funcCallKinds = { CppTokenKind.EqOp, CppTokenKind.Comma, CppTokenKind.LeftParen, CppTokenKind.AddOp, CppTokenKind.AddAssign, CppTokenKind.SubOp, CppTokenKind.SubAssign, CppTokenKind.DivOp, CppTokenKind.DivAssign, CppTokenKind.MulAssign, CppTokenKind.XorOp, CppTokenKind.XorAssign, CppTokenKind.LeftShiftOp, CppTokenKind.LeftShiftAssign, CppTokenKind.RightShiftOp, CppTokenKind.RightShiftAssign, }; var funcCallResult = Search(stream, SearchMode.Prev, funcCallKinds); CppEntityKind kind = CppEntityKind.FunctionCall; // Skip ident stream.Next(); // Skip parameters CppToken openParenToken = stream.Peek <CppToken>(); Debug.Assert(openParenToken.Kind == CppTokenKind.LeftParen); stream.Next(); var closeParenResult = Search(stream, SearchMode.Forward, CppTokenKind.RightParen); if (closeParenResult == null) { AddError(openParenToken.Position, $"Unterminated function '{functionName}'", "Function", functionName); return(true); } stream.Seek(closeParenResult.Node); stream.Next(); if (funcCallResult == null) { var endingTokenResult = Search(stream, SearchMode.Current, CppTokenKind.Semicolon, CppTokenKind.LeftBrace); if (endingTokenResult != null) { stream.Next(); if (endingTokenResult.Token.Kind == CppTokenKind.LeftBrace) { kind = CppEntityKind.FunctionBody; if (Configuration.SkipFunctionBlocks) { return(true); } } else { kind = CppEntityKind.FunctionDefinition; } } } CppEntity functionEntity = new CppEntity(kind, functionIdentToken, functionName) { DocumentationNode = FindDocumentationNode(functionIdentNode, 1), }; CppNode functionNode = new CppNode(Top, functionEntity); Add(functionNode); if (kind == CppEntityKind.FunctionDefinition) { SymbolCache.AddSource(Tag, functionName, new SourceSymbol(SourceSymbolKind.CppFunctionDefinition, functionIdentToken.Range, functionNode)); } else if (kind == CppEntityKind.FunctionBody) { SymbolCache.AddSource(Tag, functionName, new SourceSymbol(SourceSymbolKind.CppFunctionBody, functionIdentToken.Range, functionNode)); } else if (Configuration.FunctionCallSymbolsEnabled) { SymbolCache.AddReference(Tag, functionName, new ReferenceSymbol(ReferenceSymbolKind.CppFunction, functionIdentToken.Range, functionNode)); } return(true); }
private bool ParseCommand(LinkedListStream <IBaseToken> stream, IBaseNode contentRoot) { // @NOTE(final): This must always return true, due to the fact that the stream is advanced at least once DoxygenToken commandToken = stream.Peek <DoxygenToken>(); Debug.Assert(commandToken != null && commandToken.Kind == DoxygenTokenKind.Command); string commandName = commandToken.Value.Substring(1); stream.Next(); string typeName = "Command"; var rule = DoxygenSyntax.GetCommandRule(commandName); if (rule != null) { if (rule.Kind == DoxygenSyntax.CommandKind.EndCommandBlock) { var t = Top; if (t == null) { AddError(commandToken.Position, $"Unterminated starting command block in command '{commandName}'", typeName, commandName); return(false); } if (t.Entity.Kind != DoxygenEntityKind.BlockCommand) { AddError(commandToken.Position, $"Expect starting command block, but found '{t.Entity.Kind}' in command '{commandName}'", typeName, commandName); return(false); } Pop(); } // Paragraph or section command starts or command block starts -> Close previous paragraph or sectioning command if (rule.Kind == DoxygenSyntax.CommandKind.Paragraph || rule.Kind == DoxygenSyntax.CommandKind.Section || rule.Kind == DoxygenSyntax.CommandKind.StartCommandBlock) { var t = Top; if (t != null) { if (t.Entity.Kind == DoxygenEntityKind.Paragraph || t.Entity.Kind == DoxygenEntityKind.Section || t.Entity.Kind == DoxygenEntityKind.SubSection || t.Entity.Kind == DoxygenEntityKind.SubSubSection) { Pop(); } } } DoxygenEntity commandEntity = null; IEntityBaseNode <DoxygenEntity> commandNode = null; if (rule.EntityKind != DoxygenEntityKind.None) { commandEntity = new DoxygenEntity(rule.EntityKind, commandToken.Range); commandEntity.Id = commandName; commandNode = new DoxygenNode(Top, commandEntity); if (rule.IsPush) { Push(commandNode); } else { Add(commandNode); } } foreach (var arg in rule.Args) { DoxygenToken argToken = stream.Peek <DoxygenToken>(); if (argToken == null) { break; } DoxygenTokenKind expectedTokenKind = DoxygenSyntax.ArgumentToTokenKindMap.ContainsKey(arg.Kind) ? DoxygenSyntax.ArgumentToTokenKindMap[arg.Kind] : DoxygenTokenKind.Invalid; if (expectedTokenKind == DoxygenTokenKind.Invalid) { break; } if (expectedTokenKind != argToken.Kind) { AddError(argToken.Position, $"Expect argument token '{expectedTokenKind}', but got '{argToken.Kind}'", typeName, commandName); break; } if (commandNode != null) { string paramName = arg.Name; string paramValue = argToken.Value; commandNode.Entity.AddParameter(argToken, paramName, paramValue); } stream.Next(); } if (commandEntity != null) { // Get name and text parameter (Some commands, have different names and text parameters, so there is a variable list of strings) var nameParam = commandEntity.FindParameterByName("name", "id"); var textParam = commandEntity.FindParameterByName("text", "title", "caption"); if (nameParam == null || string.IsNullOrWhiteSpace(nameParam.Value)) { if (rule.Kind == DoxygenSyntax.CommandKind.Section) { if (!"mainpage".Equals(commandName)) { AddError(commandToken.Position, $"Missing identifier mapping for command '{commandName}'", typeName, commandName); } } } if (nameParam != null && !string.IsNullOrWhiteSpace(nameParam.Value)) { string symbolName = nameParam.Value; Debug.Assert(commandNode != null); if (rule.Kind == DoxygenSyntax.CommandKind.Section) { SourceSymbolKind kind = SourceSymbolKind.DoxygenSection; if ("page".Equals(commandName) || "mainpage".Equals(commandName)) { kind = SourceSymbolKind.DoxygenPage; } SymbolCache.AddSource(Tag, symbolName, new SourceSymbol(kind, nameParam.Token.Range, commandNode)); } else if ("ref".Equals(commandName) || "refitem".Equals(commandName)) { string referenceValue = nameParam.Value; TextPosition startPos = new TextPosition(0, nameParam.Token.Position.Line, nameParam.Token.Position.Column); using (TextStream referenceTextStream = new BasicTextStream(referenceValue, startPos, referenceValue.Length)) { ReferenceSymbolKind referenceTarget = ReferenceSymbolKind.Any; while (!referenceTextStream.IsEOF) { char first = referenceTextStream.Peek(); char second = referenceTextStream.Peek(1); char third = referenceTextStream.Peek(2); if (SyntaxUtils.IsIdentStart(first)) { referenceTextStream.StartLexeme(); while (!referenceTextStream.IsEOF) { if (!SyntaxUtils.IsIdentPart(referenceTextStream.Peek())) { break; } referenceTextStream.AdvanceColumn(); } var refRange = referenceTextStream.LexemeRange; string singleRereference = referenceTextStream.GetSourceText(refRange.Index, refRange.Length); if (referenceTextStream.Peek() == '(') { referenceTarget = ReferenceSymbolKind.CppFunction; referenceTextStream.AdvanceColumn(); while (!referenceTextStream.IsEOF) { if (referenceTextStream.Peek() == ')') { break; } referenceTextStream.AdvanceColumn(); } } var symbolRange = new TextRange(new TextPosition(nameParam.Token.Position.Index + refRange.Position.Index, refRange.Position.Line, refRange.Position.Column), refRange.Length); SymbolCache.AddReference(Tag, singleRereference, new ReferenceSymbol(referenceTarget, symbolRange, commandNode)); } else if (first == '#' || first == '.') { referenceTarget = ReferenceSymbolKind.CppMember; referenceTextStream.AdvanceColumn(); } else if (first == ':' || second == ':') { referenceTarget = ReferenceSymbolKind.CppMember; referenceTextStream.AdvanceColumns(2); } else { break; } } } } else if ("subpage".Equals(commandName)) { SymbolCache.AddReference(Tag, symbolName, new ReferenceSymbol(ReferenceSymbolKind.DoxygenPage, nameParam.Token.Range, commandNode)); } } } ParseBlockContent(stream, commandNode); } else { AddError(commandToken.Position, $"No parse rule for command '{commandName}' found", "Command", commandName); } return(true); }