一、模块代码
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)
三、代码仓库
如果以后有更新,最新版在这里记录: