我很难弄清楚这个问题以及减少班次的问题。
在末尾添加";"并不能解决问题,因为我无法更改语言,它需要像以下示例一样。是否有任何预处理操作数有效?
示例如下:
变量可以声明为 : 作为指针或 int 作为整数,因此,这两者都有效:
<int> a = 0
int a = 1
代码如下:
%left '<'
declaration: variable
| declaration variable
variable : type tNAME '=' expr
| type tNAME
type : '<' type '>'
| tINT
expr : tINTEGER
| expr '<' expr
它显然在 expr 之后给出了一个移位/减少问题,因为它可以针对"less"运算符的 expr 进行移位,或者为另一个变量声明进行 reduce。
我希望在变量声明上赋予优先级,并尝试创建一个 %nonassoc prec_aux并将 %<"类型">"%prec prec_aux放在 tNAME 之后,但它不能解决我的问题:S
我该如何解决这个问题?
输出为:
好吧,无法弄清楚 hwo 在回复上发布换行符和代码......所以这里是输出:
35: shift/reduce conflict (shift 47, reduce 7) on '<'
state 35
variable : type tNAME '=' expr . (7)
expr : expr . '+' expr (26)
expr : expr . '-' expr (27)
expr : expr . '*' expr (28)
expr : expr . '/' expr (29)
expr : expr . '%' expr (30)
expr : expr . '<' expr (31)
expr : expr . '>' expr (32)
'>' shift 46
'<' shift 47
'+' shift 48
'-' shift 49
'*' shift 50
'/' shift 51
'%' shift 52
$end reduce 7
tINT reduce 7
这就是输出,错误似乎是我提到的那个。
有谁知道不同的解决方案,除了向语言添加新终端之外,这不是一个真正的选择?
我认为解决方案是重写语法,以便它可以以某种方式向前看,看看它是"<"之后的类型还是 expr,但我看不到如何做。
优先级不太可能起作用,因为它是相同的字符。有没有办法为我们定义的类型赋予优先级?比如声明?
提前致谢
你的语法在这样的文本中会感到困惑:
int a = b
<int> c
第二行的"<"可以是第一个声明中表达式的一部分。 它必须进一步向前看才能找到答案。
这就是大多数语言都有语句终止符的原因。 这不会产生冲突:
%%
%token tNAME;
%token tINT;
%token tINTEGER;
%token tTERM;
%left '<';
declaration: variable
| declaration variable
variable : type tNAME '=' expr tTERM
| type tNAME tTERM
type : '<' type '>'
| tINT
expr : tINTEGER
| expr '<' expr
在创建解析器时,了解如何设计语法以消除可能的冲突会有所帮助。 为此,您需要了解解析器的工作原理,这超出了本答案的范围:)<</p>
这里的基本问题是,您需要比使用 yacc/bison 获得的 1 个令牌更多的展望。 当解析器看到一个<
它无法判断它是否完成了前一个声明并查看括号类型的开头,或者这是否是一个小于运算符。 您可以在此处执行两项基本操作:
使用解析方法,如 bison 的
%glr-parser
选项或 btyacc,它可以处理非 LR(1) 语法使用词法分析器执行额外的前瞻并返回消除歧义的标记
对于后者,您将让词法分析器在"<"之后进行额外的前瞻,如果后跟看起来像类型的东西,则返回不同的标记。 最简单的方法是使用 flex 的 /
前瞻运算符。 例如:
"<"/[ tnr]*"<" return OPEN_ANGLE;
"<"/[ tnr]*"int" return OPEN_ANGLE;
"<" return '<';
然后,您将野牛规则更改为期望OPEN_ANGLE
类型而不是<
:
type : OPEN_ANGLE type '>'
| tINT
expr : tINTEGER
| expr '<' expr
对于更复杂的问题,您可以使用 flex start 状态,甚至可以在词法分析器和解析器之间插入整个标记过滤器/转换传递。
这是修复程序,但不完全令人满意:
%{
%}
%token tNAME tINT tINTEGER
%left '<'
%left '+'
%nonassoc '=' /* <-- LOOK */
%%
declaration: variable
| declaration variable
variable : type tNAME '=' expr
| type tNAME
type : '<' type '>'
| tINT
expr : tINTEGER
| expr '<' expr
| expr '+' expr
;
此问题是这两个 LR 项目之间的冲突:点决赛:
variable : type tNAME '=' expr_no_less .
而这个:
expr : expr . '<' expr
请注意,这两者具有不同的运算符。正如您似乎认为的那样,这不是涉及"<"运营商的不同制作之间的冲突。
通过将=
添加到优先级排名中,我们可以在冲突诊断消失的意义上解决问题。
请注意,我给=
一个很高的优先级。这将通过支持减少来解决冲突。这意味着不能使用"<"表达式作为初始值设定项:
int name = 4 < 3 // syntax error
当看到<时,int name = 4
想要减少,这个想法是<
必须是下一个声明的开始,作为type
生产的一部分。>
若要允许将<
关系表达式用作初始值设定项,请将对括号的支持添加到表达式语法中。然后用户可以用括号括起来:
int foo = (4 < 3) <int> bar = (2 < 1)
如果没有更强大的解析方法或黑客,就无法解决这个问题。
如果在%left '<'
之前移动%nonassoc
,使其优先级较低怎么办?然后这种转变将受到青睐。不幸的是,这会导致您无法在声明之后编写另一个<int>
声明。
int foo = 3 <int> bar = 4
^ // error: the machine shifted and is now doing: expr '<' . expr.
所以这是解决冲突的错误方法;你希望能够编写多个这样的声明。
另一个注意事项:
我的 TXR 语言实现了与解析表达式语法等效的东西,可以很好地处理此语法。这本质上是LL(无限),它胜过LALR(1)。
我们甚至不必有单独的词法分析器和解析器!这只是由于单一符号展望的限制以及 1970 年代硬件对最高效率的需求而变得必要的。
shell 命令行的示例输出,演示了通过转换为类似 Lisp 的抽象语法树进行解析,该语法树绑定到变量 dl
(声明列表)。因此,这是通过语义操作完成的,产生了可以在TXR Lisp中进一步处理的输出。标识符通过调用intern
转换为 Lisp 符号,数字也转换为数字对象。
$ txr -l type.txr -
int x = 3 < 4 int y
(dl (decl x int (< 3 4)) (decl y int nil))
$ txr -l type.txr -
< int > x = 3 < 4 < int > y
(dl (decl x (pointer int) (< 3 4)) (decl y (pointer int) nil))
$ txr -l type.txr -
int x = 3 + 4 < 9 < int > y < int > z = 4 + 3 int w
(dl (decl x int (+ 3 (< 4 9))) (decl y (pointer int) nil)
(decl z (pointer int) (+ 4 3)) (decl w int nil))
$ txr -l type.txr -
<<<int>>>x=42
(dl (decl x (pointer (pointer (pointer int))) 42))
(type.txr
的源代码):
@(define ws)@/[ t]*/@(end)
@(define int)@(ws)int@(ws)@(end)
@(define num (n))@(ws)@{n /[0-9]+/}@(ws)@(filter :tonumber n)@(end)
@(define id (id))@
@(ws)@{id /[A-Za-z_][A-Za-z_0-9]*/}@(ws)@
@(set id @(intern id))@
@(end)
@(define type (ty))@
@(local l)@
@(cases)@
@(int)@
@(bind ty @(progn 'int))@
@(or)@
<@(type l)>@
@(bind ty @(progn '(pointer ,l)))@
@(end)@
@(end)
@(define expr (e))@
@(local e1 op e2)@
@(cases)@
@(additive e1)@{op /[<>]/}@(expr e2)@
@(bind e @(progn '(,(intern op) ,e1 ,e2)))@
@(or)@
@(additive e)@
@(end)@
@(end)
@(define additive (e))@
@(local e1 op e2)@
@(cases)@
@(num e1)@{op /[+-]/}@(expr e2)@
@(bind e @(progn '(,(intern op) ,e1 ,e2)))@
@(or)@
@(num e)@
@(end)@
@(end)
@(define decl (d))@
@(local type id expr)@
@(type type)@(id id)@
@(maybe)=@(expr expr)@(or)@(bind expr nil)@(end)@
@(bind d @(progn '(decl ,id ,type ,expr)))@
@(end)
@(define decls (dl))@
@(coll :gap 0)@(decl dl)@(end)@
@(end)
@(freeform)
@(decls dl)