jtr/DevDisciples.Json.Parser/JsonParser.cs
2024-09-15 17:23:27 +02:00

95 lines
3.0 KiB
C#

using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser;
public static partial class JsonParser
{
public static ISyntaxNode 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 ISyntaxNode Expression(ParserContext<JsonToken> ctx)
{
if (ctx.Match(JsonToken.LeftBracket))
return Array(ctx);
if (ctx.Match(JsonToken.LeftBrace))
return Object(ctx);
if (ctx.Match(JsonToken.Minus) || ctx.Match(JsonToken.Number))
return Number(ctx);
if (ctx.Match(JsonToken.String))
return String(ctx);
if (ctx.Match(JsonToken.Null))
return Null(ctx);
if (ctx.Match(JsonToken.True) || ctx.Match(JsonToken.False))
return Bool(ctx);
throw Report.Error(ctx.Current, $"Expected a JSON expression, got '{ctx.Current.Lexeme}'");
}
private static ISyntaxNode Array(ParserContext<JsonToken> ctx)
{
var previous = ctx.Previous();
List<ISyntaxNode>? 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 JsonArray(previous, elements);
}
private static ISyntaxNode Object(ParserContext<JsonToken> ctx)
{
var previous = ctx.Previous();
Dictionary<string, ISyntaxNode>? 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.Lexeme] = Expression(ctx);
} while (ctx.Match(JsonToken.Comma));
}
ctx.Consume(JsonToken.RightBrace, "Expected '}'");
return new JsonObject(previous, properties);
}
private static ISyntaxNode Number(ParserContext<JsonToken> ctx)
{
if (ctx.Previous().Type != JsonToken.Minus) return new JsonNumber(ctx.Previous());
var minus = ctx.Previous();
var number = ctx.Consume(JsonToken.Number, "Expected a number after '-'.");
return new JsonNumber(
new Lexer<JsonToken>.Token(minus.File, JsonToken.Number, $"-{number.Lexeme}", minus.Line, minus.Column)
);
}
private static ISyntaxNode String(ParserContext<JsonToken> ctx) => new JsonString(ctx.Previous());
private static ISyntaxNode Null(ParserContext<JsonToken> ctx) => new JsonNull(ctx.Previous());
private static ISyntaxNode Bool(ParserContext<JsonToken> ctx) => new JsonBool(ctx.Previous());
}