用于定义数学运算符符的文法称为运算符文法或运算符优先文法。这样的文法有这样的限制,即没有产生式的右侧有空的(空产生式)或右侧有两个相邻的非终结符。
例子 –
这是运算符语法的示例:
E->E+E/E*E/id
但是,下面给出的语法不是运算符语法,因为两个非终结符彼此相邻:
S->SAS/a
A->bSb/b
不过,我们可以将其转换为运算符语法:
S->SbSbS/SbS/a
A->bSb/b
运算符优先级解析器 –
运算符优先级解析器是解释运算符语法的自底向上解析器。此解析器仅用于运算符语法。除了运算符优先级解析器之外,任何解析器都不允许有歧义的语法。
有两种方法可以确定一对终端之间应该保持什么优先级关系:
- 使用运算符的常规关联性和优先级。
- 选择运算符优先级关系的第二种方法是首先为语言构造一个无歧义的文法,该文法反映其解析树中正确的结合性和优先级。
这个解析器依赖于以下三个优先级关系: ⋖、≐、⋗
a ⋖ b这意味着 a “优先于” b。
a ⋗ b这意味着 a “优先于” b。
a ≐ b这意味着 a “与 b 具有相同的优先级”。
图 –语法 E->E+E/E*E/id 的运算符优先级关系表
id 和 id 之间没有任何关系,因为不会比较 id 并且两个变量不能并排出现。这个表还有一个缺点——如果我们有 n 个运算符,那么表的大小将为 n*n,复杂度将为 0(n 2 )。为了减小表的大小,我们使用运算符函数table 。
运算符优先级解析器通常不存储带有关系的优先级表;相反,它们以特殊的方式实施。运算符优先级解析器使用优先级函数将终结符映射为整数,符号之间的优先级关系通过数值比较来实现。解析表可以由两个优先级函数f和g编码,它们将终结符映射到整数。我们选择 f 和 g 使得:
- f(a) < g(b) 每当 a 优先于 b
- f(a) = g(b) 只要 a 和 b 具有相同的优先级
- f(a) > g(b) 只要 a 优先于 b
示例 –考虑以下语法:
E -> E + E/E * E/( E )/id
这是表示优先函数的有向图:
由于图中没有环,我们可以制作这个函数表:
fid -> g* -> f+ ->g+ -> f$
gid -> f* -> g* ->f+ -> g+ ->f$
表的大小是2n 。
函数表的一个缺点是,即使我们在关系表中有空条目,我们在函数表中也有非空条目。空白条目也称为错误。因此关系表的错误检测能力大于函数表。
#include
#include
#include
// function f to exit from the loop
// if given condition is not true
void f()
{
printf("Not operator grammar");
exit(0);
}
void main()
{
char grm[20][20], c;
// Here using flag variable,
// considering grammar is not operator grammar
int i, n, j = 2, flag = 0;
// taking number of productions from user
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%s", grm[i]);
for (i = 0; i < n; i++) {
c = grm[i][2];
while (c != '\0') {
if (grm[i][3] == '+' || grm[i][3] == '-'
|| grm[i][3] == '*' || grm[i][3] == '/')
flag = 1;
else {
flag = 0;
f();
}
if (c == '$') {
flag = 0;
f();
}
c = grm[i][++j];
}
}
if (flag == 1)
printf("Operator grammar");
}
Input :3
A=A*A
B=AA
A=$
Output : Not operator grammar
Input :2
A=A/A
B=A+A
Output : Operator grammar
$ 在这里是一个空产生式,这在运算符语法中也是不允许的。
优点 –
- 它可以很容易地手工构建。
- 实现这种类型的解析很简单。
缺点——
- 很难处理像减号 (-) 这样的标记,它有两种不同的优先级(取决于它是一元还是二元)。
- 它仅适用于一小类语法。