jtr/DevDisciples.Json.Tools/JsonPath.Parser.cs
mdnapo 82a59eebd2 - Refactored code
- Implemented basic JSON Path interpreter
- Clean up
2024-09-22 16:30:31 +02:00

110 lines
3.8 KiB
C#

using DevDisciples.Parsing;
namespace DevDisciples.Json.Tools;
public static partial class JsonPath
{
public static partial class Parser
{
public static List<IJsonPathSyntax> Parse(string path)
{
var pathTokens = Lexer.Default.Lex("<path>", path);
var context = new Context(pathTokens.ToArray());
context.Match(JsonPathToken.DollarSign); // Match the '$' eagerly. This will make it optional by default.
var syntax = new List<IJsonPathSyntax>();
while (!context.Ended())
syntax.Add(Expression(context));
return syntax;
}
private static IJsonPathSyntax Expression(Context context)
{
if (context.Match(JsonPathToken.Dot))
return PropertyAccessorExpression(context);
if (context.Match(JsonPathToken.LeftBracket))
return IndexAccessorExpression(context);
throw context.Error("Invalid expression.");
}
private static IJsonPathSyntax PropertyAccessorExpression(Context context)
{
var token = context.Previous();
IJsonPathSyntax getter;
if (context.Match(JsonPathToken.Asterisk))
getter = new WildCardSyntax(context.Previous());
else if (context.Match(JsonPathToken.Identifier))
getter = new PropertySyntax(context.Previous());
else throw context.Error("Expected a getter expression");
return new PropertyAccessorSyntax(token, getter);
}
private static IJsonPathSyntax IndexAccessorExpression(Context context)
{
var token = context.Previous();
IJsonPathSyntax syntax;
if (context.Match(JsonPathToken.Asterisk))
syntax = new WildCardSyntax(context.Previous());
else if (context.Match(JsonPathToken.Number))
syntax = ArrayIndexExpression(token, context);
else if (context.Match(JsonPathToken.String))
syntax = ObjectIndexExpression(token, context);
else throw context.Error("Expected an index expression.");
context.Consume(JsonPathToken.RightBracket, "Expected ']' after index expression.");
return syntax;
}
private static IJsonPathSyntax ArrayIndexExpression(Lexer<JsonPathToken>.Token token, Context context)
{
var index = context.Previous();
if (!context.Match(JsonPathToken.Comma)) return new ArrayIndexSyntax(token, index);
var indexes = new List<Lexer<JsonPathToken>.Token> { index };
do
{
index = context.Consume(JsonPathToken.Number, "Invalid array index.");
if (!int.TryParse(index.Lexeme, out _)) throw context.Error(index, "Invalid array index.");
indexes.Add(index);
} while (!context.Ended() && context.Match(JsonPathToken.Comma));
return new ArrayIndexListSyntax(token, indexes.ToArray());
}
private static IJsonPathSyntax ObjectIndexExpression(Lexer<JsonPathToken>.Token token, Context context)
{
var index = context.Previous();
if (!context.Match(JsonPathToken.Comma)) return new ObjectIndexSyntax(token, index);
var indexes = new List<Lexer<JsonPathToken>.Token> { index };
do
{
index = context.Consume(JsonPathToken.String, "Invalid object index.");
indexes.Add(index);
} while (!context.Ended() && context.Match(JsonPathToken.Comma));
return new ObjectIndexListSyntax(token, indexes.ToArray());
}
}
}