95 lines
3.0 KiB
C#
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());
|
|
} |