private JsonMultiValueSyntax(Union <_void, JsonListSyntax, JsonKeyValueSyntax> parent, GreenJsonMultiValueSyntax green) { Parent = parent; Green = green; ValueNodes = new SafeLazyObjectCollection <JsonValueWithBackgroundSyntax>( green.ValueNodes.Count, index => new JsonValueWithBackgroundSyntax(this, index)); backgroundAfter = new SafeLazyObject <JsonBackgroundListSyntax>(() => new JsonBackgroundListSyntax(this)); }
/// <summary> /// Initializes a new instance of <see cref="RootJsonSyntax"/>. /// </summary> /// <param name="syntax"> /// The root <see cref="GreenJsonMultiValueSyntax"/> node from which to construct a <see cref="JsonMultiValueSyntax"/> abstract syntax tree. /// </param> /// <param name="errors"> /// The enumeration containing all errors generated during a parse. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="syntax"/> and/or <paramref name="errors"/> are null. /// </exception> public RootJsonSyntax(GreenJsonMultiValueSyntax syntax, IEnumerable <JsonErrorInfo> errors) { if (syntax == null) { throw new ArgumentNullException(nameof(syntax)); } if (errors == null) { throw new ArgumentNullException(nameof(errors)); } Syntax = new JsonMultiValueSyntax(syntax); Errors = errors is List <JsonErrorInfo> errorList ? errorList : new List <JsonErrorInfo>(errors); }
public (GreenJsonValueSyntax, JsonSymbolType) ParseList() { var listBuilder = new List <GreenJsonMultiValueSyntax>(); for (; ;) { GreenJsonMultiValueSyntax parsedValueNode = ParseMultiValue(JsonErrorCode.MultipleValues); // Always add each value, because it may contain background symbols. listBuilder.Add(parsedValueNode); // ParseMultiValue() guarantees that the next symbol is never a ValueStartSymbol. JsonSymbolType symbolType = CurrentToken.SymbolType; if (symbolType == JsonSymbolType.Comma) { if (parsedValueNode.ValueNode.ContentNode is GreenJsonMissingValueSyntax) { // Two commas or '[,'. Report(new JsonErrorInfo( JsonErrorCode.MissingValue, CurrentLength - CurrentToken.Length, CurrentToken.Length)); } } else if (symbolType == JsonSymbolType.BracketClose) { return(new GreenJsonListSyntax(listBuilder, missingSquareBracketClose: false), ShiftToNextForegroundToken()); } else { // ':', '}', EOF; assume missing closing bracket ']'. Report(new JsonErrorInfo( symbolType == JsonSymbolType.Eof ? JsonErrorCode.UnexpectedEofInArray : JsonErrorCode.ControlSymbolInArray, CurrentLength - CurrentToken.Length, CurrentToken.Length)); return(new GreenJsonListSyntax(listBuilder, missingSquareBracketClose: true), symbolType); } } }
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); } } }
// For root nodes. internal JsonMultiValueSyntax(GreenJsonMultiValueSyntax green) : this(_void._, green) { // Do not assign ParentIndex, its value is meaningless in this case. }