语法分析的结果是生成 AST。算法分为自顶向下和自底向上算法,其中,递归下降算法是一种常见的自顶向下算法。
“int age = 45”这个语句,语法分析算法的示意图:
终结符都是词法分析中产生的 Token。而那些非叶子节点,就是非终结符。
intDeclaration : Int Identifier (’=’ additiveExpression)?;
SimpleASTNode node = null;
Token token = tokens.peek(); // 预读
if (token != null && token.getType() == TokenType.Int) { // 匹配 Int
token = tokens.read(); // 消耗掉 int
if (tokens.peek().getType() == TokenType.Identifier) { // 匹配标识符
token = tokens.read(); // 消耗掉标识符
// 创建当前节点,并把变量名记到 AST 节点的文本值中,
// 这里新建一个变量子节点也是可以的
node = new SimpleASTNode(ASTNodeType.IntDeclaration, token.getText());
token = tokens.peek(); // 预读
if (token != null && token.getType() == TokenType.Assignment) {
tokens.read(); // 消耗掉等号
SimpleASTNode child = additive(tokens); // 匹配一个表达式
if (child == null) {
throw new Exception("invalide variable initialization, expecting an expression");
}
else{
node.addChild(child);
}
}
} else {
throw new Exception("variable name expected");
}
}
通常会对产生式的每个部分建立一个子节点,比如变量声明语句会建立四个子节点,分别是 int 关键字、标识符、等号和表达式。
语法规则
additiveExpression
: multiplicativeExpression
| multiplicativeExpression Plus additiveExpression
;
上面的表达式是语法规则,是用BNF表达的。以addtive为例,它有两个产生式。
产生式1:一个乘法表达式
产生式2:一个加法表达式 + 乘法表达式。
通过上面两个产生式的组合,特别是产生式2的递归调用,就能推导出所有的加减乘数算术表达式。
比如,对于2*3这个表达式,运用的是产生式1。
对于2+3*5,运用的是产生式2。
我上面用的语法规则的写法,实际上是后面会用到的Antlr工具的写法。你也可以这样书写,就是一般教材上的写法:
A -> M | A + M
M -> int | M * int
我们每个非终结符只用了一个大写字母代表,比较简洁。
其中的竖线,是选择其一。你还可以拆成最简单的方式,形成4条规则:
A -> M
A -> A + M
M -> int
M -> M * int
上面这些不同的写法,都是等价的。