python环境解析任意编程语言 tree-sitter使用方法(1)

这篇具有很好参考价值的文章主要介绍了python环境解析任意编程语言 tree-sitter使用方法(1)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

我个人目前仍在研究代码有关的知识。目前基于深度学习表征代码的论文越来越卷了,用到的工具越来越高级了。目前有一个开源项目tree-sitter,专门用于解析具体语法树,声称:

  1. 足够通用,能用于任何编程语言
  2. 足够迅速,能在文本编辑器中响应每一个用户输入
  3. 足够鲁棒,即便语法错误也能解析语法树
  4. 无依赖性,能很好地嵌入于程序中

在官方提供的playground玩了玩,的确1、2、3点都很符合。
所以个人做 (水)了本篇文章。

安装

py-tree-sitter已经做了详细的描述,所以这里简短描述,顺便说个遇到的问题。

  1. 找个合适的python环境,install
pip3 install tree_sitter
  1. 对于要解析的编程语言,随便创建文件夹(比如vendor),该目录git clone指定语言的仓库,在tree-sitter官网这里找,比如我对Java、Python、C++、C#、JS感兴趣:
git clone https://github.com/tree-sitter/tree-sitter-java
git clone https://github.com/tree-sitter/tree-sitter-python
git clone https://github.com/tree-sitter/tree-sitter-cpp
git clone https://github.com/tree-sitter/tree-sitter-c-sharp
git clone https://github.com/tree-sitter/tree-sitter-javascript

需要注意的是,C++对应cpp,C#对应c-sharp,后面使用的时候需要认清楚官方定义的名称。

  1. 创建build文件夹,用于保存xxx.so文件,该文件相当于自定义的编译器,用于解析代码生成语法树。然后复制以下代码运行。
from tree_sitter import Language

Language.build_library(
  # so文件保存位置
  'build/my-languages.so',

  # vendor文件下git clone的仓库
  [
    'vendor/tree-sitter-java',
    'vendor/tree-sitter-python',
    'vendor/tree-sitter-cpp',
    'vendor/tree-sitter-c-sharp',
    'vendor/tree-sitter-javascript',
  ]
)

这里有一个小插曲,个人用windows电脑,一开始运行这段代码直接报错,好像说缺少什么msvc文件,所以我还下载了visual studio才解决。现在看到tree-sitter__init__.py文件下,有一条compiler = new_compiler()代码,发现以下代码:

if compiler is None:
	# get_default_compiler 用于选择_default_compilers:
	'''
	_default_compilers = (
	    ('cygwin.*', 'unix'),
	    ('posix', 'unix'),
	    ('nt', 'msvc'),
    )
    '''
    compiler = get_default_compiler(plat) # windows是nt

'''
compiler_class = { 'unix':    ('unixccompiler', 'UnixCCompiler',
                               "standard UNIX-style compiler"),
                   'msvc':    ('_msvccompiler', 'MSVCCompiler',
                               "Microsoft Visual C++"),
                   'cygwin':  ('cygwinccompiler', 'CygwinCCompiler',
                               "Cygwin port of GNU C Compiler for Win32"),
                   'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',
                               "Mingw32 port of GNU C Compiler for Win32"),
                   'bcpp':    ('bcppcompiler', 'BCPPCompiler',
                               "Borland C++ Compiler"),
                 }
'''
(module_name, class_name, long_description) = compiler_class[compiler]

看了代码后就清楚了,我之前电脑缺少Microsoft Visual C++,安装visual studio,配置C++后就好了。

  1. 解析
from tree_sitter import Language, Parser

# 注意C++对应cpp,C#对应c_sharp(!这里短横线变成了下划线)
# 看仓库名称
CPP_LANGUAGE = Language('build/my-languages.so', 'cpp')
CS_LANGUAGE = Language('build/my-languages.so', 'c_sharp')

# 举一个CPP例子
cpp_parser = Parser()
cpp_parser.set_language(CPP_LANGUAGE)

# 这是b站网友写的代码,解析看看
cpp_code_snippet = '''
int mian{
  piantf("hell world");
  remake O;
}
'''

# 没报错就是成功
tree = cpp_parser.parse(bytes(cpp_code_snippet, "utf8"))
# 注意,root_node 才是可遍历的树节点
root_node = tree.root_node

最近,个人还发现了版本问题。tree-sitter 0.19.0版本运行 parser.set_language()出现:

ValueError: Incompatible Language version 14. Must be between 13 and 13

这有可能tree-sitter版本太旧所致,重装即可解决。

语法树属性(一部分)

通过debugger,可以查看语法树节点的属性(指root_node下的节点),可以发现:

# 孩子节点【节点数、节点列表】
root_node.child_count: int
root_node.children: list[Node]| None

# 该语法树节点对应代码字符串位置【左闭右开】
root_node.start_byte: int
root_node.end_byte: int

# 语法树节点对应代码 (行, 列) 位置元组
root_node.start_point: tuple[int, int]
root_node.end_point: tuple[int, int]

