public Expression Bind(PipelineExpression pipeline, EmbeddedPipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable <Expression> arguments) { var source = new ZipExpression( node.Type, pipeline.Source, arguments.First()); var lambda = ExpressionHelper.GetLambda(arguments.Last()); var input = new DocumentExpression(bindingContext.GetSerializer(typeof(object[]), null)); var itemA = new ArrayIndexExpression( input, Expression.Constant(0), bindingContext.GetSerializer(lambda.Parameters[0].Type, source.Source)); var itemB = new ArrayIndexExpression( input, Expression.Constant(1), bindingContext.GetSerializer(lambda.Parameters[1].Type, source.Other)); bindingContext.AddExpressionMapping(lambda.Parameters[0], itemA); bindingContext.AddExpressionMapping(lambda.Parameters[1], itemB); var selector = bindingContext.Bind(lambda.Body); var serializer = bindingContext.GetSerializer(selector.Type, selector); return(new PipelineExpression( new SelectExpression( source, lambda.Parameters[0].Name + "_" + lambda.Parameters[1].Name, selector), new DocumentExpression(serializer))); }
protected internal override Expression VisitArrayIndex(ArrayIndexExpression node) { var field = Visit(node.Array) as IFieldExpression; if (field != null) { var constantIndex = node.Index as ConstantExpression; if (constantIndex == null) { throw new NotSupportedException($"Only a constant index is supported in the expression {node}."); } var index = constantIndex.Value.ToString(); if (index == "-1") { // We've treated -1 as meaning $ operator. We can't break this now, // so, specifically when we are flattening fields names, this is // how we'll continue to treat -1. index = "$"; } return(new FieldExpression( field.AppendFieldName(index), node.Serializer)); } return(node); }
public override INode VisitArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { return(new ArrayIndexExpression(arrayIndexExpression.Context, arrayIndexExpression.Children.Select(Visit)) { Type = arrayIndexExpression.Type }); }
protected override Expression VisitBinary(BinaryExpression node) { var newNode = base.VisitBinary(node); var binaryExpression = newNode as BinaryExpression; if (binaryExpression != null && binaryExpression.NodeType == ExpressionType.ArrayIndex) { var serializationExpression = binaryExpression.Left as ISerializationExpression; if (serializationExpression != null) { var arraySerializer = serializationExpression.Serializer as IBsonArraySerializer; BsonSerializationInfo itemSerializationInfo; if (arraySerializer != null && arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo)) { newNode = new ArrayIndexExpression( binaryExpression.Left, binaryExpression.Right, itemSerializationInfo.Serializer, binaryExpression); } } } return(newNode); }
// private methods private Expression BindElementAt(MethodCallExpression node) { if (!ExpressionHelper.IsLinqMethod(node)) { return(base.VisitMethodCall(node)); } var newNode = base.VisitMethodCall(node); var methodCallExpression = newNode as MethodCallExpression; if (node != newNode && methodCallExpression != null) { var serializationExpression = methodCallExpression.Arguments[0] as ISerializationExpression; if (serializationExpression != null) { var arraySerializer = serializationExpression.Serializer as IBsonArraySerializer; BsonSerializationInfo itemSerializationInfo; if (arraySerializer != null && arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo)) { newNode = new ArrayIndexExpression( methodCallExpression.Arguments[0], methodCallExpression.Arguments[1], itemSerializationInfo.Serializer, methodCallExpression); } } } return(newNode); }
private BsonValue TranslateArrayIndex(ArrayIndexExpression node) { return(new BsonDocument("$arrayElemAt", new BsonArray { TranslateValue(node.Array), TranslateValue(node.Index) })); }
//--------------------------------------------------------------------- public override bool Visit(ArrayIndexExpression arrayIndexExpression) { _writer.Write("args["); arrayIndexExpression.Right.Accept(this); _writer.Write("]"); return(true); }
//--------------------------------------------------------------------- public override bool Visit(ArrayIndexExpression arrayIndexExpression) { arrayIndexExpression.Right.Accept(this); _writer.WriteLine("arr_index"); _emittedLine++; this.EmitDebugInfo(arrayIndexExpression); return(true); }
//--------------------------------------------------------------------- public override Expression Visit(ExponentationExpression exponentationExpression) { ArrayIndexExpression left = exponentationExpression.Left as ArrayIndexExpression; ConstantExpression right = exponentationExpression.Right as ConstantExpression; if (left == null || right == null || right.Value != 2) { return(base.Visit(exponentationExpression)); } this.DidAnyOptimization = true; return(new MultiplyExpression(exponentationExpression.Token as Operation, left, left)); }
public override void ExitArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { if (arrayIndexExpression.Parent is AssignmentExpression assignment && assignment.Left == arrayIndexExpression) { return; } arrayIndexExpression.ReplaceWith( NativeMethods.ArrayIndex( arrayIndexExpression.Context, arrayIndexExpression.Array, arrayIndexExpression.Index) ); }
public override void ExitArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { if (arrayIndexExpression.Array.Type.IsList(arrayIndexExpression.NearestAncestorOfType <Root>())) { arrayIndexExpression.Type = ((GenericType)arrayIndexExpression.Array.Type).GenericTypes.Single(); } else { Errors.Add(new CompilationError(arrayIndexExpression.Context, $"Unable to index {arrayIndexExpression.Array.Type}. Type must be an array.")); arrayIndexExpression.Type = new NullType(); } if (!(arrayIndexExpression.Index.Type is NumberType)) { Errors.Add(new CompilationError(arrayIndexExpression.Context, $"The index must be of type number. Got {arrayIndexExpression.Index.Type}")); } }
//--------------------------------------------------------------------- public void Visit(ParameterToken parameter) { if (_lastToken is ValueToken) { this.Visit(Operation.Multiplication(Position.NotDefined)); } if (!_parameters.Contains(parameter.Parameter)) { _parameters.Add(parameter.Parameter); } var idxExpr = new IndexExpression(_parameters.IndexOf(parameter.Parameter)); var expr = new ArrayIndexExpression(parameter, _arrayParameter, idxExpr); _expressionStack.Push(expr); _lastToken = parameter; }
public override IEnumerable <INode> VisitPrimaryExpression(TypescriptParser.PrimaryExpressionContext context) { var @base = (IExpression)Visit(context.primaryExpressionStart()).SingleOrDefault(); var operations = context.bracketExpression() .Concat <IParseTree>(context.memberAccess()) .Concat(context.methodInvocation()) .Concat(context.OP_INC()) .Concat(context.OP_DEC()) .OrderBy(x => x.GetTokens().First().Line) .ThenBy(x => x.GetTokens().First().Column); foreach (var operation in operations) { if (operation is TypescriptParser.BracketExpressionContext bracketExpression) { @base = new ArrayIndexExpression(bracketExpression, @base.Yield().Concat(Visit(bracketExpression))); } else if (operation is TypescriptParser.MemberAccessContext memberAccess) { @base = new MemberExpression(memberAccess, memberAccess.identifier().GetText(), @base.Yield()); } else if (operation is TypescriptParser.MethodInvocationContext methodInvocation) { var genericTypeArguments = Visit((@base.Context as TypescriptParser.MemberAccessContext)?.typeArgumentList()).Cast <ITypeNode>(); var arguments = Visit(methodInvocation.argumentList()).Cast <IExpression>(); @base = new MethodInvocationExpression( methodInvocation, @base.Yield().Concat <INode>(genericTypeArguments).Concat(arguments)); } else if (operation is ITerminalNode terminalNode) { @base = new PosfixOperationExpression(terminalNode, new INode[] { @base, new Token(terminalNode) }); } else { throw new Exception("Unexpected operation: " + operation); } } yield return(@base); }
protected internal override Expression VisitArrayIndex(ArrayIndexExpression node) { var field = Visit(node.Array) as IFieldExpression; if (field != null) { var constantIndex = node.Index as ConstantExpression; if (constantIndex == null) { throw new NotSupportedException($"Only a constant index is supported in the expression {node}."); } var index = FormatArrayIndex(constantIndex.Value); return(new FieldExpression( field.AppendFieldName(index), node.Serializer)); } return(node); }
public virtual void EnterArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { }
// ArrayIndexExpression private void DefaultWalk(ArrayIndexExpression node) { if (Walk(node)) { WalkNode(node.Array); WalkNode(node.Index); } PostWalk(node); }
public override LE.Expression Visit(ArrayIndexExpression arrayIndexExpression) => LE.Expression.ArrayIndex(_arrayParameter, arrayIndexExpression.Right.Accept(this));
public virtual T VisitArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { return(VisitChildren(arrayIndexExpression)); }
public string TryParse <T>(string source, ref int index, out JsonPathExpression expression) { expression = null; PathExpression <T> node; var isLocal = source[index] == '@'; var error = JsonPathParser.Parse(source, ref index, out JsonPath path); // Swallow this error from the path parser and assume the path just ended. // If it's really a syntax error, the expression parser should catch it. if (error != null && error != "Unrecognized JSON Path element.") { return(error); } var lastOp = path.Operators.Last(); if (lastOp is NameOperator name) { path.Operators.Remove(name); if (name.Name == "indexOf") { if (source[index] != '(') { return("Expected '('. 'indexOf' operator requires a parameter."); } index++; error = JsonParser.Parse(source, ref index, out JsonValue parameter, true); // Swallow this error from the JSON parser and assume the value just ended. // If it's really a syntax error, the expression parser should catch it. if (error != null && error != "Expected \',\', \']\', or \'}\'.") { return($"Error parsing parameter for 'indexOf' expression: {error}."); } if (source[index] != ')') { return("Expected ')'."); } index++; node = new IndexOfExpression <T> { Path = path, IsLocal = isLocal, Parameter = parameter }; } else { node = new NameExpression <T> { Path = path, IsLocal = isLocal, Name = name.Name } }; } else if (lastOp is LengthOperator length) { path.Operators.Remove(length); node = new LengthExpression <T> { Path = path, IsLocal = isLocal }; } else if (lastOp is ArrayOperator array) { path.Operators.Remove(array); var query = array.Query as SliceQuery; var constant = query?.Slices.FirstOrDefault()?.Index; if (query == null || query.Slices.Count() != 1 || !constant.HasValue) { return("JSON Path expression indexers only support single constant values."); } node = new ArrayIndexExpression <T> { Path = path, IsLocal = isLocal, Index = constant.Value }; } else { throw new NotImplementedException(); } expression = new PathValueExpression <T> { Path = node }; return(null); }
// Trying to parse an exception // When parsing an expression, any left markers like (, {, or [ must not be the first token // When returned, any right markers like ), }, ] and ; will still be in the token stream private static ExpressionSyntaxNode?ParseExpression(ref ReadOnlySpan <IToken> tokens, ISyntaxNode parent, ExpressionSyntaxNode?wouldBeLeft) { if (tokens.IsEmpty) { throw new InvalidOperationException("We need a token to do any attempt at parsing an expression"); } var curToken = tokens[0]; var prevTokens = tokens; tokens = tokens.Slice(1); // We first need to special case any recursive expressions // First check if we are a supported operation if (curToken is ISupportedOperationToken op) { // A supported op requires something on the left if (wouldBeLeft == null) { throw new InvalidTokenException("Left can't be null here"); } // Grab the right side expressions var couldBeRight = ParseExpression(ref tokens, parent, null); // Right side expression can not be null either if (couldBeRight == null) { throw new InvalidTokenException("Right can't be null either"); } // We are an ExpOpExp, create with the left side, the right side, and the operation return(new ExpressionOpExpressionSyntaxNode(parent, wouldBeLeft, new OperationSyntaxNode(parent, op.Operation), couldBeRight)); } else if (curToken is EqualsToken) { // Check if we are an assignment operation // Assignment must have a left as well if (wouldBeLeft == null) { throw new InvalidTokenException("Left can't be null here"); } // Parse the right, it must exist, can't assign nothing var couldBeRight = ParseExpression(ref tokens, parent, null); if (couldBeRight == null) { throw new InvalidTokenException("Right can't be null either"); } return(new ExpressionEqualsExpressionSyntaxNode(parent, wouldBeLeft, couldBeRight)); } else if (curToken is LeftBracketToken) { // We are creating an array indexer // We must have an expression to attach the array indexer to if (tokens.Length < 2) { throw new InvalidTokenException("Not enough tokens left"); } if (wouldBeLeft == null) { throw new InvalidTokenException("Left must not be null"); } // There will be some expression to get the length var lengthExpression = ParseExpression(ref tokens, parent, null); if (lengthExpression == null) { throw new InvalidTokenException("Must have an expression"); } // There has to be a right bracket after parsing the inner expression // You also will need to have either a ; or some other expression on the right if (tokens.Length < 2) { throw new InvalidTokenException("There must be a token"); } if (!(tokens[0] is RightBracketToken)) { throw new InvalidTokenException("Must be a right bracket"); } // Remove the ] tokens = tokens.Slice(1); // Create our array expression var arrIdxExp = new ArrayIndexExpression(parent, wouldBeLeft, lengthExpression); // TODO see if the same logic for detecting the end of a method train // works here. It should in theory. About 20 lines down // if there is a ;, this is the end of the expression // Don't remove the semi colon, but just return the array // indexing expression if (tokens[0] is SemiColonToken) { return(arrIdxExp); } // Otherwise, parse the expression to the right of the indexer and return that return(ParseExpression(ref tokens, parent, arrIdxExp)); } else if (curToken is DotToken) { // Method call or lots of fields // Next token must be an identifier if (tokens.Length < 3) { throw new InvalidTokenException("Not enough tokens left"); } if (!(tokens[0] is IdentifierToken id)) { throw new InvalidTokenException("An ID token must be next"); } // Must be a left hand side if we have a . if (wouldBeLeft == null) { throw new InvalidTokenException("Left can't be null"); } // There are a few things that can happen after a ., lets try them if (tokens[1] is LeftParenthesisToken) { // Left paranthesis means a method call // Parse the call parameters tokens = tokens.Slice(2); var parameters = ParseCallParameters(ref tokens, parent); // Create the method call expression var methodExpression = new MethodCallExpression(parent, wouldBeLeft, id.Name, parameters); // Try to parse an expression afterward // If we could, return it // Otherwise return the method expression as the end train var continuingExpression = ParseExpression(ref tokens, parent, methodExpression); if (continuingExpression != null) { return(continuingExpression); } return(methodExpression); } else if (tokens[1] is DotToken) { // Nested field access // Create a variable access, then parse everything to the right of that. tokens = tokens.Slice(1); return(ParseExpression(ref tokens, parent, new VariableAccessExpression(parent, wouldBeLeft, id.Name))); } else if (tokens[1] is RightParenthesisToken) { // We might be inside a call parameter detector. If so, // Just return a variable access since thats what we've done tokens = tokens.Slice(1); return(new VariableAccessExpression(parent, wouldBeLeft, id.Name)); } else if (tokens[1] is EqualsToken) { // Next is an equals, we currently have the left side as a variable // Parse the right side tokens = tokens.Slice(2); var couldBeRight = ParseExpression(ref tokens, parent, null); if (couldBeRight == null) { throw new InvalidTokenException("Right cannot be null here"); } return(new ExpressionEqualsExpressionSyntaxNode(parent, new VariableAccessExpression(parent, wouldBeLeft, id.Name), couldBeRight)); ; } else if (tokens[1] is SemiColonToken) { //End of an expression // The slice slices off the next identifier, not the semi colon tokens = tokens.Slice(1); return(new MethodReferenceExpression(parent, wouldBeLeft, id.Name)); } else if (tokens[1] is LeftBracketToken) { // We are creating a nested array indexer // Parse the index expression, then create the array expression // Then return a Parsed expression tokens = tokens.Slice(2); var arrIdxExp = ParseExpression(ref tokens, parent, null); if (arrIdxExp == null) { throw new InvalidOperationException("Must have an expression in the array indexer"); } if (tokens.IsEmpty) { throw new InvalidOperationException("Must have more tokens"); } if (!(tokens[0] is RightBracketToken)) { throw new InvalidOperationException("Must have a right bracket"); } tokens = tokens.Slice(1); var arrAccessExp = new VariableAccessExpression(parent, wouldBeLeft, id.Name); var arrExp = new ArrayIndexExpression(parent, arrAccessExp, arrIdxExp); return(ParseExpression(ref tokens, parent, arrExp)); } else { throw new InvalidTokenException("A token must be handled here"); } } if (wouldBeLeft != null) { tokens = prevTokens; return(null); } ExpressionSyntaxNode?variableNode; switch (curToken) { case IntegerConstantToken numericConstant: variableNode = new IntConstantSyntaxNode(parent, numericConstant.Value); break; case StringConstantToken stringConstant: variableNode = new StringConstantNode(parent, stringConstant.Value); break; case IdentifierToken { Name: "this" } _: variableNode = new ThisConstantNode(parent); break; case IdentifierToken { Name: "true" } _: variableNode = new TrueConstantNode(parent); break; case IdentifierToken { Name: "false" } _: variableNode = new FalseConstantNode(parent); break; case IdentifierToken { Name: "null" } _: variableNode = new NullConstantNode(parent); break; case NewToken _: { if (tokens.Length < 3) { throw new InvalidTokenException("Need tokens to parse"); } if (!(tokens[0] is IdentifierToken idToken)) { throw new InvalidTokenException("Next token must be an identifier"); } if (!(tokens[1] is LeftParenthesisToken)) { throw new InvalidTokenException("Expected a left paranthesis"); } tokens = tokens.Slice(2); var parameters = ParseCallParameters(ref tokens, parent); variableNode = new NewConstructorExpression(parent, idToken.Name, parameters); break; } case NewArrToken _: { if (tokens.Length < 3) { throw new InvalidTokenException("Need tokens to parse"); } if (!(tokens[0] is IdentifierToken idToken)) { throw new InvalidTokenException("Next token must be an identifier"); } if (!(tokens[1] is LeftParenthesisToken)) { throw new InvalidTokenException("Expected a left paranthesis"); } tokens = tokens.Slice(2); var expression = ParseExpression(ref tokens, parent, null); if (expression == null) { throw new InvalidTokenException("Must have an expression for a newarr"); } if (tokens.Length < 1) { throw new InvalidTokenException("Need tokens to parse"); } if (!(tokens[0] is RightParenthesisToken)) { throw new InvalidTokenException("Next token must be a right paranthesis"); } tokens = tokens.Slice(1); variableNode = new NewArrExpression(parent, idToken.Name, expression); break; } case IdentifierToken id: variableNode = new VariableSyntaxNode(parent, id.Name); break; default: tokens = prevTokens; return(null); } // If its empty, we're done if (tokens.IsEmpty) { return(variableNode); } var attemptToParseLower = ParseExpression(ref tokens, parent, variableNode); return(attemptToParseLower ?? variableNode); }
public bool TryParse <T>(string source, ref int index, [NotNullWhen(true)] out JsonPathExpression?expression, [NotNullWhen(false)] out string?errorMessage) { PathExpression <T> node; var isLocal = source[index] == '@'; if (!JsonPathParser.TryParse(source, ref index, out var path, out errorMessage) && // Swallow this error from the path parser and assume the path just ended. // If it's really a syntax error, the expression parser should catch it. errorMessage != "Unrecognized JSON Path element.") { expression = null !; return(false); } var lastOp = path !.Operators.Last(); if (lastOp is NameOperator name) { path.Operators.Remove(name); if (name.Name == "indexOf") { if (source[index] != '(') { errorMessage = "Expected '('. 'indexOf' operator requires a parameter."; expression = null !; return(false); } index++; if (!JsonParser.TryParse(source, ref index, out var parameter, out errorMessage, true) && // Swallow this error from the JSON parser and assume the value just ended. // If it's really a syntax error, the expression parser should catch it. errorMessage != "Expected \',\', \']\', or \'}\'.") { errorMessage = $"Error parsing parameter for 'indexOf' expression: {errorMessage}."; expression = null !; return(false); } if (source[index] != ')') { errorMessage = "Expected ')'."; expression = null !; return(false); } index++; node = new IndexOfExpression <T>(path, isLocal, parameter !); } else { node = new NameExpression <T>(path, isLocal, name.Name); } } else if (lastOp is LengthOperator length) { path.Operators.Remove(length); node = new LengthExpression <T>(path, isLocal); } else if (lastOp is ArrayOperator array) { path.Operators.Remove(array); var query = array.Query as SliceQuery; var constant = query?.Slices.FirstOrDefault <Slice?>()?.Index; if (query == null || query.Slices.Count() != 1 || !constant.HasValue) { errorMessage = "JSON Path expression indexers only support single constant values."; expression = null !; return(false); } node = new ArrayIndexExpression <T>(path, isLocal, constant.Value); } else { throw new NotImplementedException(); } expression = new PathValueExpression <T>(node); return(true); }
// ArrayIndexExpression protected internal virtual bool Walk(ArrayIndexExpression node) { return true; }
protected internal virtual void PostWalk(ArrayIndexExpression node) { }
public virtual void ExitArrayIndexExpression(ArrayIndexExpression arrayIndexExpression) { }
// ArrayIndexExpression protected internal virtual bool Walk(ArrayIndexExpression node) { return(true); }