用Tkinter打造GUI开发工具(21)改进ast模块的分析代码和Python程序标色思路

用Tkinter打造GUI开发工具(21)改进ast模块的分析代码和Python程序标色思路
在上一篇文章,我们尝试用ast模块开发分析Python3程序的代码,在使用中有些小问题.
网友(Likianta-上海)给出了新的代码,我按照他的代码,进行了改进,新程序“py010b.py”基本能满足分析的要求.
我们对同一个Python程序“py010a.py”进行了测试,对比结果如下:
py010a.py”输出结果如下:

库 {}
类 {'ParseTree': [12]}
函数 {'generic_visit': [14], 'visit_ClassDef': [17], 'visit_ImportFrom': [21], 'visit_FunctionDef': [28], 'visit_Assign': [32], 'MyParse': [39], 'readtext': [54]}
变量 {'lib_dict': [7], 'var_dict': [8], 'func_dict': [9], 'class_dict': [10], 'py': [61], 'py_root': [63]}

py010b.py”输出结果如下:

库 {'ast': [1]}
类 {'ParseTree': [12]}
函数 {'MyParse': [39], 'readtext': [54], 'generic_visit': [14], 'visit_ClassDef': [17], 'visit_ImportFrom': [21], 'visit_FunctionDef': [28], 'visit_Assign': [32]}
变量 {'lib_dict': [7, 44, 23], 'var_dict': [8, 45, 35], 'func_dict': [9, 46, 29], 'class_dict': [10, 47, 18], 'py': [61], 'py_root': [63, 48], 'pt': [49], 'ss': [55, 58], 's': [57]}

很明显,“py010b.py”程序优于“py010a.py”程序。其他不多说了,直接给出“py010b.py”全部程序代码。

from _ast import *
from ast import parse as ast_parse, walk as ast_walk

global lib_dict   #库字典
global var_dict   #变量字典
global func_dict  #函数字典
global class_dict  #类字典
lib_dict={}
var_dict={}
func_dict={}
class_dict={}

class AstHelper:
    
    def main(self, file):
        out = {}
        
        with open(file, mode='r', encoding='utf-8-sig') as f:
            text = f.read()
        root = ast_parse(text)
        
        for node in ast_walk(root):
            if not hasattr(node, 'lineno'):
                continue
            x = out.setdefault(node.lineno, [])
            x.append((str(type(node)), self.eval_node(node)))
        
        return out
    
    def main2(self, text):
        out = {}
        root = ast_parse(text)
        
        for node in ast_walk(root):
            if not hasattr(node, 'lineno'):
                continue
            x = out.setdefault(node.lineno, [])
            x.append((str(type(node)), self.eval_node(node)))
        
        return out
    
    def eval_node(self, node):
        result = None
        
        while result is None:
            # ------------------------------------------------ output result
            if isinstance(node, Attribute):
                result = node.attr
            elif isinstance(node, ClassDef):
                result = node.name
            elif isinstance(node, FunctionDef):
                result = node.name
            elif isinstance(node, Name):
                result = node.id
            elif isinstance(node, Str):
                result = node.s
            # ------------------------------------------------ compound obj
            elif isinstance(node, Import):
                result = {}
                for imp in node.names:
                    if imp.asname is None:
                        result[imp.name] = imp.name
                    else:
                        result[imp.name] = imp.asname
            elif isinstance(node, ImportFrom):
                result = {}
                module = node.module
                for imp in node.names:
                    if imp.asname is None:
                        result[module + '.' + imp.name] = \
                            module + '.' + imp.name
                    else:
                        result[module + '.' + imp.name] = \
                            module + '.' + imp.asname
            elif isinstance(node, Assign):
                result = {}
                a, b = node.targets, node.value
                v = self.eval_node(b)
                for i in a:
                    k = self.eval_node(i)
                    result[k] = v
            # ------------------------------------------------ take reloop
            elif isinstance(node, Call):
                node = node.func
            elif isinstance(node, Expr):
                node = node.value
            elif isinstance(node, Subscript):
                node = node.value
            else:
                result = str(node._fields)
        
        return result