'''
以上的行、列以及字符串位置都是以0开始
'''

# 语法树命名节点、命名类型 以及 语法树对应的文本
# 因为具体语法树有代码所有的标记,所以一些符号可能没有类型
# 我猜测该属性可以用于区别具体语法树符号节点,构建抽象语法树
root_node.is_named: bool
root_node.type: str # 没有类型时,这里显示代码原始标记
root_node.text: bytes

# 语法树父节点
root_node.parent: Node| None

# 语法树左兄弟、左命名兄弟
root_node.prev_sibling: Node| None
root_node.prev_named_sibling: Node| None


# 语法树右兄弟、右命名兄弟
root_node.next_sibling: Node| None
root_node.next_named_sibling: Node| None

还有其他节点,不过我觉得有些trivial,这里不展开分析了。

解析小例子

我发现这个tree-sitter库是看到论文GraphCodeBert后才了解到,后来,很多研究比如UniXcoder,CodeT5,TreeBert和SynCoBert【不开源的论文】等等都用了该库。
【吐槽:深度学习表征代码越来越卷了,Money and Equipment Is All You Need 属于是了,各个下游任务刷榜。本来实验室刚从传统算法转机器学习,就一个GPU,留给我硕士菜鸡的毕业的机会都快弄没了。😅】

GraphCodeBert使用语法树分词的方法还是不错的,这里是原论文别人写的代码,GraphCodeBert的分词代码网址在这里,个人觉得很不错,供参考:

from tree_sitter import Language, Parser


def tree_to_token_index(root_node):
    '''
    定位代码token,返回token在代码中原始位置

    从root_node开始,深度遍历其孩子节点:
    1. 如果root_node没有孩子(root_node是叶节点)或者root_node是字符串或者注释,直接返回code_snippet对应的位置
        个人猜想:
        估计某些编程语言的string和comment类型的语法树只有单引号、双引号叶子节点,而该节点内容被忽略掉了
    2. 如果有孩子节点,深度遍历,回溯时获取结果

    使用的属性: 
    root_node.start_point: tuple[int, int]
    root_node.end_point: tuple[int, int]

    参数: root_node: Node

    返回: code_tokens: list[tuple[tuple[int,int], tuple[int, int]]]
    '''

    # 我突然发现该代码没有检测到cpp的string(也就是"hell world"),所以我改了第一行的第二个条件
    # 其他编程语言可能会有改变,所以需要小心谨慎
    # 原代码行:
    # if (len(root_node.children) == 0 or root_node.type == 'string') and root_node.type != 'comment':

    if (len(root_node.children) == 0 or root_node.type.find('string') != -1) and root_node.type != 'comment':
        return [(root_node.start_point, root_node.end_point)]
    else:
        code_tokens = []
        for child in root_node.children:
            code_tokens += tree_to_token_index(child)
        return code_tokens


def index_to_code_token(index, code):
    '''
    从 tree_to_token_index 返回的token位置元组列表 以及 代码行 生成代码token
    这里第二个参数,GraphCodeBert项目源代码写的是code,不是line_of_code

    1. 如果token起止都在同一行
        定位该代码行,定位改行的起止列,获取token
    2. token跨行【比如Python三个单引号包围的注释、或者Javascript中的模板字符串等等】
        1) 定位首行的token所在列
        2) 循环遍历到目标行之前,所有内容
        3) 定位末行的token所在列
        以上内容拼接即可

    参数: index: list[tuple[tuple[int,int], tuple[int, int]]]
    参数: code: list[str]

    返回: s: str
    '''
    start_point = index[0]
    end_point = index[1]
    if start_point[0] == end_point[0]:
        s = code[start_point[0]][start_point[1]:end_point[1]]
    else:
        s = ""
        s += code[start_point[0]][start_point[1]:]
        for i in range(start_point[0]+1, end_point[0]):
            s += code[i]
        s += code[end_point[0]][:end_point[1]]
    return s


if __name__ == '__main__':
    # 声明CPP代码解析器
    CPP_LANGUAGE = Language('build/my-languages.so', 'cpp')
    cpp_parser = Parser()
    cpp_parser.set_language(CPP_LANGUAGE)

    # 这c语言不是我写的
    cpp_code_snippet = '''
    int mian{
        piantf("hell world");
        remake O;
    }
    '''

    # 完成解析,获取根节点
    tree = cpp_parser.parse(bytes(cpp_code_snippet, "utf8"))
    root_node = tree.root_node

    # 获取token对应的位置
    tokens_index = tree_to_token_index(root_node)
    # 获取代码行
    cpp_loc = cpp_code_snippet.split('\n')
    # 获取对应每个位置下的token
    code_tokens = [index_to_code_token(x, cpp_loc) for x in tokens_index]
    # ['int', 'mian', '{', 'piantf', '(', '"hell world"', ')', ';', 'remake', 'O', ';', '}']
    print(code_tokens)

提取具体节点

