我从语言的其他形式良好(且运作良好)的语法开始。变量,
二元运算符,函数调用,列表,循环,条件等。对于这个语法,我想添加我所谓的object
构造:
object
: object_name ARROW more_objects
;
more_objects
: object_name
| object_name ARROW more_objects
;
object_name
: IDENTIFIER
;
关键是能够访问嵌套在对象中的标量。例如:
car->color
monster->weapon->damage
pc->tower->motherboard->socket_type
我将object
添加为primary_expression
:
primary_expression
: id_lookup
| constant_value
| '(' expression ')'
| list_initialization
| function_call
| object
;
现在这是一个示例脚本:
const list = [ 1, 2, 3, 4 ];
for var x in list {
send "foo " + x + "!";
}
send "Done!";
在将非终结object
添加为primary_expression
之前,一切都是阳光和小狗。即使在我添加之后,Bison也不会抱怨。没有报告转移和/或减少冲突。并且生成的代码在没有声音的情况下编译。但是当我尝试运行上面的示例脚本时,我会被告知error on line 2: Attempting to use undefined symbol '{' on line 2.
如果我将脚本更改为:
var list = 0;
for var x in [ 1, 2, 3, 4 ] {
send "foo " + x + "!";
}
send "Done!";
然后我得到error on line 3: Attempting to use undefined symbol '+' on line 3.
显然语法中object
的存在正在弄乱解析器的行为[SOME],我觉得我忽略了一个相当简单的语言理论原则,可以用jiff修复它,但是事实上,没有任何转移/减少冲突让我感到困惑。
编写这些规则是否有更好的方式(语法上)?我错过了什么?为什么没有冲突?
(如果它有帮助,这里是full grammar file)
更新:为了澄清,这种编译成虚拟机运行代码的语言嵌入到另一个系统中 - 特别是游戏。它有标量和列表,没有复杂的数据类型。当我说我想在语言中添加object
时,这实际上是用词不当。我没有在用我的语言添加对用户定义类型的支持。
使用object
构造访问的对象实际上是游戏中的对象,我允许语言处理器通过将VM连接到游戏引擎的中间层进行访问。该层旨在尽可能地将语言定义和虚拟机机制与游戏引擎的实现和细节分离。
所以当我用我的语言写道:
player->name
只能由编译器编纂。 “播放器”和“名称”不是传统的identifier
,因为它们没有添加到符号表中,并且在编译时没有对它们进行任何操作,除非将对播放器名称的请求转换为3地址代码。
答案 0 :(得分:2)
在yacc源文件中使用直接字符串时,您似乎在做一个经典错误。由于您使用词法分析器,因此只能在yacc源文件中使用令牌名称。 More on this here
答案 1 :(得分:1)
所以我花了一段合理的时间来挑选语法(和野牛输出),看不出这里有什么明显的错误。如果没有办法执行它,我就无法通过实验轻松弄清楚究竟发生了什么。因此,以下是我在调试语法时经常遇到的一些具体步骤。希望你可以做任何你尚未完成的任何事情,然后可能会发布任何可能揭示的结果的后续(或编辑你的问题):
object
和{{组成的序列ARROW加入了1}}规则。这是否按预期工作?more_object
(使用一些其他地方没有出现的令牌)并查看是否可以包含这些令牌而不会破坏其他所有令牌。object
运行野牛。检查输出以尝试跟踪您添加的规则及其影响的状态。尝试删除这些规则并重复此过程 - 改变了什么?这是非常耗时的,并且是一个巨大的痛苦,但它是一个很好的最后的手段。我推荐一支铅笔和一些纸。查看错误输出的结构 - “+”被识别为标识符标记,因此被查找为符号。可能值得检查你的词法分析器,看看它是如何处理标识符令牌的。你可能只是偶然地抓住了太多。作为进一步的调试技术,您可以考虑将其中一些令牌文字(例如“+”,“{”等)转换为真实的令牌,以便bison的错误报告可以帮助您多做一点。
编辑:好吧,我挖的越多,我就越相信词法分析器不一定能正常运作。我会仔细检查你从yylex()得到的令牌流是否符合你的期望,然后继续进行。特别是,它看起来像你认为特殊的一堆符号(例如'+'和'{')被一些正则表达式捕获,或者至少被允许传递标识符。答案 2 :(得分:1)
我认为您的主要问题是您未能定义子树构造函数 在你的对象子语法中。 (编辑:OP说他留下了语义行为 他的示例文字中的对象。这不会改变以下答案。)
您可能还必须按所遇到的顺序查找对象。 也许你打算:
primary_expression
: constant_value { $$ = $1; }
| '(' expression ')' { $$ = $2; }
| list_initialization { $$ = $1; }
| function_call { $$ = $1; }
| object { $$ = $1; }
;
object
: IDENTIFIER { $$ = LookupVariableOrObject( yytext ); }
| object ARROW IDENTIFIER { $$ = LookupSubobject( $1, yytext ); }
;
我假设如果一个人遇到标识符X,则默认解释 是它是一个变量名称。但是,如果你遇到X - > Y,那么即使是X. 是一个变量名,你想要对象 X与子对象Y.
LookupVarOrObject做的是查找遇到的最左边的标识符,看它是否可变 (并返回与idlookup基本相同的值,它必须生成AST_VAR类型的AST节点), 或者查看它是否是有效的对象名,并返回标记为AST_OBJ的AST节点, 或者如果标识符不是其中之一就抱怨。
LookupSuboject的作用是检查其左操作数以确保它是AST_OBJ (或AST_VAR,其名称恰好与对象的名称相同)。 并抱怨,如果不是。如果是,则查找yytext-child对象 名为AST_OBJ。
编辑:基于另一个答案中的讨论评论,OP原文中的右递归 如果OP的语义检查检查全局词法分析器状态(yytext),语法可能会有问题。 这个解决方案是左递归的,不会与特定陷阱发生冲突。
答案 3 :(得分:1)
你没有得到转移/减少冲突,因为使用object_name
和more_objects
的规则是正确递归的 - 而不是Yacc(Bison)最自然处理的左递归规则。
在经典的Yacc上,你会发现你可以用尽“object->name->what->not
”符号的足够深度的堆栈空间。 Bison在运行时扩展了它的堆栈,所以你必须耗尽内存,这比当机器有几兆内存(或更少)内存要困难得多。
右递归的一个结果是,在读取链中的最后一个对象名称之前不会发生缩减(或者更准确地说,除此之外的一个符号)。我看到你也使用了statement_list
规则的右递归 - 以及其他许多地方。
答案 4 :(得分:0)
id_lookup :IDENTIFIER
与
正式相同OBJECT_NAME :IDENTIFIER
和object_name将接受id_lookup不会的所有内容,因此assertLookup(yytext);可能会运行在可能看起来像IDENTIFIER的所有内容上,并且不被单一规则接受只是为了在2之间做出决定然后object_name无法接受,因为单个前瞻禁止它。
对于twilight zone,你得到错误的两个字符不会被声明为令牌,并且会打开未完成行为的区域,并且当语法松散时可能会使解析器试图将它们视为潜在的标识符。
答案 5 :(得分:0)
我刚尝试使用bison 2.4.1在Ubuntu 10.04中运行muscl,我能够运行两个没有语法错误的示例。我的猜测是你的野牛版本中有一个错误。如果我以某种方式运行您的解析器错误,请告诉我。以下是您提供的第一个示例的输出。
./muscle < ./test1.m (this was your first test)
\-statement list
|-declaration (constant)
| |-symbol reference
| | \-list (constant)
| \-list
| |-value
| | \-1
| |-value
| | \-2
| |-value
| | \-3
| \-value
| \-4
|-loop (for-in)
| |-symbol reference
| | \-x (variable)
| |-symbol reference
| | \-list (constant)
| \-statement list
| \-send statement
| \-binary op (addition)
| |-binary op (addition)
| | |-value
| | | \-foo
| | \-symbol reference
| | \-x (variable)
| \-value
| \-!
\-send statement
\-value
\-Done!
+-----+----------+-----------------------+-----------------------+
| 1 | VALUE | 1 | |
| 2 | ELMT | @1 | |
| 3 | VALUE | 2 | |
| 4 | ELMT | @3 | |
| 5 | VALUE | 3 | |
| 6 | ELMT | @5 | |
| 7 | VALUE | 4 | |
| 8 | ELMT | @7 | |
| 9 | LIST | | |
| 10 | CONST | @10 | @9 |
| 11 | ITER_NEW | @11 | @10 |
| 12 | BRA | @14 | |
| 13 | ITER_INC | @11 | |
| 14 | ITER_END | @11 | |
| 15 | BRT | @22 | |
| 16 | VALUE | foo | |
| 17 | ADD | @16 | @11 |
| 18 | VALUE | ! | |
| 19 | ADD | @17 | @18 |
| 20 | SEND | @19 | |
| 21 | BRA | @13 | |
| 22 | VALUE | Done! | |
| 23 | SEND | @22 | |
| 24 | HALT | | |
+-----+----------+-----------------------+-----------------------+
foo 1!
foo 2!
foo 3!
foo 4!
Done!