这是修复,但并不完全令人满意:
%{
%}
%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 项目之间的冲突:dot-final:
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)
如果没有更强大的解析方法或黑客,就无法解决这个问题。
如果你移动%nonassoc
before %left '<'
,给它低优先级怎么办?那么这种转变将受到青睐。不幸的是,这会导致您无法<int>
在声明之后编写另一个声明。
int foo = 3 <int> bar = 4
^ // error: the machine shifted and is now doing: expr '<' . expr.
所以这是解决冲突的错误方法;您希望能够编写多个这样的声明。
另一个注意事项:
我的 TXR 语言实现了类似于 Parse Expression Grammars 的功能,可以很好地处理这种语法。这本质上是 LL(无限),它胜过 LALR(1)。
我们甚至不需要单独的词法分析器和解析器!这只是一个符号前瞻的限制以及对 1970 年硬件的最大效率的需要所必需的。
来自 shell 命令行的示例输出,演示了通过转换为类似于 Lisp 的抽象语法树进行的解析,该语法树绑定到变量dl
(声明列表)。所以这与语义动作一起完成,产生可以在 TXR Lisp 中进一步处理的输出。标识符通过调用转换为 Lisp 符号intern
,数字也被转换为数字对象。
$ 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)