用python中的ply写一个小的解释器

时间:2024-02-18 21:31:32
# -*- coding: utf-8 -*-
import ast
import six
import logging

from ply.yacc import yacc
from ply.lex import lex

logger = logging.getLogger()

reserved = {
    \'and\': \'AND\',
    \'or\': \'OR\',
    \'None\': \'NONE\',
    \'>\': \'GT\',
    \'<\': \'LT\',
    \'>=\': \'GE\',
    \'<=\': \'LE\',
    \'!=\': \'NE\',
    \'in\': \'IN\',
    \'not\': \'NOT\',
    \'==\': \'IE\'
}

# Token list
tokens = [\'NUMBER\', \'LIST\', \'FLOAT\', \'STR\',\'PLUS\', \'MINUS\', \'TIMES\', \'DIVIDE\',\'LPAREN\', \'RPAREN\', \'ID\'] + list(reserved.values())

def MyLexer():
     # Regular expression rules for simple tokens
    t_PLUS = r\'\+\'
    t_MINUS = r\'\-\'
    t_TIMES = r\'\*\'
    t_DIVIDE = r\'/\'
    t_LPAREN = r\'\(\'
    t_RPAREN = r\'\)\'
    t_GE = r\'\>\=\'
    t_LE = r\'\<\=\'
    t_GT = r\'\>\'
    t_IE = r\'\=\=\'
    t_LT = r\'\<\'
    t_STR = r"""\".*?\"|\\'.*?\\'"""
    t_NE = r\'\!\=\'

    def t_LIST(t):
        r\'\[.*\]\'
        t.value = ast.literal_eval(t.value)
        return t

    def t_FLOAT(t):
        r\'\d+\.+\d+\'
        t.value = float(t.value)
        return t

    # A regular expression rule with some action code
    def t_NUMBER(t):
        r\'\d+\'
        t.value = int(t.value)
        return t

    # 单独为保留字写的匹配规则
    def t_ID(t):
        r\'[a-zA-Z_][a-zA-Z]*\'
        t.type = reserved.get(t.value, \'ID\')    # Check for reserved words
        return t


    # Define a rule so we can track line numbers
    def t_newline(t):
        r\'\n+\'
        t.lexer.lineno += len(t.value)

     # A string containing ignored characters (spaces and tabs)
    t_ignore = \' \t\'

     # Error handling rule
    def t_error(t):
        logger.error("Illegal character \'%s\'" % t.value[0])
        t.lexer.skip(1)

    # Build the lexer from my environment and return it
    return lex()

precedence = (
    (\'left\', \'AND\', \'OR\'),
    (\'left\', \'PLUS\', \'MINUS\'),
    (\'left\', \'TIMES\', \'DIVIDE\'),
    (\'right\', \'UMINUS\'),  # - 作为单目运算符, 这里的优先级是虚的
    (\'left\', \'LPAREN\', \'RPAREN\')
)

# 越基本越往下写
def p_expr(p):
    \'\'\'
        expr : expr PLUS expr
             | expr MINUS expr
             | expr TIMES expr
             | expr DIVIDE expr
    \'\'\'
    if p[2] == \'+\':
        p[0] = p[1] + p[3]
    elif p[2] == \'-\':
        p[0] = p[1] - p[3]
    elif p[2] == \'*\':
        p[0] = p[1] * p[3]
    elif p[2] == \'/\':
        p[0] = p[1] / p[3]


def p_compare(p):
    \'\'\'
        expr : expr GT expr
             | expr GE expr
             | expr IE expr
             | expr LE expr
             | expr LT expr
             | expr NE expr
    \'\'\'
    if p[2] == \'>\':
        p[0] = p[1] > p[3]
    elif p[2] == \'>=\':
        p[0] = p[1] >= p[3]
    elif p[2] == \'==\':
        p[0] = p[1] == p[3]
    elif p[2] == \'<=\':
        p[0] = p[1] <= p[3]
    elif p[2] == \'<\':
        p[0] = p[1] < p[3]
    elif p[2] == "!=":
        p[0] = p[1] != p[3]

def p_logic(p):
    \'\'\'
        expr : expr OR expr
             | expr AND expr
             | NOT expr
             | expr IN expr
    \'\'\'
    if p[2] == "or":
        p[0] = p[1] or p[3]
    elif p[2] == "and":
        p[0] = p[1] and p[3]
    elif p[1] == \'not\':
        p[0] = not p[2]
    elif p[2] == \'in\':
        p[0] = p[1] in p[3]
    print(list(p))

def p_expr_list(p):
    \'\'\'
        expr : list
    \'\'\'
    p[0] = p[1]


def p_list(p):
    \'\'\'
        list : LIST
    \'\'\'
    p[0] = p[1]

def p_expr_num(p):
    \'\'\'
        expr : num
    \'\'\'
    p[0] = p[1]


def p_num(p):
    \'\'\'
        num : NUMBER
    \'\'\'
    p[0] = p[1]

def p_expr_float(p):
    \'\'\'
        expr : float
    \'\'\'
    p[0] = p[1]


def p_float(p):
    \'\'\'
        float : FLOAT
    \'\'\'
    p[0] = p[1]


def p_expr_str(p):
    \'\'\'
        expr : str
    \'\'\'
    p[0] = p[1]


def p_str(p):
    \'\'\'
        str : STR
    \'\'\'
    p[0] = ast.literal_eval(p[1])

def p_expr_none(p):
    \'\'\'
        expr : none
    \'\'\'
    p[0] = p[1]

def p_none(p):
    \'\'\'
        none : NONE
    \'\'\'
    p[0] = ast.literal_eval(p[1])

def p_group(p):
    \'\'\'
        expr : LPAREN expr RPAREN
    \'\'\'
    p[0] = p[2]

def p_expr_uminus(p):
    \'\'\'
        expr : MINUS expr %prec UMINUS
    \'\'\'
    p[0] = -p[2]

def p_error(p):
    logger.error(\'Syntax error:{}\'.format(p))

lexer = MyLexer()
parser = yacc()

if __name__ == "__main__":
    while True:
        if six.PY3:
            a_string = input("输入:")
        else:
            a_string = raw_input("输入:")
        print(a_string)
        lexer.input(a_string)
        for i,l in enumerate(lexer):
            print(str(i+1) + "=>", l)
        print(parser.parse(a_string))