98 lines
3.5 KiB
C#
98 lines
3.5 KiB
C#
using DevDisciples.Json.Parser.Syntax;
|
|
using DevDisciples.Parsing;
|
|
|
|
namespace DevDisciples.Json.Parser;
|
|
|
|
public static partial class JsonParser
|
|
{
|
|
public static IJsonSyntax Parse(string file, string source)
|
|
{
|
|
var tokens = JsonLexer.Default.Lex(file, source).ToArray();
|
|
var context = new Context(tokens);
|
|
var nodes = Expression(context);
|
|
return nodes;
|
|
}
|
|
|
|
private static IJsonSyntax Expression(ParserContext<JsonToken> ctx)
|
|
{
|
|
if (ctx.Match(JsonToken.LeftBracket))
|
|
return ArrayExpression(ctx);
|
|
|
|
if (ctx.Match(JsonToken.LeftBrace))
|
|
return ObjectExpression(ctx);
|
|
|
|
if (ctx.Match(JsonToken.Minus) || ctx.Match(JsonToken.Number))
|
|
return NumberExpression(ctx);
|
|
|
|
if (ctx.Match(JsonToken.String))
|
|
return StringExpression(ctx);
|
|
|
|
if (ctx.Match(JsonToken.Null))
|
|
return NullExpression(ctx);
|
|
|
|
if (ctx.Match(JsonToken.True) || ctx.Match(JsonToken.False))
|
|
return BoolExpression(ctx);
|
|
|
|
throw Report.SyntaxException(ctx.Current, $"Expected a JSON expression, got '{ctx.Current.Lexeme}'");
|
|
}
|
|
|
|
private static IJsonSyntax ArrayExpression(ParserContext<JsonToken> ctx)
|
|
{
|
|
var previous = ctx.Previous();
|
|
List<IJsonSyntax>? elements = null;
|
|
|
|
if (!ctx.Check(JsonToken.RightBracket))
|
|
{
|
|
do
|
|
{
|
|
elements ??= new();
|
|
elements.Add(Expression(ctx));
|
|
} while (ctx.Match(JsonToken.Comma));
|
|
}
|
|
|
|
ctx.Consume(JsonToken.RightBracket, "Expected ']'");
|
|
|
|
return new JsonArraySyntax(previous, elements?.ToArray() ?? System.Array.Empty<IJsonSyntax>());
|
|
}
|
|
|
|
private static IJsonSyntax ObjectExpression(ParserContext<JsonToken> ctx)
|
|
{
|
|
var previous = ctx.Previous();
|
|
Dictionary<Lexer<JsonToken>.Token, IJsonSyntax>? properties = null;
|
|
|
|
if (!ctx.Check(JsonToken.RightBrace))
|
|
{
|
|
do
|
|
{
|
|
var key = ctx.Consume(JsonToken.String, "Expected property name");
|
|
ctx.Consume(JsonToken.Colon, "Expected ':' after property name");
|
|
properties ??= new();
|
|
properties[key] = Expression(ctx);
|
|
} while (ctx.Match(JsonToken.Comma));
|
|
}
|
|
|
|
ctx.Consume(JsonToken.RightBrace, "Expected '}'");
|
|
|
|
var propertiesArray = properties?.Select(kv => new JsonPropertySyntax(kv.Key, kv.Value)).ToArray();
|
|
|
|
return new JsonObjectSyntax(previous, propertiesArray ?? System.Array.Empty<JsonPropertySyntax>());
|
|
}
|
|
|
|
private static IJsonSyntax NumberExpression(ParserContext<JsonToken> ctx)
|
|
{
|
|
if (ctx.Previous().Type != JsonToken.Minus) return new JsonNumberSyntax(ctx.Previous());
|
|
|
|
var minus = ctx.Previous();
|
|
var number = ctx.Consume(JsonToken.Number, "Expected a number after '-'.");
|
|
|
|
return new JsonNumberSyntax(
|
|
new Lexer<JsonToken>.Token(minus.File, JsonToken.Number, $"-{number.Lexeme}", minus.Line, minus.Column)
|
|
);
|
|
}
|
|
|
|
private static IJsonSyntax StringExpression(ParserContext<JsonToken> ctx) => new JsonStringSyntax(ctx.Previous());
|
|
|
|
private static IJsonSyntax NullExpression(ParserContext<JsonToken> ctx) => new JsonNullSyntax(ctx.Previous());
|
|
|
|
private static IJsonSyntax BoolExpression(ParserContext<JsonToken> ctx) => new JsonBoolSyntax(ctx.Previous());
|
|
} |