/// <summary> /// Returns a <see cref="SyntaxError"/> for an unexpected token. /// </summary> /// <param name="token">The unexpected token.</param> /// <returns>A <see cref="SyntaxError"/> for <paramref name="token"/>.</returns> Exception UnexpectedToken(Json5Token token) { if (token.Type == Json5TokenType.Eof) { return(new Json5UnexpectedEndOfInputException(token.Line, token.Column)); } return(new Json5UnexpectedInputException(token.Input, token.Line, token.Column)); }
/// <summary> /// Parses and returns a <see cref="Json5Value"/>. /// </summary> /// <returns>A <see cref="Json5Value"/>.</returns> public Json5Value Parse() { if (this.parsed) { throw new InvalidOperationException("A Json5Parser may only be used once."); } this.state = State.Value; this.root = null; this.stack = new Stack <Json5Container>(); this.currentContainer = null; this.parsed = true; string key = null; start: Json5Token token = this.lexer.Read(); switch (this.state) { case State.Value: switch (token.Type) { case Json5TokenType.String: this.Add(new Json5String(token.String), key); goto start; case Json5TokenType.Number: this.Add(new Json5Number(token.Number), key); goto start; case Json5TokenType.Punctuator: switch (token.Character) { case '[': this.Add(new Json5Array(), key); goto start; case '{': this.Add(new Json5Object(), key); goto start; } throw UnexpectedToken(token); case Json5TokenType.Identifier: // Since these are literal tokens, check token.Input // instead of token.String. Whereas `true` and `\x74rue` // are both valid identifiers that represent the same // thing, only `true` is a boolean literal. switch (token.Input) { case "true": this.Add(new Json5Boolean(true), key); goto start; case "false": this.Add(new Json5Boolean(false), key); goto start; case "null": this.Add(Json5Value.Null, key); goto start; case "Infinity": this.Add(new Json5Number(double.PositiveInfinity), key); goto start; case "NaN": this.Add(new Json5Number(double.NaN), key); goto start; } break; } break; case State.BeforeArrayElement: if (token.Type == Json5TokenType.Punctuator && token.Character == ']') { this.Pop(); goto start; } this.state = State.Value; goto case State.Value; case State.AfterArrayElement: if (token.Type == Json5TokenType.Punctuator) { switch (token.Character) { case ',': this.state = State.BeforeArrayElement; goto start; case ']': this.Pop(); goto start; } } break; case State.BeforeObjectKey: switch (token.Type) { case Json5TokenType.Punctuator: if (token.Character == '}') { this.Pop(); goto start; } break; case Json5TokenType.Identifier: case Json5TokenType.String: // All identifiers are valid as keys // even if they are literals like // `true`, `null`, or `Infinity`. key = token.String; this.state = State.AfterObjectKey; goto start; } break; case State.AfterObjectKey: if (token.Type != Json5TokenType.Punctuator || token.Character != ':') { break; } this.state = State.Value; goto start; case State.AfterObjectValue: if (token.Type == Json5TokenType.Punctuator) { switch (token.Character) { case ',': this.state = State.BeforeObjectKey; goto start; case '}': this.Pop(); goto start; } } break; case State.End: if (token.Type == Json5TokenType.Eof) { return(this.root); } break; } throw UnexpectedToken(token); //throw new Exception("Invalid tree state."); }