yacc使用的分析技术只允许在规则规约后执行动作。假设有如下规则:
~~~
def p_foo(p):
"foo : A B C D"
print "Parsed a foo", p[1],p[2],p[3],p[4]
~~~
方法只会在符号A,B,C和D都完成后才能执行。可是有的时候,在中间阶段执行一小段代码是有用的。假如,你想在A完成后立即执行一些动作,像下面这样用空规则:
~~~
def p_foo(p):
"foo : A seen_A B C D"
print "Parsed a foo", p[1],p[3],p[4],p[5]
print "seen_A returned", p[2]
def p_seen_A(p):
"seen_A :"
print "Saw an A = ", p[-1] # Access grammar symbol to left
p[0] = some_value # Assign value to seen_A
~~~
在这个例子中,空规则seen_A将在A移进分析栈后立即执行。p[-1]指代的是在分析栈上紧跟在seen_A左侧的符号。在这个例子中,是A符号。像其他普通的规则一样,在嵌入式行为中也可以通过为p[0]赋值来返回某些值。
使用嵌入式动作可能会导致移进归约冲突,比如,下面的语法是没有冲突的:
~~~
def p_foo(p):
"""foo : abcd
| abcx"""
def p_abcd(p):
"abcd : A B C D"
def p_abcx(p):
"abcx : A B C X"
~~~
可是,如果像这样插入一个嵌入式动作:
~~~
def p_foo(p):
"""foo : abcd
| abcx"""
def p_abcd(p):
"abcd : A B C D"
def p_abcx(p):
"abcx : A B seen_AB C X"
def p_seen_AB(p):
"seen_AB :"
~~~
会产生移进归约冲,只是由于对于两个规则abcd和abcx中的C,分析器既可以根据abcd规则移进,也可以根据abcx规则先将空的seen_AB归约。
嵌入动作的一般用于分析以外的控制,比如为本地变量定义作用于。对于C语言:
~~~
def p_statements_block(p):
"statements: LBRACE new_scope statements RBRACE"""
# Action code
...
pop_scope() # Return to previous scope
def p_new_scope(p):
"new_scope :"
# Create a new scope for local variables
s = new_scope()
push_scope(s)
...
~~~
在这个例子中,new_scope作为嵌入式行为,在左大括号{之后立即执行。可以是调正内部符号表或者其他方面。statements_block一完成,代码可能会撤销在嵌入动作时的操作(比如,pop_scope())
- 0 一些翻译约定
- 1 前言和预备
- 2 介绍
- 3 PLY概要
- 4 Lex
- 4.1 Lex的例子
- 4.2 标记列表
- 4.3 标记的规则
- 4.4 标记的值
- 4.5 丢弃标记
- 4.6 行号和位置信息
- 4.7 忽略字符
- 4.8 字面字符
- 4.9 错误处理
- 4.10 构建和使用lexer
- 4.11 @TOKEN装饰器
- 4.12 优化模式
- 4.13 调试
- 4.14 其他方式定义词法规则
- 4.15 额外状态维护
- 4.16 Lexer克隆
- 4.17 Lexer的内部状态
- 4.18 基于条件的扫描和启动条件
- 4.19 其他问题
- 5 语法分析基础
- 6 Yacc
- 6.1 一个例子
- 6.2 将语法规则合并
- 6.3 字面字符
- 6.4 空产生式
- 6.5 改变起始符号
- 6.6 处理二义文法
- 6.7 parser.out调试文件
- 6.8 处理语法错误
- 6.9 行号和位置的跟踪
- 6.10 构造抽象语法树
- 6.11 嵌入式动作
- 6.12 Yacc的其他
- 7 多个语法和词法分析器
- 8 使用Python的优化模式
- 9 高级调试
- 9.1 调试lex()和yacc()命令
- 9.2 运行时调试
- 10 如何继续