1
0
mirror of https://github.com/whowechina/popn_pico.git synced 2025-03-03 16:43:49 +01:00
2022-08-22 21:51:48 +08:00

74 lines
2.0 KiB
Python

"""
sexp.py
Copyright 2015 Adam Greig
Licensed under the MIT licence, see LICENSE file for details.
S-Expression parser/emitter
"""
from __future__ import print_function, division
import re
from decimal import Decimal
def parse(sexp, empty_string_placeholder="~"):
"""
Parse an S-expression into Python lists.
"""
r = [[]]
token = None
quote = False
for c in sexp:
if c == '(' and not quote:
r.append([])
elif c in (')', ' ', '\n') and not quote:
if token is not None:
r[-1].append(token)
token = None
if c == ')':
t = r.pop()
r[-1].append(t)
elif c == '"' and (token is None or token[-1] != '\\'):
quote = not quote
if not token and not quote:
token = empty_string_placeholder
else:
if token is None:
token = ''
token += c
return r[0][0]
def generate(sexp, depth=0):
"""Turn a list of lists into an s-expression."""
single_word = re.compile("^-?[a-zA-Z0-9_*\.]+$")
parts = []
for node in sexp:
if isinstance(node, str) and not single_word.match(node):
node.replace("\"", "\\\"")
node.replace("\n", "\\n")
node = "\"{}\"".format(node)
if isinstance(node, (int, Decimal)):
node = str(node)
if isinstance(node, float):
node = "{:.4f}".format(node)
if isinstance(node, (list, tuple)):
node = generate(node, depth+1)
parts.append(node)
return "\n{}({})".format(" "*depth*2, " ".join(parts))
def find(sexp, *names):
"""Return the first node in `sexp` whose name is in `names`"""
for child in sexp:
if child[0] in names:
return child
def find_all(sexp, *names):
"""Yield all nodes in `sexp` whose name is in `names`."""
for child in sexp:
if child[0] in names:
yield child