123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- #!/usr/bin/env python
- # Copyright (c) 2014 Ingo Ruhnke <grumbel@gmail.com>
- #
- # This software is provided 'as-is', without any express or implied
- # warranty. In no event will the authors be held liable for any damages
- # arising from the use of this software.
- #
- # Permission is granted to anyone to use this software for any purpose,
- # including commercial applications, and to alter it and redistribute it
- # freely, subject to the following restrictions:
- #
- # 1. The origin of this software must not be misrepresented; you must not
- # claim that you wrote the original software. If you use this software
- # in a product, an acknowledgment in the product documentation would be
- # appreciated but is not required.
- # 2. Altered source versions must be plainly marked as such, and must not be
- # misrepresented as being the original software.
- # 3. This notice may not be removed or altered from any source distribution.
- # Taken from Flexlay
- def sexpr_read_from_file(filename):
- with open(filename, "rt") as fin:
- content = fin.read()
- return parse(content, filename)
- def parse(string, context=None):
- parser = SExprParser(string)
- try:
- return parser.parse()
- except Exception as e:
- raise SExprParseError(context, parser.line, parser.column, str(e))
- def num(s):
- try:
- return int(s)
- except ValueError:
- return float(s)
- class SExprParseError(Exception):
- def __init__(self, context, line, column, message):
- super().__init__("%s:%d:%d: error: %s" % (context, line, column, message))
- self.context = context
- self.line = line
- self.column = column
- class SExprParser:
- def __init__(self, text):
- self.text = text
- def state_list(self, c):
- if c is None:
- pass # handled in parse()
- elif c == '(':
- self.stack.append([])
- elif c == ')':
- self.stack[-2].append(self.stack.pop())
- elif c == "\"":
- self.state = self.state_string
- self.atom = ""
- elif c == ";":
- self.state = self.state_comment
- self.atom = None
- elif c == "#":
- self.state = self.state_bool
- self.atom = "#"
- elif c.isdigit() or c in "-+":
- self.state = self.state_number
- self.atom = c
- elif c.isalpha() or c in "-_":
- self.state = self.state_symbol
- self.atom = c
- elif c.isspace():
- pass
- else:
- raise Exception("unexpected character: '%s'" % c)
- def state_comment(self, c):
- if c == '\n':
- self.state = self.state_list
- else:
- pass
- def state_string(self, c):
- if c is None:
- raise Exception("string not closed at end of file")
- elif c == "\\":
- self.index += 1
- c = self.text[self.index]
- if c == "n":
- self.atom += "\n"
- elif c == "t":
- self.atom += "\t"
- elif c == "\\":
- self.atom += "\\"
- elif c == "\"":
- self.atom += "\""
- else:
- self.atom += "\\"
- self.atom += c
- elif c == "\"":
- self.stack[-1].append(self.atom)
- self.atom = None
- self.state = self.state_list
- else:
- self.atom += c
- def state_bool(self, c):
- if len(self.atom) == 2:
- if self.atom == "#f":
- self.stack[-1].append(False)
- elif self.atom == "#t":
- self.stack[-1].append(True)
- else:
- raise Exception("unknown token: %s" % self.atom)
- self.atom = None
- self.state = self.state_list
- self.index -= 1
- elif c is None:
- raise Exception("incomplete bool: %s" % self.atom)
- else:
- self.atom += c
- def state_number(self, c):
- if c is None or (not c.isdigit() and c != "."):
- self.stack[-1].append(num(self.atom))
- self.atom = None
- self.state = self.state_list
- self.index -= 1
- else:
- self.atom += c
- def state_symbol(self, c):
- if c is None or c.isspace() or c == '(' or c == ')':
- self.stack[-1].append(self.atom)
- self.atom = None
- self.state = self.state_list
- self.index -= 1
- else:
- self.atom += c
- def parse(self):
- self.atom = None
- self.stack = [[]]
- self.state = self.state_list
- self.line = 1
- self.column = 0
- self.index = 0
- while self.index < len(self.text):
- c = self.text[self.index]
- if c == '\n':
- self.line += 1
- self.column = 0
- else:
- self.column += 1
- self.state(c)
- self.index += 1
- self.state(None)
- if len(self.stack) == 1:
- return self.stack[0]
- else:
- raise Exception("list not closed")
- # EOF #
|