PLY认为数学表达式是我实现变量后的语法错误

时间:2014-09-10 16:27:14

标签: python yacc lex ply

我一直在玩PLY,在得到示例后我决定实现变量。这工作得很好但是现在任何未分配给变量的数学表达式似乎都会引发语法错误(不是python语法错误,而是我语言中的语法错误)。例如:

calc> a = 5
Name: a
Number: 5
Assigning variable a to 5
{'a': 5}
calc> 6 + 1
Number: 6
SYNTACTIC ERROR: line: 1 position: 0 Syntax error: 6
Number: 1
{'a': 5}

我注意到,我将数字语法功能置于变量赋值之上,然后变量中断并进行计算。

词法:

#!/usr/bin/env python

### LEXICAL ANALYSIS ###


import ply.lex as lex

import colorama
colorama.init()

tokens = (
    "NUMBER",
    "PLUS",
    "MINUS",
    "MULTIPLY",
    "DIVIDE",
    "LBRACKET",
    "RBRACKET",
    "NAME",
    "EQUALS"
)

t_PLUS = r"\+"
t_MINUS = r"-"
t_MULTIPLY = r"\*"
t_DIVIDE = r"/"
t_LBRACKET = r"\("
t_RBRACKET = r"\)"
t_EQUALS = r"="

t_ignore = "\t\r "

def t_NUMBER(t):
    r"\d+"
    print "Number:", t.value
    t.value = int(t.value)
    return t

def t_NAME(t):
    r"[a-zA-Z]+\w*"
    print "Name:", t.value
    return t

def t_newline(t):
    r"\n+"
    t.lexer.lineno += len(t.value)

def t_COMMENT(t):
    r"\#.*"
    print "Comment:", t.value

def t_error(t):
    print colorama.Fore.RED + "LEXICAL ERROR: line:", t.lexer.lineno, "position:", t.lexer.lexpos, "Invalid token:", t.value, colorama.Fore.RESET
    t.lexer.skip(len(t.value))

lexer = lex.lex()

分析器:

import ply.yacc as yacc

from langlex import tokens

import colorama
colorama.init()

variables = {}

def p_assignment(p):
    "assignment : NAME EQUALS expression"
    print "Assigning variable", p[1], "to", p[3]
    variables[p[1]] = p[3]

def p_expression_plus(p):
    "expression : expression PLUS term"
    p[0] = p[1] + p[3]

def p_expression_minus(p):
    "expression : expression MINUS term"
    p[0] = p[1] - p[3]

def p_expression_term(p):
    "expression : term"
    p[0] = p[1]

def p_expression_name(p):
    "expression : NAME"
    p[0] = variables[p[1]]

def p_term_times(p):
    "term : term MULTIPLY factor"
    p[0] = p[1] * p[3]

def p_term_div(p):
    "term : term DIVIDE factor"
    p[0] = p[1] / p[3]

def p_term_factor(p):
    "term : factor"
    p[0] = p[1]

def p_factor_expr(p):
    "factor : LBRACKET expression RBRACKET"
    p[0] = p[2]

def p_factor_num(p):
    "factor : NUMBER"
    p[0] = p[1]

def p_error(p):
    if(p):
        print colorama.Fore.RED + "SYNTACTIC ERROR: line:", p.lexer.lineno, "position:", p.lexpos, "Syntax error:", p.value, colorama.Fore.RESET
    else:
        print colorama.Fore.RED + "SYNTACTIC ERROR: Unknown syntax error" + colorama.Fore.RESET

parser = yacc.yacc()

while True:
    s = raw_input("calc> ")

    if not(s):
        continue
    result = parser.parse(s)

    if(result):
        print result

    print variables

1 个答案:

答案 0 :(得分:2)

在创建p_assignment时,您创建了一个新的起始语法符号。来自文档:

  

yacc规范中定义的第一个规则确定起始语法符号。每当解析器减少启动规则并且没有更多可用输入时,解析就会停止并返回最终值。

     

http://www.dabeaz.com/ply/ply.html#ply_nn24

对于你的语法,这意味着唯一允许的输入句子是赋值:

$ python p_o.py 
Generating LALR tables
calc> a=1
Name: a
Number: 1
Assigning variable a to 1
{'a': 1}
calc> a
Name: a
SYNTACTIC ERROR: Unknown syntax error
{'a': 1}
calc> 1
Number: 1
SYNTACTIC ERROR: line: 1 position: 0 Syntax error: 1 
{'a': 1}

因此,我们需要一个起始语法符号,通过某种路径解析为表达式。我选择添加statement非终端作为起始语法符号:

...
def p_statement_assignment(p):
    "statement : assignment"
    pass

def p_statement_expression(p):
    "statement : expression"
    p[0] = p[1]

def p_assignment(p):
    "assignment : NAME EQUALS expression"
    print "Assigning variable", p[1], "to", p[3]
    variables[p[1]] = p[3]
...
$ python p_1.py 
Generating LALR tables
calc> a=1
Name: a
Number: 1
Assigning variable a to 1
{'a': 1}
calc> a
Name: a
1
{'a': 1}
calc> 1
Number: 1
1
{'a': 1}
calc>