mirror of
https://github.com/whowechina/popn_pico.git
synced 2025-03-03 16:43:49 +01:00
74 lines
2.0 KiB
Python
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
|