牛骨文教育服务平台(让学习变的简单)

6.11 嵌入式动作

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())