其实tree-sitter还可以手动配置想要的语法树节点,通过定义query,便于直接提取特定语法树节点。我看有代码为了定位语法树节点,dfs语法树,手动写判断,一大堆代码,还要回溯判断父节点,太困难了。等一段时间后,在接下来的文章:
python环境解析任意编程语言 tree-sitter使用方法(2)

其他类似工作

python环境做C语言分析-pycparser的使用方法(1)

python环境做C语言分析-pycparser的使用方法(2)文章来源地址https://www.toymoban.com/news/detail-489084.html

到了这里,关于python环境解析任意编程语言 tree-sitter使用方法(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【手写数据库toadb】语言解析器,编程语言是这样被解析理解,解析器利器flex和bison,解析树与逆波兰式

    ​ 专栏内容 : 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学

    2024年02月08日
    浏览(45)
  • VSCode 配置 C 语言编程环境

    目录 一、下载 mingw64 二、配置环境变量 三、三个配置文件 四、格式化代码 1、安装插件 2、保存时自动格式化 3、左 { 不换行 上了两年大学,都还没花心思去搭建 C 语言编程环境,惭愧,惭愧。 mingw64 是著名的 C/C++ 编译器 GCC 的 Windows 版本。 下载地址:MinGW-w64 - for 32 and 64

    2024年02月10日
    浏览(42)
  • C语言练习题解析:挑战与突破,开启编程新篇章!(2)

    💓博客主页:江池俊的博客 ⏩收录专栏:C语言刷题专栏 👉专栏推荐:✅C语言初阶之路 ✅C语言进阶之路 💻代码仓库:江池俊的代码仓库 🎉欢迎大家点赞👍评论📝收藏⭐ 🎈前言: 本专栏每篇练习将包括 5个选择题 + 2个编程题 ,将涵盖C语言的不同方面,包括基础语法、

    2024年02月10日
    浏览(48)
  • C语言练习题解析:挑战与突破,开启编程新篇章!(1)

    💓博客主页:江池俊的博客 ⏩收录专栏:C语言刷题专栏 👉专栏推荐:✅C语言初阶之路 ✅C语言进阶之路 💻代码仓库:江池俊的代码仓库 🎉欢迎大家点赞👍评论📝收藏⭐ 🤝表情分享:🔎📷🥇🎈🐬🍁💯⭕️💮📍🚩👀🚨🧩💥📌🌴 🎈前言: 本专栏每篇练习将包

    2024年02月10日
    浏览(47)
  • MQTT,JSON,VSCODE(C语言编程环境)心得

    心得基于linux虚拟机和SSH方式,编辑基于VSCODE,编译基于GCC或G++,调试基于GDB的插件,代码管理基于git。 安装GIT:sudo apt-get install git 配置GIT: git config --global user.name “xxx” git config --global user.email “xxx@163.com” ssh-keygen -t rsa -C “xxx@163.com” 拷贝到网站 根据提示进入目录 cd

    2023年04月24日
    浏览(47)
  • 2023-05-20青少年软件编程(C语言)等级考试试卷(一级)解析

    2023-05-20青少年软件编程(C语言)等级考试试卷(一级)解析 T1、输出第二个整数 输入三个整数,把第二个输入的整数输出。 时间限制:1000 内存限制:65536 输入 只有一行,共三个整数,整数之间由一个空格分隔。整数是32位有符号整数。 输出 只有一行,一个整数,即输入

    2024年02月09日
    浏览(53)
  • Python编程语言简介

    Python 是荷兰人 Guido van Rossum (吉多·范罗苏姆,中国程序员称其为“龟叔”)在 1990 年初开发的一种解释型编程语言。 Python 的诞生是极具戏曲性的,据 Guido 自述记载,Python 语言是在圣诞节期间为了打发无聊的时间而开发的,之所以会选择 Python 作为该编程语言的名字,是因

    2024年02月07日
    浏览(54)
  • 编程语言与Python介绍

    计算机存储数据格式 原理: 计算机的工作需要基于电,而电信号只有高低电频两种状态。将高低电频命名为0和1(也就意味着计算机只识别0和1的二进制数)所以计算机才能识别诸多信息,原因是使用了二进制数进行排列组合 存储单位: 二进制数使用比特位(bit)表示:一

    2024年02月04日
    浏览(48)
  • 【C语言实现windows环境下Socket编程TCP/IP协议】

    代码是别人的,问题是我的。顺便记录一下遇见的各种问题和我的解决办法。 可能的解决方案: 1、服务端和客户端不在一个局域网,可以开热点,这样就在了。然后ipconfig查看IP地址,就ok了。至于怎么查看在不在就ping一下对方就好了。 2、一个局域网下也ping不通:看看自己

    2024年02月04日
    浏览(48)
  • 最喜爱的编程语言——Python

            编程语言(programming language)可以简单的理解为一种计算机和人都能识别的语言。一种能够让程序员准确地定义计算机所需数据的计算机语言,并精确地定义在不同情况下所应当采取的行动。        编程语言处在不断的发展和变化中,从最初的机器语言发展到如今

    2024年02月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包