用Python实现一个Lisp解析器

一、模块代码

Talk is cheap, show the code:

python 复制代码
# lisp.py

import re

scanner = re.Scanner([
    (r'\s+', None),
    (r'[^"()\s]+|"[^"]*"', lambda scanner, token: ('NAME', token)),
    (r'\(', lambda scanner, token: (token, token)),
    (r'\)', lambda scanner, token: (token, token)),
])

class Node:
    def __init__(self, parent=None, name=None):
        self.parent = parent
        self.name = name if parent else 'root'
        self.children = []
        if parent:
            parent.children.append(self)

    @property
    def stripname(self):
        return self.name.strip('"')

    def __add__(self, item):
        assert isinstance(item, str), type(item)
        if self.name is None:
            self.name = item
        else:
            Node(self, item)
        return self

    def __contains__(self, item):
        return any(item == node for level, node in self)

    def __eq__(self, item):
        assert isinstance(item, str), type(item)
        return item == self.name

    def __getitem__(self, item):
        assert isinstance(item, (int, str)), type(item)
        if isinstance(item, int):
            return self.children[item]
        if isinstance(item, str):
            return (node for level, node in self if node == item)

    def __iter__(self, level=0):
        yield level, self
        for child in self.children:
             yield from child.__iter__(level + 1)

    def __repr__(self):
        return f'Node({self.name!r})'

    def __str__(self):
        lines = [level * '| ' + node.name for level, node in self]
        return '\n'.join(lines)

def ParseLisp(text):
    results, remainder = scanner.scan(text)
    assert remainder == '', repr(remainder[:200])
    types = [typ for typ, name in results]
    assert types.count('NEXT') == types.count('PREV'), (types.count('NEXT'), types.count('PREV'))
    root = node = Node()
    for typ, name in results:
        if typ == '(':
            node = Node(node)
        elif typ == ')':
            node = node.parent
        elif typ == 'NAME':
            node += name
    return root

二、用法参考

python 复制代码
import lisp

text = '''
    (status
      (written
        (timestamp 2022 3 17 10 33 1)
        (author "Mentor Graphics Corporation")
        (program "xDX Designer Edif Exporter"
          (version "X-ENTP VX.2.8 <17297288>  2020-10-02 15:37:39")
        )
      )
    )
    (status
      (written
        (timestamp 2024 6 1 6 3 19)
        (author "Shixian Li")
        (program "Lisp Parser"
          (version "v1.0.2  2024-06-01 15:22:31")
        )
      )
    )
'''

root = lisp.ParseLisp(text)

# 遍历节点
for status in root['status']:

    # 在节点下按照参数顺序遍历
    timestamp = status[0][0]

    # 获取节点下的一级子节点
    timestamp_string = '-'.join(child.name for child in timestamp.children)
    print('Timestamp:', timestamp_string)

    version = status[0][2][1]

    # 判断节点名称
    assert version == 'version'

    # 获取移除引号的节点名称
    print('Version:', version[0].stripname)

三、代码仓库

如果以后有更新,最新版在这里记录:

https://github.com/znsoooo/lisp-parser

相关推荐
青少儿编程课堂1 小时前
蓝桥杯青少组图形化省考报名倒计时·全国青少年信息素养大赛·电子学会图形化考级:校园黑白棋人机对战解析
c++·python·算法·bfs·信息学竞赛
tellmewhoisi1 小时前
nlp:知识库检索和dify知识库创建
python·自然语言处理
2601_968793142 小时前
Python Requests 轻量自动化:无需浏览器,实现真人级账号浏览模拟
开发语言·python·自动化
段一凡-华北理工大学11 小时前
工业智能体:从Codex/Claude到项目实战~系列文章05:从“AI助手“到“工业智能体“:能力跃迁与落地范式
人工智能·python·claude·codex·工业智能体·高炉炼铁智能化
深蓝AI11 小时前
ContextVar 到线程池就丢了?asyncio.to_thread 和 run_in_executor 实测
python
岁月宁静12 小时前
什么是 Agent:从"会聊天"到"会做事"
人工智能·python
岁月宁静13 小时前
AI 应用开发工程师简历怎么写:HR 初筛、技术复筛、面试追问,三道关卡各看什么?
人工智能·python·面试
来让爷抱一个13 小时前
Python 异步里混用同步库,事件循环被卡住的全过程
开发语言·python
小肝一下15 小时前
3.基础语法(3)
python
深蓝AI15 小时前
Python 子进程树:父进程被杀后,孙进程为何还在跑?
python