您应该查找“抽象语法树”和“表达式树”,以及“词法分析”、“语法”、“解析”和“编译器理论”。对于大多数事物来说,从文本输入中获取意义是相当困难的(尽管我们通常会尝试确保我们有简单的输入)。
生成解析器的第一步是编写输入语言的语法。在这种情况下,您的输入语言是一些数学表达式,因此您可以做如下操作:
expr => <function_identifier> ( stmt )
( stmt )
<variable_identifier>
<numerical_constant>
stmt => expr <operator> stmt
我已经好几年没有写过像这样的语法了(请查阅BNF
和EBNF
),所以我可能会犯一些明显的错误,但会有其他人友善地指出。
根据你如何处理运算符优先级(乘除比加减等),这可能会变得更加复杂,但是在这种情况下语法的目的是帮助你编写解析器。
有一些工具可以帮助你完成这项任务(如yacc
、bison
、antlr
等),但你也可以手动完成。有许多方法可以做到这一点,但它们都有一个共同点——堆栈。处理这种语言需要使用一种称为下推自动机的东西,它只是一种可以基于新输入、当前状态和堆栈顶部项进行决策的方式。它可以做出的决策包括压入、弹出、改变状态和组合(将2+3
转换为5
就是一种组合形式)。组合通常被称为产生式,因为它产生了一个结果。
在各种常见的解析器类型中,你几乎肯定会从递归下降解析器开始。它们通常直接用通用编程语言(如C)编写。这种解析器由多个(通常很多)函数组成,它们互相调用,并最终使用系统堆栈作为下推自动机堆栈。
你还需要写下构成你的语言的不同类型的单词和运算符。这些单词和运算符称为词素,代表你的语言的标记。我在语法中用<like_this>
表示这些标记,括号除外,它们表示它们自己。
你最有可能想用一组正则表达式来描述你的词素。如果你使用过grep
、sed
、awk
或perl
,你应该熟悉它们。它们是一种描述所谓的正则语言的方式,可以被一些称为有限状态自动机的东西处理。这只是一种说法,即它是一个程序,可以通过仅考虑其当前状态和下一个输入(下一个字符的输入)来做出关于改变状态的决策。例如,你的词法描述的一部分可能是:
[A-Z] variable-identifier
sqrt function-identifier
log function-identifier
[0-9]+ unsigned-literal
+ operator
- operator
还有一些工具可以为此生成代码。其中之一是高度与解析器生成程序yacc集成的词法分析器lex,但由于您正在学习,因此您也可以使用C编写自己的标记化/词法分析代码。
在完成所有这些工作之后(可能需要相当长的时间),您需要让解析器构建一个树来表示输入的表达式和语法。在表达式求值的简单情况下(例如编写一个简单的命令行计算器程序),您可以让解析器在处理输入时评估公式,但对于您的情况,我理解的是,您需要制作一棵树(或逆波兰表示法,但在我看来,树更容易)。
然后,在读取变量的值之后,您可以遍历树并计算实际数字。