using Jtr.Parsing; namespace Jtr.Tools; public static partial class JsonPath { public static partial class Parser { public static List Parse(string path) { var pathTokens = Lexer.Default.Lex("", 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(); 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.Token token, Context context) { var index = context.Previous(); if (!context.Match(JsonPathToken.Comma)) return new ArrayIndexSyntax(token, index); var indexes = new List.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.Token token, Context context) { var index = context.Previous(); if (!context.Match(JsonPathToken.Comma)) return new ObjectIndexSyntax(token, index); var indexes = new List.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()); } } }