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