Ejemplo n.º 1
0
        public (GreenJsonValueSyntax, JsonSymbolType) ParseMap()
        {
            var mapBuilder            = new List <GreenJsonKeyValueSyntax>();
            var keyValueSyntaxBuilder = new List <GreenJsonMultiValueSyntax>();

            // Maintain a separate set of keys to aid error reporting on duplicate keys.
            HashSet <string> foundKeys = new HashSet <string>();

            for (; ;)
            {
                // Save CurrentLength for error reporting before parsing the key.
                int keyStart = CurrentLength;
                GreenJsonMultiValueSyntax multiKeyNode  = ParseMultiValue(JsonErrorCode.MultiplePropertyKeys);
                GreenJsonValueSyntax      parsedKeyNode = multiKeyNode.ValueNode.ContentNode;

                // Analyze if this is an actual, unique property key.
                int  parsedKeyNodeStart = keyStart + multiKeyNode.ValueNode.BackgroundBefore.Length;
                bool gotKey;
                Maybe <GreenJsonStringLiteralSyntax> validKey = Maybe <GreenJsonStringLiteralSyntax> .Nothing;

                switch (parsedKeyNode)
                {
                case GreenJsonMissingValueSyntax _:
                    gotKey = false;
                    break;

                case GreenJsonStringLiteralSyntax stringLiteral:
                    gotKey = true;
                    string propertyKey = stringLiteral.Value;

                    // Expect unique keys.
                    if (!foundKeys.Contains(propertyKey))
                    {
                        validKey = stringLiteral;
                        foundKeys.Add(propertyKey);
                    }
                    else
                    {
                        Report(new JsonErrorInfo(
                                   JsonErrorCode.PropertyKeyAlreadyExists,
                                   parsedKeyNodeStart,
                                   parsedKeyNode.Length,
                                   // Take the substring, key may contain escape sequences.
                                   new JsonErrorInfoParameter <string>(Json.Substring(parsedKeyNodeStart + 1, parsedKeyNode.Length - 2))));
                    }
                    break;

                default:
                    gotKey = true;
                    Report(new JsonErrorInfo(
                               JsonErrorCode.InvalidPropertyKey,
                               parsedKeyNodeStart,
                               parsedKeyNode.Length));
                    break;
                }

                // Reuse keyValueSyntaxBuilder.
                keyValueSyntaxBuilder.Clear();
                keyValueSyntaxBuilder.Add(multiKeyNode);

                // Keep parsing multi-values until encountering a non ':'.
                JsonSymbolType symbolType = CurrentToken.SymbolType;
                bool           gotColon   = false;
                while (symbolType == JsonSymbolType.Colon)
                {
                    if (gotColon)
                    {
                        // Multiple ':' without a ','.
                        Report(new JsonErrorInfo(
                                   JsonErrorCode.MultiplePropertyKeySections,
                                   CurrentLength - CurrentToken.Length,
                                   CurrentToken.Length));
                    }
                    else
                    {
                        gotColon = true;
                    }

                    // ParseMultiValue() guarantees that the next symbol is never a ValueStartSymbol.
                    keyValueSyntaxBuilder.Add(ParseMultiValue(JsonErrorCode.MultipleValues));
                    symbolType = CurrentToken.SymbolType;
                }

                // One key-value section done.
                var jsonKeyValueSyntax = new GreenJsonKeyValueSyntax(validKey, keyValueSyntaxBuilder);

                mapBuilder.Add(jsonKeyValueSyntax);

                bool isComma      = symbolType == JsonSymbolType.Comma;
                bool isCurlyClose = symbolType == JsonSymbolType.CurlyClose;

                // '}' directly following a ',' should not report errors.
                // '..., : }' however misses both a key and a value.
                if (isComma || (isCurlyClose && (gotKey || gotColon)))
                {
                    // Report missing property key and/or value.
                    if (!gotKey)
                    {
                        Report(new JsonErrorInfo(
                                   JsonErrorCode.MissingPropertyKey,
                                   CurrentLength - CurrentToken.Length,
                                   CurrentToken.Length));
                    }

                    // Report missing value error from being reported if all value sections are empty.
                    // Example: { "key1":: 2, "key2": , }
                    // Skip the fist value section, it contains the key node.
                    if (jsonKeyValueSyntax.ValueSectionNodes.Skip(1).All(x => x.ValueNode.ContentNode is GreenJsonMissingValueSyntax))
                    {
                        Report(new JsonErrorInfo(
                                   JsonErrorCode.MissingValue,
                                   CurrentLength - CurrentToken.Length,
                                   CurrentToken.Length));
                    }
                }

                if (!isComma)
                {
                    if (isCurlyClose)
                    {
                        return(new GreenJsonMapSyntax(mapBuilder, missingCurlyClose: false), ShiftToNextForegroundToken());
                    }

                    // ']', EOF; assume missing closing bracket '}'.
                    Report(new JsonErrorInfo(
                               symbolType == JsonSymbolType.Eof ? JsonErrorCode.UnexpectedEofInObject : JsonErrorCode.ControlSymbolInObject,
                               CurrentLength - CurrentToken.Length,
                               CurrentToken.Length));

                    return(new GreenJsonMapSyntax(mapBuilder, missingCurlyClose: true), symbolType);
                }
            }
        }
 public virtual TResult Visit(GreenJsonValueSyntax node, T arg) => node == null ? default : node.Accept(this, arg);
 /// <summary>
 /// Initializes a new instance of <see cref="GreenJsonValueWithBackgroundSyntax"/>.
 /// </summary>
 /// <param name="backgroundBefore">
 /// The background symbols which directly precede the content value node.
 /// </param>
 /// <param name="contentNode">
 /// The content node containing the actual value.
 /// </param>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="backgroundBefore"/> and/or <paramref name="contentNode"/> are null.
 /// </exception>
 public GreenJsonValueWithBackgroundSyntax(GreenJsonBackgroundListSyntax backgroundBefore, GreenJsonValueSyntax contentNode)
 {
     BackgroundBefore = backgroundBefore ?? throw new ArgumentNullException(nameof(backgroundBefore));
     ContentNode = contentNode ?? throw new ArgumentNullException(nameof(contentNode));
     Length = BackgroundBefore.Length + ContentNode.Length;
 }
 public virtual TResult DefaultVisit(GreenJsonValueSyntax node, T arg) => default;