# Mungo is magical data format that easily chomps up silly datafiles
# with the ferocity of a mongoose (mungos mungo). Then through parsing,
# it can be excreted into tabular/associative/structured form!
# [] are comments,
# "", '', `` are string quotes
# *-+|~>=# are the default list delimiters
#  (define more delimiters if you think you need to)
# : is the mapping delimiter, used when associating keys with string values

class MungoParseException(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)


# Returns whether or not a file exists.
def FileExists(f):
    try:
        file = open(f)
    except IOError:
        return False
    return True

LIST_DELIMITERS = "*-+|~>=#"
MAPPING_DELIMITER = ':'
WHITESPACE = " \t"
QUOTE = "'`\""
COMMENT_OPEN = '['
COMMENT_CLOSE = ']'

# Convert Mungo format into Python dictionary
def MungoToDict(filename):
    result = {}
    if FileExists(filename):
        buffer = ""
        f = open(filename)
        for line in f:
            buffer += line
        f.close()

        lists = {}
        delimiters = []
        delimiter_current = None
        comment = False
        quote = False
        d = None
        k = ""
        v = ""
        line_index = 1
        for i in range(len(buffer)):
            c = buffer[i]
            # Next line
            if c == '\n':
                if quote:
                    v += c
                line_index += 1
            elif not comment and not quote:
                # List delimiter
                if c in LIST_DELIMITERS:
                    # Map current key to a string value and create a new list item
                    if d != None and delimiter_current == c:
                        if not len(v):
                            raise MungoParseException("Expected a value identifier but got list delimiter '" + c + "' on line " + str(line_index))
                        d[k] = v
                    # Map current key to a list
                    elif c not in lists:
                        delimiter_current = c
                        if len(k) and len(v):
                            raise MungoParseException("Unexpected list delimiter '" + c + "' on line " + str(line_index))
                        if d != None and not len(v):
                            raise MungoParseException("Expected a key identifier but got list delimiter '" + c + "' on line " + str(line_index))
                        if d == None:
                            d = result
                        else:
                            d[v] = {}
                            d = d[v]
                        lists[c] = d
                        delimiters.append(c)
                    # Map current key to a string value and return to a list on an upper level
                    else:
                        delimiter_current = c
                        if not len(v):
                            raise MungoParseException("Expected a value identifier but got list delimiter '" + c + "' on line " + str(line_index))
                        d[k] = v
                        d = lists[c]
                        delimiter_index = False
                        for i in range(len(delimiters)):
                            if delimiters[i] == c:
                                delimiter_index = i
                                break
                        if delimiter_index != None:
                            while delimiter_index < len(delimiters) - 1:
                                del lists[delimiters.pop()]
                    k = ""
                    v = ""
                # Mapping delimiter, make current indentifier into the key, make new indentifier a value
                elif c == MAPPING_DELIMITER:
                    if d == None:
                         raise MungoParseException("Attempt to define mapping before a list was started on line " + str(line_index))
                    if not len(v):
                        raise MungoParseException("Expected a key identifier but got mapping delimiter '" + c + "' on line " + str(line_index))
                    k = v
                    v = ""
                elif c == COMMENT_OPEN:
                    comment = True
                elif c in QUOTE:
                    if d == None:
                         raise MungoParseException(c + " quote encountered before a list was started on line " + str(line_index))
                    quote = c
                elif c not in WHITESPACE:
                    if d == None:
                         raise MungoParseException("Indentifier character '" + c + "' encountered before a list was started on line " + str(line_index))
                    v += c
            elif quote:
                if c == quote:
                    quote = False
                else:
                    v += c
            elif comment and c == COMMENT_CLOSE:
                comment = False
        # If we reached EOF mid-string, whine
        if not len(v) or (len(v) and not len(k)):
            raise MungoParseException("Expected a value identifier but got EOF on line " + str(line_index))
        if len(k) > 0:
            d[k] = v
    else:
        raise MungoParseException("Failed to open '" + filename + "'")

    # Finally, return our resolved dictionary.
    return result