tango-py/tango/parser.py

92 lines
2.3 KiB
Python

from tango.value import TangoIdent, TangoExpr
PAREN_OPEN = '('
PAREN_CLOSE = ')'
DOUBLE_QUOTE = '"'
WHITESPACE = ' \t\n\r'
class StringReader:
def __init__(self, s):
self._src = s
self._len = len(s)
self._cur = -1
def next(self):
if self._len == self._cur + 1:
return None
else:
self._cur += 1
return self._src[self._cur]
class TangoParser:
def __init__(self, src):
self._cur = None
self._src = src
self._next()
def parse(self):
while self._cur is not None:
self._skip_whitespace()
if self._cur is PAREN_OPEN:
return self._parse_expr()
elif self._cur is DOUBLE_QUOTE:
return self._parse_str()
elif self._cur.isalpha():
return self._parse_ident()
elif self._cur.isnumeric():
return self._parse_int()
else:
self._raise_invalid_char()
def _parse_expr(self):
expr = TangoExpr()
self._next()
while self._cur is not None:
self._skip_whitespace()
if self._cur is PAREN_CLOSE:
self._next()
return expr
else:
expr.append(self.parse())
self._raise_unexpected_end()
def _parse_ident(self):
ident = self._read_until(WHITESPACE + PAREN_CLOSE)
return TangoIdent(ident)
def _parse_str(self):
self._next()
s = self._read_until(DOUBLE_QUOTE)
self._next()
return s
def _parse_int(self):
s = self._read_until(WHITESPACE + PAREN_CLOSE)
return int(s)
def _read_until(self, delims):
s = ''
while self._cur is not None and self._cur not in delims:
s += self._cur
self._next()
return s
def _next(self):
self._cur = self._src.next()
return self._cur
def _raise_unexpected_end(self):
raise ValueError('Unexpected end of stream')
def _raise_invalid_char(self):
raise ValueError('Invalid chr: ' + self._cur)
def _skip_whitespace(self):
if self._cur in WHITESPACE:
while self._next() in WHITESPACE:
continue