/// <summary> /// Applies the bracket operands. Executes the expressionBuilder with all the operands in the brackets. /// </summary> /// <param name="bracketOpen">The operator that opened the bracket.</param> /// <param name="bracketOperands">The list of operands within the brackets.</param> /// <param name="bracketClose">The operator that closed the bracket.</param> /// <param name="state">The current parse state.</param> /// <exception cref="FunctionArgumentCountException">When the number of opreands does not match the number of arguments</exception> /// <exception cref="FunctionArgumentTypeException">When argument Type does not match the type of the expression</exception> /// <exception cref="OperationInvalidException">When an error occured while executing the expressionBuilder</exception> public override void ApplyBracketOperands(Operator bracketOpen, Stack <Operand> bracketOperands, Operator bracketClose, ParseState state) { var operandSource = StringSegment.Encompass(bracketOperands.Select(x => x.SourceMap)); var functionArguments = bracketOperands.Select(x => x.Expression); //if we have been given specific argument types validate them if (ArgumentTypes != null) { var expectedArgumentCount = ArgumentTypes.Count; if (expectedArgumentCount != bracketOperands.Count) { throw new FunctionArgumentCountException( operandSource, expectedArgumentCount, bracketOperands.Count); } functionArguments = bracketOperands.Zip(ArgumentTypes, (o, t) => { try { return(ExpressionConversions.Convert(o.Expression, t)); } catch (InvalidOperationException) { //if we cant convert to the argument type then something is wrong with the argument //so we will throw it up throw new FunctionArgumentTypeException(o.SourceMap, t, o.Expression.Type); } }); } var functionSourceMap = StringSegment.Encompass(bracketOpen.SourceMap, operandSource); var functionArgumentsArray = functionArguments.ToArray(); Expression output; try { output = ExpressionBuilder(functionArgumentsArray); } catch (Exception ex) { throw new OperationInvalidException(functionSourceMap, ex); } if (output != null) { state.Operands.Push(new Operand(output, functionSourceMap)); } }
/// <summary> /// Applies the bracket operands. Adds the evaluated operand within the bracket to the state. /// </summary> /// <param name="bracketOpen">The operator that opened the bracket.</param> /// <param name="bracketOperands">The list of operands within the brackets.</param> /// <param name="bracketClose">The operator that closed the bracket.</param> /// <param name="state">The current parse state.</param> /// <exception cref="OperandExpectedException">When brackets are empty.</exception> /// <exception cref="OperandUnexpectedException">When there is more than one element in the brackets</exception> public virtual void ApplyBracketOperands(Operator bracketOpen, Stack <Operand> bracketOperands, Operator bracketClose, ParseState state) { if (bracketOperands.Count == 0) { var insideBrackets = StringSegment.Between(bracketOpen.SourceMap, bracketClose.SourceMap); throw new OperandExpectedException(insideBrackets); } else if (bracketOperands.Count > 1) { var operandSpan = StringSegment.Encompass(bracketOperands.Skip(1).Select(x => x.SourceMap)); throw new OperandUnexpectedException(operandSpan); } var bracketOperand = bracketOperands.Pop(); var sourceMap = StringSegment.Encompass(new[] { bracketOpen.SourceMap, bracketOperand.SourceMap, bracketClose.SourceMap }); state.Operands.Push(new Operand(bracketOperand.Expression, sourceMap)); }
/// <summary> /// Applies the token to the parsing state. Adds an operator to the state, when executed the operator will /// check it has enough operands and they are in the correct position. It will then execute the expressionBuilder /// placing the result in the state. /// </summary> /// <param name="token">The token to apply.</param> /// <param name="state">The state to apply the token to.</param> public override void Apply(Token token, ParseState state) { //Apply previous operators if they have a high precedence and they share an operand var anyLeftOperators = this.ParamaterPositions.Any(x => x == RelativePosition.Left); while (state.Operators.Count > 0 && this.OrderOfPrecedence != null && anyLeftOperators) { var prevOperator = state.Operators.Peek().Definition as OperatorDefinition; var prevOperatorPrecedence = prevOperator?.OrderOfPrecedence; if (prevOperatorPrecedence <= this.OrderOfPrecedence && prevOperator.ParamaterPositions.Any(x => x == RelativePosition.Right)) { state.Operators.Pop().Execute(); } else { break; } } state.Operators.Push(new Operator(this, token.SourceMap, () => { //Pop all our right arguments, and check there is the correct number and they are all to the right var rightArgs = new Stack <Operand>(state.Operands.PopWhile(x => x.SourceMap.IsRightOf(token.SourceMap))); var expectedRightArgs = this.ParamaterPositions.Count(x => x == RelativePosition.Right); if (expectedRightArgs > 0 && rightArgs.Count > expectedRightArgs) { var spanWhereOperatorExpected = StringSegment.Encompass(rightArgs .Reverse() .Take(rightArgs.Count - expectedRightArgs) .Select(x => x.SourceMap)); throw new OperandUnexpectedException(token.SourceMap, spanWhereOperatorExpected); } else if (rightArgs.Count < expectedRightArgs) { throw new OperandExpectedException(token.SourceMap, new StringSegment(token.SourceMap.SourceString, token.SourceMap.End, 0)); } //Pop all our left arguments, and check they are not to the left of the next operator var nextOperatorEndIndex = state.Operators.Count == 0 ? 0 : state.Operators.Peek().SourceMap.End; var expectedLeftArgs = this.ParamaterPositions.Count(x => x == RelativePosition.Left); var leftArgs = new Stack <Operand>(state.Operands .PopWhile((x, i) => i < expectedLeftArgs && x.SourceMap.IsRightOf(nextOperatorEndIndex) )); if (leftArgs.Count < expectedLeftArgs) { throw new OperandExpectedException(token.SourceMap, new StringSegment(token.SourceMap.SourceString, token.SourceMap.Start, 0)); } //Map the operators into the correct argument positions var args = new List <Operand>(); foreach (var paramPos in this.ParamaterPositions) { Operand operand = paramPos == RelativePosition.Right ? rightArgs.Pop() : leftArgs.Pop(); args.Add(operand); } //our new source map will encompass this operator and all its operands var sourceMapSpan = StringSegment.Encompass(new[] { token.SourceMap }.Concat(args.Select(x => x.SourceMap))); Expression expression; try { expression = ExpressionBuilder(args.Select(x => x.Expression).ToArray()); }catch (Exception ex) { throw new OperationInvalidException(sourceMapSpan, ex); } state.Operands.Push(new Operand(expression, sourceMapSpan)); })); }