我找到了一个简单的数学解析器类,但它不支持逻辑运算或表达式,如:
(a > b) ? (x^2) : (a / 3.56)
这是解析器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Calcultra
{
class Parser
{
// The available operators for use in mathematical expressions.
private string[] _operators = { "-", "+", "/", "*", "^" };
// The mathematical operations performed by the operators.
private Func<double, double, double>[] _operations = {
(a1, a2) => a1 - a2,
(a1, a2) => a1 + a2,
(a1, a2) => a1 / a2,
(a1, a2) => a1 * a2,
(a1, a2) => Math.Pow(a1, a2)
};
/**
* Parses and evaluates a mathematical expression and returns the result.
*/
public double Eval(string expression)
{
List<string> tokens = getTokens(expression);
Stack<double> operandStack = new Stack<double>();
Stack<string> operatorStack = new Stack<string>();
int tokenIndex = 0;
while (tokenIndex < tokens.Count)
{
string token = tokens[tokenIndex];
if (token == "(")
{
string subExpr = getSubExpression(tokens, ref tokenIndex);
operandStack.Push(Eval(subExpr));
continue;
}
if (token == ")")
{
throw new ArgumentException("Mis-matched parentheses in expression");
}
// If this is an operator.
if (Array.IndexOf(_operators, token) >= 0)
{
while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek()))
{
string op = operatorStack.Pop();
double arg2 = operandStack.Pop();
double arg1 = operandStack.Pop();
operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
}
operatorStack.Push(token);
}
else
{
operandStack.Push(double.Parse(token));
}
tokenIndex += 1;
}
while (operatorStack.Count > 0)
{
string op = operatorStack.Pop();
double arg2 = operandStack.Pop();
double arg1 = operandStack.Pop();
operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
}
return operandStack.Pop();
}
/**
* Why even write a description for this function.
*/
private string getSubExpression(List<string> tokens, ref int index)
{
StringBuilder subExpr = new StringBuilder();
int parenlevels = 1;
index += 1;
while (index < tokens.Count && parenlevels > 0)
{
string token = tokens[index];
if (tokens[index] == "(")
{
parenlevels += 1;
}
if (tokens[index] == ")")
{
parenlevels -= 1;
}
if (parenlevels > 0)
{
subExpr.Append(token);
}
index += 1;
}
if ((parenlevels > 0))
{
throw new ArgumentException("Mis-matched parentheses in expression");
}
return subExpr.ToString();
}
/**
* Tokenizes the given mathematical expression.
*/
private List<string> getTokens(string expression)
{
string operators = "()^*/+-";
List<string> tokens = new List<string>();
StringBuilder sb = new StringBuilder();
foreach (char c in expression.Replace(" ", string.Empty))
{
if (operators.IndexOf(c) >= 0)
{
if ((sb.Length > 0))
{
tokens.Add(sb.ToString());
sb.Length = 0;
}
tokens.Add(c.ToString());
}
else
{
sb.Append(c);
}
}
if ((sb.Length > 0))
{
tokens.Add(sb.ToString());
}
return tokens;
}
}
}
在提出这个问题的过程中,我尝试了更多来理解代码,我真正掌握的主要部分是:
// If this is an operator.
if (Array.IndexOf(_operators, token) >= 0)
{
while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek()))
{
string op = operatorStack.Pop();
double arg2 = operandStack.Pop();
double arg1 = operandStack.Pop();
operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
}
operatorStack.Push(token);
}
else
{
operandStack.Push(double.Parse(token));
}
我看到它正在检查运算符,并将参数之间的操作推送到操作数堆栈。我想这里是我检查条件语句的地方,我需要检查?
和:
符号,获取所有三个子表达式,计算条件表达式,并选择最后两个中的一个子表达式推入操作数堆栈。至少这是我理解它的方式。我不太确定如何处理这些比较。
我意识到有一个类似的问题:Adding Conditionals & Functions to a Math Parser
但是我正在使用C#.NET我无法弄清楚如何适应我的代码所做的。我不是要求你为我编写代码(不是我会抱怨xP),但我真的需要一个起点。我需要采取哪些步骤来完成这项工作。
答案 0 :(得分:0)
我没试过这个,但是代码看起来并不太复杂,你应该自己尝试一下。尝试在调试器中单步执行Parse
。
首先要做的是将"<"
,"?"
和":"
等新字符串添加到_operators
并在getTokens
中查找。同时将Func
添加到执行操作的_operations
。
我预见的一个问题是它处理的唯一数据类型是double
,而<
等关系运算符返回bool
s。您可以使用旧的C约定,其中零为false且非零为true,但请注意,这将使您的解析器非类型安全。
您要面对的另一个障碍是三元运算符?
的参数是表达式,而不是标记,而您的解析器只查看标记。您可以使用Pascal中的快捷方式来完全评估每个表达式。您将不得不修改Eval
以处理?
具有最低优先级但在您的表达式中位于:
之前的事实,并且看起来Eval
严格保留-to-权。 IIRC Pascal使用了另一个快捷方式,在内部将括号添加到表达式以处理优先级,因此例如1>0 ? 1+1 : 1-1
变为(1>0)?((1+1):(1-1))
变为1?(2:0)
,变为2
。
更进一步,你必须生成Abstract Syntax Trees,而不是代币。