/中文/
/中文/
/中文/
/中文/
/中文/
/中文/
/中文/
/中文/
/中文/
/中文/
递归下降语法分析器是一款占用空间小,使用简单的语法分析方法。递归下降语法分析器适合手写语法编译,由开发人员高度控制,在提供错误信息方面也很有优势,欢迎下载使用!
【递归下降语法分析器相关说明】:
文法规定节点即使没有儿子(儿子是空),括号和逗号也是不可省略的,所以只有一个节点的话也要写成A(,)。现在我们要写一个解析器,输入这种字符串,然后在内存中建立起这棵二叉树。
其中内存中的二叉树是用下面这样的类来表示的:
class Node
{
public Node LeftChild { get; private set; }
public Node RightChild { get; private set; }
public char Label { get; private set; }
public Node(char label, Node left, Node right)
{
Label = label;
LeftChild = left;
RightChild = right;
}
}
一般步骤:
使用一个索引来记录当前扫描的位置。通常将它做成一个整数字段。为每个非终结符编写一个方法。如果一个非终结符有超过一个的产生式,则在这个方法中对采用哪个产生式进行分支预测。处理单一产生式时,遇到正确终结符则将第一步创建的扫描索引位置向前移动;如遇到非终结符则调用第二步中创建的相应方法。如果需要产生解析的结果(比如本例中的二叉树),在方法返回之前将它构造出来。我们马上来试验一下。首先建立一个类,然后存放一个索引变量来保存当前扫描位置。然后要为每一个非终结符创建一个方法,我们的文法中只有一个非终结符N,所以只需创建一个方法:
class BinaryTreeParser
{
private string m_inputString;
private int m_index;
//初始化输入字符串和索引的构造函数,略
Node ParseNode()
{
}
}
回到刚才的产生式,我们看到非终结符N有两个产生式,所以在ParseNode方法的一开始我们必须做出分支预测。分支预测的方法是超前查看(look ahead)。就是说我们先“偷窥”当前位置前方的字符,然后判断应该用哪个产生式继续分析。非终结符N的两个产生式其中一个会产生a(N, N)这个的结构,而另一个则直接产生空字符串。那现在知道,起码有一种可能就是会遇到一个字母,这时候应该采用N → a(N, N)这个产生式继续分析。那么什么时候应该采用N → ε进行分析呢?我们观察产生式右侧所有出现N的地方,倘若N是空字符串,那么N后面的字符就会直接出现,也就是逗号和右括号。于是这就是我们的分支预测:如果超前查看遇到英文字母,预测分支N → a(N, N)如果超前查看遇到逗号、右括号预测分支N → ε转化成代码就是这样:
Node ParseNode()
{
int lookAheadIndex = m_index;
char lookAheadChar = m_inputString[lookAheadIndex];
if (Char.IsLetter(lookAheadChar))
{
//采用N → a(N, N)继续分析
}
else if (lookAheadChar == ',' || lookAheadChar == ')' )
{
//采用N → ε继续分析
}
else
{
throw new Exception("语法错误");
}
}接下来我们分别来看两个分支怎么处理。先来看N → ε,这种情况下非终结符是个空字符串,所以我们不需要移动当前索引,直接返回null表示空节点。再来看N → a(N, N) 分支,倘若输入的字符串没有任何语法错误,那就应该依次遇到字母、左括号、N、逗号、N右括号。根据上面的规则,凡是遇到终结符,就移动当前索引,直接向前扫描;而要是遇到非终结符,就递归调用相应节点的方法。所以(不考虑语法错误)的完整方法代码如下:Node ParseNode()
{
int lookAheadIndex = m_index;
char lookAheadChar = m_inputString[lookAheadIndex];
if (Char.IsLetter(lookAheadChar))
{
//采用N → a(N, N)继续分析
char label = m_inputString[m_index++]; //解析字母
m_index++; //解析左括号,因为不需要使用它的值,所以直接跳过
Node left = ParseNode(); //非终结符N,递归调用
m_index++; //解析逗号,跳过
Node right = ParseNode(); //非终结符N,递归调用
m_index++; //解析右括号,跳过
return new Node(label, left, right);
}
else if (lookAheadChar == ',' || lookAheadChar == ')')
{
//采用N → ε继续分析
//无需消耗输入字符,直接返回null
return null;
}
else
{
throw new Exception("语法错误");
}
}