我正在学习如何编写标记器,解析器以及作为练习我用JavaScript编写计算器。
我正在使用prase树方法(我希望我的这个术语正确)来构建我的计算器。我根据运算符优先级构建了一个令牌树。
例如,给定表达式a*b+c*(d*(g-f))
,正确的树将是:
+
/ \
* \
/ \ \
a b \
*
/ \
c *
/ \
d -
/ \
g f
一旦我进了树,我就可以遍历它并递归地在左右节点的每个根节点上应用操作来找到表达式的值。
然而,最大的问题是实际构建这棵树。我无法弄清楚如何正确地做到这一点。我无法拆分运算符+
,-
,/
和*
,并且由于优先级而从左侧和右侧创建树。
到目前为止,我所做的是将表达式标记化。所以给定a*b+c*(d*(g-f))
,我最终会得到一个标记数组:
[a, Operator*, b, Operator+, c, Operator*, OpenParen, d, Operator*, OpenParen, g, Operator-, f, CloseParen, CloseParen]
但是,我无法弄清楚如何从这个令牌数组转到我可以遍历并找出值的树的下一步。任何人都可以帮我解释如何做到这一点吗?
答案 0 :(得分:1)
正确的伴侣,我不知道如何使这看起来很漂亮
我在C中编写了一个类似的程序,但我的树是颠倒的,这意味着新的操作符成为根。
中的计算器解析树例如:输入2 + 3 - 4
从空节点开始
{}
规则1:当您读取数字时,在当前节点的左侧或右侧附加一个子项,无论哪个为空
{}
/
{2}
规则2:然后你读了一个操作符,你必须从当前节点{2}开始爬到一个空节点,当你找到一个时,将它的值改为+,如果没有空节点,你必须创建一个然后使它成为树的根
{+}
/
{2}
你遇到另一个号码,转到规则1,我们目前在{+}找到一个空的一方(这次正确)
{+}
/ \
{2} {3}
现在我们有了新的操作数' - ',但由于{3}的父级已满,您必须创建新节点并使其成为所有内容的根
{-}
/
{+}
/ \
{2} {3}
哦,看看那个,另一个数字,因为我们当前指向根,让我们找一个空的{ - }孩子,(提示右边)
{-}
/ \
{+} {4}
/ \
{2} {3}
构建树后,查看函数getresult()来计算所有内容
您可能想知道如何使用Parenthesization。我是这样做的:
每次遇到'('并在其中构建其余输入时,我都会创建全新的树。如果我读了另一个'(',我创建另一个并继续使用新树构建。
读取输入后,我将所有树的根连接到另一个以制作最终的树。查看代码和自述文件,我必须绘制解释一切。
希望这也有助于未来的读者
答案 1 :(得分:0)
我经常看到这个问题所以我只是把它写下来了。这肯定会让你朝着正确的方向前进,但要小心,这是一个自上而下的解析器,因此它可能无法用你期望的优先级来解释表达式。
class Program
{
static void Main()
{
Console.WriteLine(SimpleExpressionParser.Parse("(10+30*2)/20").ToString());
Console.ReadLine();
//
// ouput: ((10+30)*2)/20
}
public static class SimpleExpressionParser
{
public static SimpleExpression Parse(string str)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
int index = 0;
return InternalParse(str, ref index, 0);
}
private static SimpleExpression InternalParse(string str, ref int index, int level)
{
State state = State.ExpectLeft;
SimpleExpression _expression = new SimpleExpression();
int startIndex = index;
int length = str.Length;
while (index < length)
{
char chr = str[index];
if (chr == ')' && level != 0)
{
break;
}
switch (state)
{
case State.ExpectLeft:
case State.ExpectRight:
{
SimpleExpression expression = null;
if (Char.IsDigit(chr))
{
int findRep = FindRep(Char.IsDigit, str, index + 1, length - 1);
expression = new SimpleExpression(int.Parse(str.Substring(index, findRep + 1)));
index += findRep;
}
else if (chr == '(')
{
index++;
expression = InternalParse(str, ref index, level + 1);
}
if (expression == null)
{
throw new Exception(String.Format("Expression expected at index {0}", index));
}
if (state == State.ExpectLeft)
{
_expression.Left = expression;
state = State.ExpectOperator;
}
else
{
_expression.Right = expression;
state = State.ExpectFarOperator;
}
}
break;
case State.ExpectOperator:
case State.ExpectFarOperator:
{
SimpleExpressionOperator op;
switch (chr)
{
case '+': op = SimpleExpressionOperator.Add; break;
case '-': op = SimpleExpressionOperator.Subtract; break;
case '*': op = SimpleExpressionOperator.Multiply; break;
case '/': op = SimpleExpressionOperator.Divide; break;
default:
throw new Exception(String.Format("Invalid operator encountered at index {0}", index));
}
if (state == State.ExpectOperator)
{
_expression.Operator = op;
state = State.ExpectRight;
}
else
{
index++;
return new SimpleExpression(op, _expression, InternalParse(str, ref index, level));
}
}
break;
}
index++;
}
if (state == State.ExpectLeft || state == State.ExpectRight)
{
throw new Exception("Could not complete expression");
}
return _expression;
}
private static int FindRep(Func<char, bool> validator, string str, int beginPos, int endPos)
{
int pos;
for (pos = beginPos; pos <= endPos; pos++)
{
if (!validator.Invoke(str[pos]))
{
break;
}
}
return pos - beginPos;
}
private enum State
{
ExpectLeft,
ExpectRight,
ExpectOperator,
ExpectFarOperator
}
}
public enum SimpleExpressionOperator
{
Add,
Subtract,
Multiply,
Divide
}
public class SimpleExpression
{
private static Dictionary<SimpleExpressionOperator, char> opEquivs = new Dictionary<SimpleExpressionOperator, char>()
{
{ SimpleExpressionOperator.Add, '+' },
{ SimpleExpressionOperator.Subtract, '-' },
{ SimpleExpressionOperator.Multiply, '*' },
{ SimpleExpressionOperator.Divide, '/' }
};
public SimpleExpression() { }
public SimpleExpression(int literal)
{
Literal = literal;
IsLiteral = true;
}
public SimpleExpression(SimpleExpressionOperator op, SimpleExpression left, SimpleExpression right)
{
Operator = op;
Left = left;
Right = right;
}
public bool IsLiteral
{
get;
set;
}
public int Literal
{
get;
set;
}
public SimpleExpressionOperator Operator
{
get;
set;
}
public SimpleExpression Left
{
get;
set;
}
public SimpleExpression Right
{
get;
set;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
AppendExpression(sb, this, 0);
return sb.ToString();
}
private static void AppendExpression(StringBuilder sb, SimpleExpression expression, int level)
{
bool enclose = (level != 0 && !expression.IsLiteral && expression.Right != null);
if (enclose)
{
sb.Append('(');
}
if (expression.IsLiteral)
{
sb.Append(expression.Literal);
}
else
{
if (expression.Left == null)
{
throw new Exception("Invalid expression encountered");
}
AppendExpression(sb, expression.Left, level + 1);
if (expression.Right != null)
{
sb.Append(opEquivs[expression.Operator]);
AppendExpression(sb, expression.Right, level + 1);
}
}
if (enclose)
{
sb.Append(')');
}
}
}
}
答案 2 :(得分:0)
您可能对math.js感兴趣,这是一个包含高级表达式解析器的JavaScript数学库(请参阅docs和examples。您只需解析一个表达式:
var node = math.parse('a*b+c*(d*(g-f))');
返回节点树(可以编译和评估)。
您可以在此处学习解析器代码:https://github.com/josdejong/mathjs/blob/master/src/expression/parse.js
您可以在这里找到一些简单的表达式解析器示例(对于C ++和Java):http://www.speqmath.com/tutorials/,这对于研究表达式解析器的基础知识非常有用。
答案 3 :(得分:-1)
参见https://github.com/carlos-chaguendo/arboles-binarios 该算法将代数表达式转换为二叉树。将表达式转换为postfix,然后计算并绘制树
测试:2+3*4+2^3
输出:
22 =
└── +
├── +
│ ├── 2
│ └── *
│ ├── 3
│ └── 4
└── ^
├── 2
└── 3