You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python PLY开发转译器:实现多行代码解析的方法及代码示例

如何用PLY实现多行代码的解析与运行?

问题描述

我正在使用Python的PLY库编写一个转译器,但目前仅能读取并转换单行代码。以下是我的Yacc相关实现代码:

lexer = lex.lex()
precedence = (
    ('left', 'ADD', 'SUB'),
    ('left', 'MULTI', 'DIV'),
    ('left', 'RPAREN', 'LPAREN')
)
def p_expression_term(p):
    ''' expression : term ENDOFLINE | var_assign ENDOFLINE | empty '''
    p[0] = p[1]
def p_expression_empty(p):
    'empty : '
    pass
def p_expression_add(p):
    'term : term ADD term'
def p_expression_sub(p):
    'term : term SUB term'
def p_term_multi(p):
    '''term : term MULTI term '''
def p_term_div(p):
    'term : term DIV term'
def p_term_pow(p):
    'term : term POW term'
    p[0] = pow(p[1], p[3])
def p_term_factor(p):
    'term : factor'
    p[0] = p[1]
def p_factor_num(p):
    'factor : NUMBER'
    p[0] = ['number', p[1]]
def p_expressionParenth(p):
    '''term : LPAREN term RPAREN'''
    p[0] = p[2]
    pass
def p_var_assign1(p):
    '''var_assign : NAME EQUAL NAME | NAME EQUAL term '''
    p[0] = ast_tree.ast_name1(p[1], p[3])
def p_var_assignBe(p):
    '''var_assign : LET NAME BE term'''
def p_var_assign3(p):
    '''var_assign : NAME IDENTIFIER EQUAL term'''
# Error rule for syntax errors
def p_error(p):
    print("Syntax error in input! at " + str(p))
# Build the parser
parser = yacc.yacc()
while True:
    try:
        s = '''let x be 12;'''
    except EOFError:
        break
    if not s:
        continue
    result = parser.parse(s)
    result = ast_tree.fill_locations(result)
    print(ast.unparse(result))
    exit()

我当前在p_expression_term方法中进行代码解析,此前尝试过GitHub上的一些解决方案但均未生效。请问如何实现对输入中多行代码的运行与解析?能否提供对应的代码片段?


解决方案

你的核心问题在于顶层语法规则只支持单个带结束符的表达式,无法处理多个语句的序列。我们需要修改Yacc的语法结构,让它能识别由多个语句组成的“程序”,同时调整解析逻辑来处理多行输入。

以下是修改后的关键代码片段和解释:

1. 重构顶层语法规则

首先定义一个program规则,用来表示由多个语句组成的完整程序,再定义statement规则对应单个语句(支持换行或分号作为结束符):

# 顶层规则:程序由多个语句组成
def p_program(p):
    '''program : statement
               | program statement'''
    # 收集所有语句到列表中
    if len(p) == 2:
        p[0] = [p[1]] if p[1] is not None else []
    else:
        if p[2] is not None:
            p[0] = p[1] + [p[2]]
        else:
            p[0] = p[1]

# 单个语句:表达式 + 结束符(换行或分号),支持空行
def p_statement(p):
    '''statement : expression ENDOFLINE
                 | expression SEMICOLON
                 | empty ENDOFLINE
                 | empty SEMICOLON'''
    p[0] = p[1]  # 只保留表达式部分,丢弃结束符

# 原表达式规则去掉结束符,专注于表达式本身
def p_expression(p):
    ''' expression : term | var_assign | empty '''
    p[0] = p[1]

2. 修复表达式规则的实现细节

注意你之前的加减乘除规则没有设置p[0],这会导致AST无法正确生成,需要补充:

def p_expression_add(p):
    'term : term ADD term'
    # 这里替换为你的AST构建逻辑,示例用简单结构
    p[0] = ['add', p[1], p[3]]

def p_expression_sub(p):
    'term : term SUB term'
    p[0] = ['sub', p[1], p[3]]

def p_term_multi(p):
    '''term : term MULTI term '''
    p[0] = ['multi', p[1], p[3]]

def p_term_div(p):
    'term : term DIV term'
    p[0] = ['div', p[1], p[3]]

# 补全var_assignBe规则的p[0]设置
def p_var_assignBe(p):
    '''var_assign : LET NAME BE term'''
    p[0] = ast_tree.ast_name1(p[2], p[4])  # 假设和var_assign1逻辑一致

3. 修改解析循环以支持多行输入

原来的测试代码固定了单行输入,现在改成读取多行内容(比如从标准输入读取),并遍历解析后的语句列表处理:

import sys

# Build the parser
parser = yacc.yacc()

try:
    # 读取多行输入(比如用户输入多行后按Ctrl+D结束)
    s = sys.stdin.read()
except EOFError:
    s = ''

if s:
    result = parser.parse(s)
    # 遍历每个语句并处理
    for stmt in result:
        if stmt is not None:
            stmt = ast_tree.fill_locations(stmt)
            print(ast.unparse(stmt))

关键修改说明

  • 顶层规则重构:通过program规则允许多个statement的序列,这是支持多行的核心。
  • 语句结束符分离:把ENDOFLINE/SEMICOLON从表达式规则移到statement规则,让表达式本身不依赖结束符,更符合语法逻辑。
  • 空行处理:支持空行(通过empty ENDOFLINE规则),避免空行触发语法错误。
  • 补全AST生成:确保所有语法规则都设置了p[0],否则解析结果会丢失节点。

内容的提问来源于stack exchange,提问作者beton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:58:15