All checks were successful
Run the JSON parser tests / test (push) Has been skipped
110 lines
3.8 KiB
C#
110 lines
3.8 KiB
C#
using Jtr.Parsing;
|
|
|
|
namespace Jtr.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.SyntaxException($"Invalid expression '{context.Current.Lexeme}'.");
|
|
}
|
|
|
|
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.SyntaxException("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.SyntaxException("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.SyntaxException(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());
|
|
}
|
|
}
|
|
} |