from collections import namedtuple

from parsley import makeGrammar

class Symbol(namedtuple("Symbol", "name prefix type")):
    _MARKER = object()

    def __new__(cls, name, prefix=None):
        return super(Symbol, cls).__new__(cls, name, prefix, Symbol._MARKER)


class Keyword(namedtuple("Keyword", "name prefix type")):
    _MARKER = object()

    def __new__(cls, name_or_symbol, prefix=None):
        name = name_or_symbol

        if isinstance(name_or_symbol, Symbol):
            name = name_or_symbol.name
            prefix = name_or_symbol.prefix

        return super(Keyword, cls).__new__(cls, name, prefix, Keyword._MARKER)


class Vector(tuple):
    pass


TaggedValue = namedtuple("TaggedValue", "tag value")

# XXX: There needs to be a character type and a string-that-escapes-newlines
# type in order to have full roundtripping.

edn = makeGrammar(open('edn.parsley').read(),
                  {
                    'Symbol': Symbol,
                    'Keyword': Keyword,
                    'Vector': Vector,
                    'TaggedValue': TaggedValue
                  },
                  name='edn')


def loads(string):
    return edn(string).edn()


def _dump_bool(obj):
    if obj:
        return 'true'
    else:
        return 'false'


def _dump_str(obj):
    return '"%s"' % (repr(obj)[1:-1].replace('"', '\\"'),)


def _dump_symbol(obj):
    if obj.prefix:
        return '%s/%s' % (obj.prefix, obj.name)
    return obj.name


def _dump_keyword(obj):
    if obj.prefix:
        return ':%s/%s' % (obj.prefix, obj.name)
    return ':' + obj.name


def _dump_list(obj):
    return '(' + ' '.join(map(dumps, obj)) + ')'


def dumps(obj):
    # XXX: It occurs to me that the 'e' in 'edn' means that there should be a
    # way to extend this -- jml
    RULES = [
        (bool, _dump_bool),
        (int, str),
        (float, str),
        (str, _dump_str),
        (type(None), lambda x: 'nil'),
        (Keyword, _dump_keyword),
        (Symbol, _dump_symbol),
        (tuple, _dump_list),
    ]
    for base_type, dump_rule in RULES:
        if isinstance(obj, base_type):
            return dump_rule(obj)
    raise ValueError("Cannot encode %r" % (obj,))

@@ -633,24 +633,388 @@
 ._MARKER)%0A%0A%0A
+# XXX: jml is not convinced that it's best to decode vectors or lists to%0A# tuples.  Sure that grants immutability, but it seems weird.  I'd much rather%0A# a conversion to regular Python lists, or to some proper immutable linked%0A# list / vector implementation.  The best thing would be to allow us to plug%0A# in what we'd like these things to be decoded to, I guess.%0A
 class Vector

Make sure dreid gets a piece of my mind