def MyParse(ss):
    global lib_dict   #库字典
    global var_dict   #变量字典
    global func_dict  #函数字典
    global class_dict  #类字典
    lib_dict={}
    var_dict={}
    func_dict={}
    class_dict={}
    
    helper = AstHelper()
    res = helper.main2(ss)
    

    dict_filter = {
        "<class '_ast.Import'>"     : lib_dict,
        "<class '_ast.ImportFrom'>" : lib_dict,
        "<class '_ast.Assign'>"     : var_dict,
        "<class '_ast.FunctionDef'>": func_dict,
        "<class '_ast.ClassDef'>"   : class_dict,
    }
    
    for lineno, data in res.items():
        
        for i in data:
            type_, value = i
            if type_ in dict_filter:
                d = dict_filter.get(type_)
                if isinstance(value, str):
                    node = d.setdefault(value, [])
                    node.append(lineno)
                else:
                    for k in value.keys():
                        node = d.setdefault(k, [])
                        node.append(lineno)


def readtext(filename):   #读入文本文件
    ss=''
    with open(filename,encoding='utf-8') as f:
        s=f.read()
        ss=ss+s
    return ss

py=readtext('py010a.py')   #读取py文件     

py_root=MyParse(py)
print('库',lib_dict)
print('类',class_dict)
print('函数',func_dict)
print('变量',var_dict)

尽管ast模块很不错了,它解决了库、类、函数、变量树功能。通过双击名称,可快速定位于所在的程序行。
我下来要完成的工作是使Python代码彩色化,即不同代码元素,标注不同的颜色。这个程序我已经编写完成了,将来随我的《用Tkinter打造GUI开发工具》书中提供全部代码。
下面给朋友介绍一些开发思路。
在这里插入图片描述
上图左边的函数树就是使用“py010b.py”模块。右边使用了Tkinter的Text控件设计了上下2个代码编辑运行模块。
尽管离我们目标类似VS的可视化设计还有点距离。但也不妨碍我们能组合一个Python教学玩具。见下图。
在这里插入图片描述
跑题了。下面继续讲代码彩色化思路。

首先, 我们要识别程序中的Python关键字。设计了iskeyword(s)函数,代码如下。

def iskeyword(s):
    #保留字400
    keywords = ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 
            'class', 'continue', 'def', 'del', 'elif', 'else', 
            'except', 'finally', 'for', 'from', 'global', 'if', 
            'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or',
            'print','input','self', 'pass', 'raise', 'return', 'try', 
            'while', 'with', 'yield','next']
    f=0
    if s in keywords :
        f=400+keywords.index(s)
    return f

其次,要识别Python程序中的分割符号。设计了isdelimiter(s)函数,代码如下。

def isdelimiter(s):
    #界符300
    delimiters = ['{','}','[',']','(',')',',',':',';']
    f=-1
    if s in delimiters  :
        f=300+delimiters.index(s)+1
    return f

最后要识别Python程序中的运算和操作符号。设计了isoperator(s)函数,代码如下。

def isoperator(s):
    #运算符200
    operator = ['+','-','*','/','%','++','--','+=','-=','+=','/=',#算术运算符
    			'==','!=','>','<','>=','<=', #关系运算符
    			'&','|','^','~','<<','>>','>>>',#位运算符
    			'&&','||','!',#逻辑运算符
    			'=','+=','-=','*=','/=','%=','<<=','>>=','&=','^=','|=',#赋值运算符
    			'?:']#条件运算符
    f=0
    if s in operator :
        f=200+operator.index(s)
    return f

重复分析程序,直到程序分析完成。
无法识别字符串均为用户创建的名称,例如模块名,类名,函数名,变量名等等。

根据识别出的字符串,进行颜色标注,就会实现程序代码的彩色化。

前面我曾演示了Tkinter鼠标移动控件的图片。
在这里插入图片描述
在这里插入图片描述
一直没有进行可视化设计的根本原因,我一直无法设计出可用滑块控制的超长窗口控件,上周末在家尝试,已经实现了这个技术,并且做出了示例,下一步就进行设计出新标准控件,这样就能修改窗口中新放置的控件属性了。
在这里插入图片描述
是不是离类似“VB”那样的“Visual Python”可视化开发又进了一步。

上面是正在开发的中文Python教学软件,中英文关键字都能够识别和运行。等完善后,给大家提供。
独狼荷蒲qq:2775205
小白量化群:524949939
微信公众号:独狼股票分析

发布了56 篇原创文章 · 获赞 67 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hepu8/article/details/98118080