## Copyright 2010, 2011, 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>.
##
## This file is part of Hop.
##
## Hop is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Hop is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
## or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
## License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Hop.  If not, see <http://www.gnu.org/licenses/>.

# SPKI SEXP

import StringIO

class SyntaxError(Exception): pass

def write_sexp_str(f, x):
    f.write(str(len(x)))
    f.write(':')
    f.write(x)

def write_sexp(f, sexp):
    if type(sexp) is list:
        f.write('(')
        for x in sexp: write_sexp(f, x)
        f.write(')')
        return
    if type(sexp) is str:
        write_sexp_str(f, sexp)
        return
    if type(sexp) is tuple:
        f.write('[')
        write_sexp_str(f, sexp[0])
        f.write(']')
        write_sexp_str(f, sexp[1])
        return

def next(f, n):
    chunk = f.read(n)
    if len(chunk) < n: raise EOFError("reading sexp")
    return chunk

def skipws(f):
    while True:
        c = next(f, 1)
        if not c.isspace(): return c

def read_sexp(f):
    c = skipws(f)
    if c == '(':
        l = []
        while True:
            val = read_sexp(f)
            if val is None: return l
            l.append(val)
    if c == '[':
        h = read_sexp(f)
        c = skipws(f)
        if c != ']': raise SyntaxError("Missing hint close bracket")
        b = read_sexp(f)
        return (h, b)
    if c.isdigit():
        size = ord(c) - 48
        while True:
            c = next(f, 1)
            if c == ':': return next(f, size)
            if not c.isdigit(): raise SyntaxError("Illegal character in byte vector length")
            size = size * 10 + ord(c) - 48
    if c == ')':
        return None
    raise SyntaxError("Illegal character in sexp")

def parse(s):
    return read_sexp(StringIO.StringIO(s))

def format(x):
    f = StringIO.StringIO()
    write_sexp(f, x)
    v = f.getvalue()
    f.close()
    return v
