所以我一直在阅读一些关于词法分析器、解析器、解释器甚至编译的内容。
对于我正在尝试实现的一种语言,我选择了递归下降解析器。由于该语言的原始语法具有左递归,我不得不稍微重写它。
这是我所拥有的语法的简化版本(请注意,它不是任何标准格式语法,但有点伪,我猜,这就是我在文档中找到它的方式):
expr:
-----
expr + expr
expr - expr
expr * expr
expr / expr
( expr )
integer
identifier
为了摆脱左递归,我把它变成了这个(注意添加了 NOT 运算符):
expr:
-----
expr_term {+ expr}
expr_term {- expr}
expr_term {* expr}
expr_term {/ expr}
expr_term:
----------
! expr_term
( expr )
integer
identifier
然后使用以下子例程(简化的伪代码)检查我的令牌:
public string Expression()
{
string term = ExpressionTerm();
if (term != null)
{
while (PeekToken() == OperatorToken)
{
term += ReadToken() + Expression();
}
}
return term;
}
public string ExpressionTerm()
{
//PeekToken and ReadToken accordingly, otherwise return null
}
这行得通!调用后的结果Expression
总是等于给出的输入。
这让我想知道:如果我在这些子例程中创建 AST 节点而不是字符串,并使用中缀评估器评估 AST(它还牢记运算符的关联性和优先级等),我不会得到相同的结果?
如果我这样做了,那么为什么有这么多主题涵盖“修复左递归,牢记关联性等等”,而实际上解决起来“非常简单”,甚至看起来似乎不是问题?或者它真的是人们关心的结果 AST 的结构(而不是它的评估结果)?谁能解释一下,我可能也搞错了,哈哈!