Inheritance: NamedItemInfo, IArgumentInfo
        private static IEnumerable<IArgumentInfo> ParseArgumentItem(RdParseContext context) {
            List<IArgumentInfo> arguments = null;

            TokenStream<RdToken> tokens = context.Tokens;
            tokens.Advance(1);

            // Past '\item'. Inside { } we can find any number of '\dots' which are keywords.
            Debug.Assert(tokens.CurrentToken.TokenType == RdTokenType.OpenCurlyBrace);

            if (tokens.CurrentToken.TokenType == RdTokenType.OpenCurlyBrace) {
                int startTokenIndex, endTokenIndex;
                if (RdParseUtility.GetKeywordArgumentBounds(tokens, out startTokenIndex, out endTokenIndex)) {
                    TextRange range = TextRange.FromBounds(tokens[startTokenIndex].End, tokens[endTokenIndex].Start);
                    string argumentsText = context.TextProvider.GetText(range);

                    string[] argumentNames = argumentsText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    arguments = new List<IArgumentInfo>();

                    // Move past \item{}
                    tokens.Position = endTokenIndex + 1;
                    Debug.Assert(tokens.CurrentToken.TokenType == RdTokenType.OpenCurlyBrace);

                    if (tokens.CurrentToken.TokenType == RdTokenType.OpenCurlyBrace) {
                        string description = RdText.GetText(context);

                        foreach (string n in argumentNames) {
                            string name = n.Trim();
                            if (name == @"\dots") {
                                name = "...";
                            }

                            ArgumentInfo info = new ArgumentInfo(name, description.Trim());
                            arguments.Add(info);
                        }
                    }
                }
            }

            return arguments;
        }
        private static ISignatureInfo ParseSignature(string functionName, ParseContext context, IReadOnlyDictionary<string, string> argumentsDescriptions = null) {
            SignatureInfo info = new SignatureInfo(functionName);
            List<IArgumentInfo> signatureArguments = new List<IArgumentInfo>();

            // RD data may contain function name(s) without braces
            if (context.Tokens.CurrentToken.TokenType == RTokenType.OpenBrace) {
                FunctionCall functionCall = new FunctionCall();
                functionCall.Parse(context, context.AstRoot);

                for (int i = 0; i < functionCall.Arguments.Count; i++) {
                    IAstNode arg = functionCall.Arguments[i];

                    string argName = null;
                    string argDefaultValue = null;
                    bool isEllipsis = false;
                    bool isOptional = false;

                    ExpressionArgument expArg = arg as ExpressionArgument;
                    if (expArg != null) {
                        argName = context.TextProvider.GetText(expArg.ArgumentValue);
                    } else {
                        NamedArgument nameArg = arg as NamedArgument;
                        if (nameArg != null) {
                            argName = context.TextProvider.GetText(nameArg.NameRange);
                            argDefaultValue = RdText.CleanRawRdText(context.TextProvider.GetText(nameArg.DefaultValue));
                        } else {
                            MissingArgument missingArg = arg as MissingArgument;
                            if (missingArg != null) {
                                argName = string.Empty;
                            } else {
                                EllipsisArgument ellipsisArg = arg as EllipsisArgument;
                                if (ellipsisArg != null) {
                                    argName = "...";
                                    isEllipsis = true;
                                }
                            }
                        }
                    }

                    ArgumentInfo argInfo = new ArgumentInfo(argName);
                    argInfo.DefaultValue = argDefaultValue;
                    argInfo.IsEllipsis = isEllipsis;
                    argInfo.IsOptional = isOptional; // TODO: actually parse

                    if (argumentsDescriptions != null) {
                        string description;
                        if (argumentsDescriptions.TryGetValue(argName, out description)) {
                            argInfo.Description = description;
                        }
                    }
                    signatureArguments.Add(argInfo);
                }
            }

            info.Arguments = signatureArguments;
            return info;
        }