/// <summary> /// Obtiene el valor de una variable /// </summary> private async Task <VariableModel> GetVariableValueAsync(string name, ExpressionsCollection expressions, CancellationToken cancellationToken) { VariableModel variable = new VariableModel(name, VariableModel.VariableType.Unknown); // Asigna el valor if (expressions.Count > 0) { variable.Value = await ExecuteExpressionAsync(expressions, cancellationToken); } // Devuelve la variable return(variable); }
/// <summary> /// Lee las expresiones /// </summary> private ExpressionsCollection ReadExpression(ExpressionReaderMode mode, out string error) { ExpressionsCollection expressions = new ExpressionsCollection(); Token nextToken = GetToken(true); // Inicializa los valores de salida error = ""; // Lee las expresiones while (!IsEof(nextToken) && nextToken.IsExpressionPart && string.IsNullOrWhiteSpace(error)) { // Añade el token a la colección de expresiones if (nextToken.Type == Token.TokenType.Variable) { expressions.Add(ReadVariableIdentifier(GetToken(), out error)); } else { expressions.Add(new ExpressionBase(GetToken())); } // Obtiene el siguiente token nextToken = GetToken(true); } // Comprueba si ha habido algún error switch (mode) { case ExpressionReaderMode.Normal: if (nextToken.Type != Token.TokenType.EndSentenceBlock && nextToken.Type != Token.TokenType.RightLlave && nextToken.Type != Token.TokenType.Sentence) { error = "Falta el final de sentencia en la expresión"; } break; case ExpressionReaderMode.AtVariable: if (nextToken.Type != Token.TokenType.RightCorchete) { error = "Falta el corchete final en la definición de variable"; } break; case ExpressionReaderMode.AtAttributes: if (nextToken.Type != Token.TokenType.RightLlave && nextToken.Type != Token.TokenType.Literal) { error = "Falta el final de sentencia en la definición de atributos"; } break; } // Devuelve la colección de expresiones return(expressions); }
/// <summary> /// Ejecuta una expresión /// </summary> protected async Task <VariableModel> ExecuteExpressionAsync(ExpressionsCollection expressions, CancellationToken cancellationToken) { (string error, VariableModel result) = await ExpressionEvaluator.EvaluateAsync(Context.Actual, expressions, cancellationToken); // Añade el error si es necesario if (!string.IsNullOrWhiteSpace(error)) { // Añade el error AddError(error); // El resultado no es válido result = null; } // Devuelve el resultado return(result); }
/// <summary> /// Transforma las expresiones /// </summary> private string TransformExpressions(ExpressionsCollection expressions) { string result = string.Empty; // Añade las expresiones foreach (ExpressionBase expressionBase in expressions) { switch (expressionBase) { case ExpressionConstant expression: result += TransformExpressionConstant(expression); break; case ExpressionFunction expression: result += TransformExpressionFunction(expression); break; case ExpressionParenthesis expression: result += TransformExpressionParenthesis(expression); break; case ExpressionOperatorLogical expression: result += TransformExpressionOperatorLogical(expression); break; case ExpressionOperatorMath expression: result += TransformExpressionOperatorMath(expression); break; case ExpressionOperatorRelational expression: result += TransformExpressionOperatorRelational(expression); break; case ExpressionVariableIdentifier expression: result += TransformExpressionVariable(expression); break; } } // Devuelve el resultado return(result); }
/// <summary> /// Calcula una expresión /// </summary> private VariableModel Compute(ContextModel context, ExpressionsCollection stackExpressions, out string error) { Stack <VariableModel> stackOperators = new Stack <VariableModel>(); // Inicializa los argumentos de salida error = string.Empty; // Calcula el resultado foreach (ExpressionBase expressionBase in stackExpressions) { if (string.IsNullOrWhiteSpace(error)) { switch (expressionBase) { case ExpressionConstant expression: stackOperators.Push(new VariableModel("Constant", expression.Value)); break; case ExpressionVariableIdentifier expression: VariableModel variable = Search(context, expression, out error); // Comprueba que se haya encontrado la variable if (variable == null) { error = "Cant find the variable value"; } // Si no hay ningún error, se añade la variable a la pila if (string.IsNullOrWhiteSpace(error)) { stackOperators.Push(variable); } break; case ExpressionOperatorBase expression: if (stackOperators.Count < 2) { error = "Ther is not enough operators in stack for execute this operation"; } else { VariableModel second = stackOperators.Pop(); //? cuidado al sacar de la pila, están al revés VariableModel first = stackOperators.Pop(); VariableModel result = ComputeBinary(expression, first, second, out error); // Si no ha habido ningún error, se añade a la pila if (string.IsNullOrEmpty(error)) { stackOperators.Push(result); } } break; } } } // Obtiene el resultado if (string.IsNullOrWhiteSpace(error) && stackOperators.Count == 1) { return(stackOperators.Pop()); } else if (stackOperators.Count == 0) { error = "There is no operators in the operations stack"; return(null); } else { error = "There are too much operator in the operations stack"; return(null); } }
/// <summary> /// Evalúa una serie de expresiones pasadas a notación polaca /// </summary> public VariableModel EvaluateRpn(ContextModel context, ExpressionsCollection expressionsRpn, out string error) { return(Compute(context, expressionsRpn.Clone(), out error)); }
/// <summary> /// Evalúa una serie de expresiones /// </summary> public VariableModel Evaluate(ContextModel context, ExpressionsCollection expressions, out string error) { return(Compute(context, new ExpressionConversorRpn().ConvertToRPN(expressions), out error)); }
/// <summary> /// Interpreta una expresión hasta encontrar un token de cierre /// </summary> private ExpressionsCollection ParseExpression(Token.TokenType[] tokensTypeEnd, out string error) { ExpressionsCollection expressions = new ExpressionsCollection(); int parenthesisOpen = 0; bool isEnd = false; // Inicializa los argumentos de salida error = string.Empty; // Interpreta las expresiones while (!isEnd && string.IsNullOrEmpty(error)) { // Añade el token actual a la colección de expresiones switch (ActualToken.Type) { case Token.TokenType.ArithmeticOperator: expressions.Add(GetExpressionMath(ActualToken.Value)); break; case Token.TokenType.LeftParentesis: case Token.TokenType.RightParentesis: // Añade la expresión del paréntesis expressions.Add(new ExpressionParenthesis(ActualToken.Type == Token.TokenType.LeftParentesis)); // Añade o quita el paréntesis del contador if (ActualToken.Type == Token.TokenType.LeftParentesis) { parenthesisOpen++; } else if (ActualToken.Type == Token.TokenType.RightParentesis) { parenthesisOpen--; } break; case Token.TokenType.LogicalOperator: expressions.Add(GetExpressionLogical(ActualToken.Value)); break; case Token.TokenType.RelationalOperator: expressions.Add(GetExpressionRelation(ActualToken.Value)); break; case Token.TokenType.Number: expressions.Add(new ExpressionConstant(Context.Variables.VariableModel.VariableType.Numeric, ActualToken.Value.GetDouble(0))); break; case Token.TokenType.String: expressions.Add(new ExpressionConstant(Context.Variables.VariableModel.VariableType.String, ActualToken.Value)); break; case Token.TokenType.UserDefined: switch ((TokenSubType)(ActualToken.SubType ?? 2000)) { case TokenSubType.Date: expressions.Add(new ExpressionConstant(Context.Variables.VariableModel.VariableType.Date, ActualToken.Value.GetDateTime())); break; case TokenSubType.Variable: expressions.Add(new ExpressionVariableIdentifier(ActualToken.Value)); break; default: expressions.Add(new ExpressionError("Unknown token")); break; } break; case Token.TokenType.Variable: expressions.Add(new ExpressionVariableIdentifier(ActualToken.Value)); break; default: expressions.Add(new ExpressionError("Unknown expression")); break; } // Si la última expresión es un error, se detiene if (expressions.Count > 0 && expressions[0] is ExpressionError expression) { error = expression.Message; } else { // Pasa al siguiente token NextToken(); // Comprueba si es el final de la expresión isEnd = IsEndExpression(tokensTypeEnd, parenthesisOpen); } } // Devuelve la colección de expresiones return(expressions); }
public DynamicExpression() { Values = new ExpressionsCollection(Reserved); }
/// <summary> /// Convierte una colección de expresiones en una pila de expresiones en notación polaca inversa (sin paréntesis) /// </summary> internal ExpressionsCollection ConvertToRPN(ExpressionsCollection expressions) { ExpressionsCollection stackOutput = new ExpressionsCollection(); Stack <ExpressionBase> stackoperations = new Stack <ExpressionBase>(); // Convierte las expresiones en una pila foreach (ExpressionBase expression in expressions) { if (expression is ExpressionBase) { switch (expression.Token.Type) { case Tokens.Token.TokenType.LeftParentesis: // Paréntesis izquierdo, se mete directamente en la pila de operadores stackoperations.Push(expression); break; case Tokens.Token.TokenType.RightParentesis: bool end = false; // Paréntesis derecho. Saca todos los elementos del stack hasta encontrar un paréntesis izquierdo while (stackoperations.Count > 0 && !end) { ExpressionBase expressionoperation = stackoperations.Pop(); if (expressionoperation.Token.Type == Tokens.Token.TokenType.LeftParentesis) { end = true; } else { stackOutput.Add(expressionoperation); } } break; case Tokens.Token.TokenType.Arithmeticoperation: case Tokens.Token.TokenType.Logicaloperation: case Tokens.Token.TokenType.Relationaloperation: bool endoperation = false; // Recorre los operadores de la pila while (stackoperations.Count > 0 && !endoperation) { ExpressionBase objLastoperation = null; // Obtiene el último operador de la pila (sin sacarlo) objLastoperation = stackoperations.Peek(); // Si no hay ningún operador en la pila o la prioridad del operador actual es mayor que la del último de la pila se mete el último operador if (objLastoperation == null || expression.Token.Type == Tokens.Token.TokenType.LeftParentesis || expression.Priority > objLastoperation.Priority) { endoperation = true; } else // ... si el operador tiene una prioridad menor que el último de la fila, se quita el último operador de la pila y se compara de nuevo { stackOutput.Add(stackoperations.Pop()); } } // Añade el operador a la pila de operadores stackoperations.Push(expression); break; case Tokens.Token.TokenType.Number: case Tokens.Token.TokenType.String: case Tokens.Token.TokenType.Variable: // Si es un número o una cadena o una variable se copia directamente en la pila de salida stackOutput.Add(expression); break; default: stackOutput.Add(new ExpressionBase(new Tokens.Token { Type = Tokens.Token.TokenType.Error, Content = "Expresión desconocida" })); break; } } } // Añade todos los elementos que queden en el stack de operadores al stack de salida while (stackoperations.Count > 0) { stackOutput.Add(stackoperations.Pop()); } // Devuelve la pila convertida a notación polaca inversa return(stackOutput); }
///// <summary> ///// Evalúa una serie de expresiones pasadas a notación polaca (RPN) ///// </summary> //internal VariableModel EvaluateRpn(ContextModel context, ExpressionsCollection expressionsRpn, out string error) //{ // return Compute(context, expressionsRpn.Clone(), out error); //} /// <summary> /// Calcula una expresión /// </summary> private async Task <(string error, VariableModel variable)> ComputeAsync(ContextModel context, ExpressionsCollection stackExpressions, CancellationToken cancellationToken) { string error = string.Empty; Stack <VariableModel> stackOperators = new Stack <VariableModel>(); // Calcula el resultado foreach (ExpressionBase expressionBase in stackExpressions) { if (string.IsNullOrWhiteSpace(error)) { switch (expressionBase) { case ExpressionConstant expression: stackOperators.Push(new VariableModel("Constant", expression.Value)); break; case ExpressionVariableIdentifier expression: (string errorSearch, VariableModel variable) = await SearchAsync(context, expression, cancellationToken); // Comprueba que se haya encontrado la variable if (!string.IsNullOrWhiteSpace(errorSearch)) { error = errorSearch; } else if (variable == null) { error = "Cant find the variable value"; } // Si no hay ningún error, se añade la variable a la pila if (string.IsNullOrWhiteSpace(error)) { stackOperators.Push(variable); } break; case ExpressionFunction expression: VariableModel resultFunction = await Processor.ExecuteFunctionAsync(expression, cancellationToken); // Si se ha podido ejecutar la función, la añade a la pila if (resultFunction == null) { error = $"Cant execute function {expression.Function}"; } else { stackOperators.Push(resultFunction); } break; case ExpressionOperatorBase expression: if (stackOperators.Count < 2) { error = "There is not enough operators in stack for execute this operation"; } else { VariableModel second = stackOperators.Pop(); //? cuidado al sacar de la pila, están al revés VariableModel first = stackOperators.Pop(); VariableModel result = ComputeBinary(expression, first, second, out error); // Si no ha habido ningún error, se añade a la pila if (string.IsNullOrEmpty(error)) { stackOperators.Push(result); } } break; } } } // Obtiene el resultado if (!string.IsNullOrWhiteSpace(error)) { return(error, null); } else if (string.IsNullOrWhiteSpace(error) && stackOperators.Count == 1) { return(error, stackOperators.Pop()); } else if (stackOperators.Count == 0) { return("There is no operators in the operations stack", null); } else { return("There are too much operator in the operations stack", null); } }
/// <summary> /// Evalúa una serie de expresiones /// </summary> internal async Task <(string error, VariableModel variable)> EvaluateAsync(ContextModel context, ExpressionsCollection expressions, CancellationToken cancellationToken) { return(await ComputeAsync(context, new ExpressionConversorRpn().ConvertToRPN(expressions), cancellationToken)); }
/// <summary> /// Convierte una colección de expresiones en una pila de expresiones en notación polaca inversa (sin paréntesis) /// </summary> internal ExpressionsCollection ConvertToRPN(ExpressionsCollection expressions) { ExpressionsCollection stackOutput = new ExpressionsCollection(); Stack <ExpressionBase> stackOperators = new Stack <ExpressionBase>(); // Convierte las expresiones en una pila foreach (ExpressionBase expressionBase in expressions) { switch (expressionBase) { case ExpressionParenthesis expression: // El paréntesis izquierdo, se mete directamente en la pila de operadores if (expression.Open) { stackOperators.Push(expression); } else { bool end = false; // Paréntesis derecho. Saca todos los elementos del stack hasta encontrar un paréntesis izquierdo while (stackOperators.Count > 0 && !end) { ExpressionBase expressionOperator = stackOperators.Pop(); if (expressionOperator is ExpressionParenthesis expressionStack) { if (!expressionStack.Open) { end = true; } else { stackOutput.Add(expressionStack); } } else { stackOutput.Add(expressionOperator); } } } break; case ExpressionOperatorBase expression: bool endOperator = false; // Recorre los operadores de la pila while (stackOperators.Count > 0 && !endOperator) { ExpressionBase lastOperator = stackOperators.Peek(); // Si no hay ningún operador en la pila o la prioridad del operador actual // es mayor que la del último de la pila, se mete el último operador if (EndSearchOperator(expression, lastOperator)) { endOperator = true; } else // ... si el operador tiene una prioridad menor que el último de la pila, se quita el último operador de la pila y se compara de nuevo { stackOutput.Add(stackOperators.Pop()); } } // Añade el operador a la pila de operadores stackOperators.Push(expression); break; case ExpressionConstant expression: stackOutput.Add(expression); break; case ExpressionVariableIdentifier expression: stackOutput.Add(expression); break; default: stackOutput.Add(new ExpressionError("Unknown expression")); break; } } // Añade todos los elementos que queden en el stack de operadores al stack de salida while (stackOperators.Count > 0) { stackOutput.Add(stackOperators.Pop()); } // Devuelve la pila convertida a notación polaca inversa return(stackOutput); }
/// <summary> /// Calcula una expresión /// </summary> private ValueBase Compute(ExpressionsCollection stackExpressions) { Stack <ValueBase> stackoperations = new Stack <ValueBase>(); bool hasError = false; // Calcula el resultado foreach (ExpressionBase expression in stackExpressions) { if (!hasError) { if (expression.Token.Type == Tokens.Token.TokenType.String || expression.Token.Type == Tokens.Token.TokenType.Number) { stackoperations.Push(ValueBase.GetInstance(expression.Token.Content)); } else if (expression is ExpressionVariableIdentifier) { ValueBase variableValue = GetValueVariable(expression as ExpressionVariableIdentifier); // Añade el resultado a la pila (aunque haya un error, para que así este sea el último operando en la pila) if (variableValue != null) { hasError = variableValue.HasError; stackoperations.Push(variableValue); } else { hasError = true; stackoperations.Push(ValueBase.GetError("No se encuentra el valor de la variable")); } } else { ValueBase result = null; // Realiza la operación switch (expression.Token.Content) { case "+": case "-": case "*": case "/": case ">=": case "<=": case "==": case "!=": case ">": case "<": case "||": case "&&": result = ComputeBinary(stackoperations, expression.Token.Content); break; default: result = ValueBase.GetError($"Operador desconocido: {expression.Token.Content}"); break; } // Añade el resultado a la pila (aunque haya error, para que así sea el último operador de la pila) hasError = result.HasError; stackoperations.Push(result); } } } // Obtiene el resultado if (hasError || stackoperations.Count == 1) { return(stackoperations.Pop()); } else if (stackoperations.Count == 0) { return(ValueBase.GetError("No hay ningún operador en la pila de operaciones")); } else { return(ValueBase.GetError("Hay más de un operador en la pila de instrucciones")); } }
/// <summary> /// Evalúa una serie de expresiones /// </summary> internal ValueBase Evaluate(ExpressionsCollection stackExpressions) { return(Compute(stackExpressions.Clone())); }