从PLY yacc访问并打印AST

Access and print AST from ply yacc(从PLY yacc访问并打印AST)
本文介绍了从PLY yacc访问并打印AST的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用PLY编译一种语言(C-减法),以构建词法分析器和解析器。在PLY文档中很清楚,构建AST完全由用户决定(https://www.dabeaz.com/ply/ply.html#ply_nn2):

Yacc.py的输出通常是抽象语法树(AST)。但是,这完全由用户决定。
那么,在我的词法分析器和解析器正常工作的情况下,我如何构建一个AST阅读器(因为正如文档中所说,输出是一个AST,所以我只需要访问它)?我正在尝试使用下面的代码,但仅收到None作为响应。

AST代码:

def dumpast(node):
    _dumpast(node)
    print('pass 3')
    print('')

def _dumpast(node, level=0):

    if isinstance(node, tuple):
        indent(level)
        nchildren = len(node) - 1

        print("(%s" % node[0], end='')

        if nchildren > 0:
            print(" ", end='')

        for c in range(nchildren):
            _dumpast(node[c+1], level+1)
            if c != nchildren-1:
                print(' ', end='')

        print(")", end='')
    elif isinstance(node, list):
        indent(level)
        nchildren = len(node)

        print("[", end='')

        if nchildren > 0:
            print(" ", end='')

        for c in range(nchildren):
            _dumpast(node[c], level+1)
            if c != nchildren-1:
                print(' ', end='')
        print("]", end='')
    else:
        print("%s" % str(node), end='')

def indent(level):
    print('')
    for i in range(level):
        print('  |',end='')

main.py

import ply.yacc as yacc
import ply.lex as lex
from tabulate import tabulate
import sys
from lexer import *
from parser import Parser
from ast import dumpast

lexer = lex.lex()
print('pass 1')
parser = yacc.yacc(module=Parser)

with open(sys.argv[1], 'r') as f:
    dumpast(parser.parse(f.read(),lexer))
    print('pass 2')

响应:

通过%1
非第3页

通过2

我做错了什么吗?或者,有没有人可以分享AST建筑教程来访问PLY?

编辑:包括parser.py以显示解析的工作原理:

import ply.yacc as yacc
import lexer
from state import state

class Parser():
    tokens = lexer.tokens

    def p_program(p):
        'program : declaration_list'
        state.ast = p[1]
        print('passou 4')

    def p_declaration_list(p):
        '''declaration_list : declaration_list declaration
                            | declaration'''
        if len(p) == 3:
            p[0] = p[1]
            p[0].append(p[2])
        else:
            p[0] = p[1]

    def p_declaration(p):
        '''declaration : var_declaration 
                       | fun_declaration'''
        state.ast = p[1]

    def p_var_declaration(p):
        '''var_declaration : type_specifier ID 
                           | type_specifier ID LBRACKET NUM RBRACKET'''
        if len(p) == 3:
            p[0] = ('ASSIGN', ('ID', p[1]))
        else:
            p[0] = ('ASSIGN', ('ID', p[1]), ('NUM', p[4]))

    def p_type_specifier(p):
        '''type_specifier : INT 
                          | VOID'''
        state.ast = p[1]

    def p_fun_declaration(p):
        'fun_declaration : type_specifier ID LPAREN params RPAREN compound_stmt'
        p[0] = ('ASSIGN', ('ID', p[1]), p[4], p[6])

    def p_params(p):
        '''params : param_list 
                  | VOID'''
        state.ast = p[1]

    def p_param_list(p):
        '''param_list : param_list COLON param 
                      | param'''
        if len(p) == 4:
            p[0] = p[1]
            p[0].append(p[3])
        else:
            p[0] = p[1]

    def p_param(p):
        '''param : type_specifier ID 
                 | type_specifier ID LBRACKET RBRACKET'''
        p[0] = ('ASSIGN', ('ID', p[1]))
        
    def p_compound_stmt(p):
        'compound_stmt : LKEY local_declarations statement_list RKEY'
        p[0] = (p[2], p[3])

    def p_local_declarations(p):
        '''local_declarations : local_declarations var_declaration
                              | empty'''
        if len(p) > 1:
            p[0] = (p[1], p[2])
        else:
            pass

    def p_statement_list(p):
        '''statement_list : statement_list statement
                          | empty'''
        if len(p) > 1:
            p[0] = p[1]
            p[0].append(p[3])
        else:
            pass

    def p_statement(p):
        '''statement : expression_stmt
                     | compound_stmt
                     | selection_stmt
                     | iteration_stmt
                     | return_stmt'''
        state.ast = p[1]
                    
    def p_expression_stmt(p):
        '''expression_stmt : expression SEMICOLON
                           | SEMICOLON'''
        if len(p) == 3:
            p[0] = p[1]
        else:
            pass

    def p_selection_stmt(p):
        '''selection_stmt : IF LPAREN expression RPAREN statement 
                          | IF LPAREN expression RPAREN statement ELSE statement'''
        if len(p) == 6:
            p[0] = ('IF', p[3], p[5], ('NIL',))
        else:
            p[0] = ('IF', p[3], p[5], p[7])

    def p_iteration_stmt(p):
        'iteration_stmt : WHILE LPAREN expression RPAREN statement'
        p[0] = ('WHILE', p[3], p[5])

    def p_return_stmt(p):
        '''return_stmt : RETURN SEMICOLON
                       | RETURN expression SEMICOLON'''
        if len(p) == 3:
            p[0] = ('RETURN',)
        else:
            p[0] = ('RETURN', p[2])

    def p_expression(p):
        '''expression : var EQUAL expression 
                      | simple_expression'''
        if len(p) > 2:
            p[0] = ('ASSIGN', (p[1], p[3]))
        else:
            p[0] = [p[1]]

    def p_var(p):
        '''var : ID 
               | ID LBRACKET expression RBRACKET'''
        if len(p) == 2:
            p[0] = ('ID', p[1])
        else:
            p[0] = (('ID', p[1]), [p[3]])

    def p_simple_expression(p):
        '''simple_expression : additive_expression relop additive_expression
                             | additive_expression'''
        if len(p) == 4:
            p[0] = (p[1], ('RELOP', p[2]), p[3])
        else:
            p[0] = [p[1]]

    def p_relop(p):
        '''relop : LESSOREQUAL
                 | LESS
                 | GREAT
                 | GREATOREQUAL
                 | DOUBLEEQUAL
                 | NOTEQUAL'''
        p[0] = ('RELOP', p[1])

    def p_additive_expression(p):
        '''additive_expression : additive_expression addop term 
                               | term'''
        if len(p) == 4:
            p[0] = (p[1], p[2], p[3])
        else:
            p[0] = p[1]

    def p_addop(p):
        '''addop : PLUS
                 | MINUS'''
        p[0] = p[1]
                 
    def p_term(p):
        '''term : term mulop factor 
                | factor'''
        if len(p) == 4:
            p[0] = (p[1], p[2], p[3])
        else:
            p[0] = p[1]

    def p_mulop(p):
        '''mulop : MULT
                 | DIV'''
        p[0] = p[1]

    def p_factor(p):
        '''factor : LPAREN expression RPAREN
                  | var 
                  | call 
                  | NUM'''
        if len(p) == 4:
            p[0] = p[2]
        elif p == 'NUM':
            p[0] = ('ASSIGN', ('NUM', p[1]))
        else:
            p[0] = p[1]

    def p_call(p):
        'call : ID LPAREN args RPAREN'
        p[0] = (('ID', p[1]), p[3])

    def p_args(p):
        '''args : arg_list 
                | empty'''
        if p == '':
            pass
        else:
            state.ast = p[1]

    def p_args_list(p):
        '''arg_list : arg_list COLON expression 
                    | expression'''
        if len(p) == 4:
            p[0] = p[1]
            p[0].append(p[3])
        else:
            p[0] = p[1]

    def p_error(p):
        if not p:
            print("SYNTAX ERROR AT EOF")

    def p_empty(p):
        'empty :'
        pass

推荐答案

parser.parse()返回顶层还原操作放置到p[0]中的任何内容。您的顶级缩减操作不会将任何内容放入p[0]。所以parse函数返回None,这就是您传递给AST转储程序的内容。

不需要使用像state.ast这样的状态(或全局)变量来从PLY解析器返回值;这是一种您可能从不同解析器生成器的解决方案中复制的模式。我注意到,您还将state.ast赋值给state.ast,而不是在其他操作中设置p[0],即使在解析器生成器中也不可能是正确的,因为解析器生成器没有提供更方便的机制来返回顶级结果。这些也需要更正。

这篇关于从PLY yacc访问并打印AST的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Leetcode 234: Palindrome LinkedList(Leetcode 234:回文链接列表)
How do I read an Excel file directly from Dropbox#39;s API using pandas.read_excel()?(如何使用PANDAS.READ_EXCEL()直接从Dropbox的API读取Excel文件?)
subprocess.Popen tries to write to nonexistent pipe(子进程。打开尝试写入不存在的管道)
I want to realize Popen-code from Windows to Linux:(我想实现从Windows到Linux的POpen-code:)
Reading stdout from a subprocess in real time(实时读取子进程中的标准输出)
How to call type safely on a random file in Python?(如何在Python中安全地调用随机文件上的类型?)