diff options
author | Jack Moffitt <jack@metajack.im> | 2014-08-28 09:34:23 -0600 |
---|---|---|
committer | Jack Moffitt <jack@metajack.im> | 2014-09-08 20:21:42 -0600 |
commit | c6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch) | |
tree | d1d74076cf7fa20e4f77ec7cb82cae98b67362cb /python/toml/toml.py | |
parent | db2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff) | |
download | servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip |
Cargoify servo
Diffstat (limited to 'python/toml/toml.py')
-rw-r--r-- | python/toml/toml.py | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/python/toml/toml.py b/python/toml/toml.py new file mode 100644 index 00000000000..8b3ecb67a2a --- /dev/null +++ b/python/toml/toml.py @@ -0,0 +1,443 @@ +import datetime, decimal + +try: + _range = xrange +except NameError: + unicode = str + _range = range + basestring = str + unichr = chr + +def load(f): + """Returns a dictionary containing the named file parsed as toml.""" + if isinstance(f, basestring): + with open(f) as ffile: + return loads(ffile.read()) + elif isinstance(f, list): + for l in f: + if not isinstance(l, basestring): + raise Exception("Load expects a list to contain filenames only") + d = [] + for l in f: + d.append(load(l)) + r = {} + for l in d: + toml_merge_dict(r, l) + return r + elif f.read: + return loads(f.read()) + else: + raise Exception("You can only load a file descriptor, filename or list") + +def loads(s): + """Returns a dictionary containing s, a string, parsed as toml.""" + implicitgroups = [] + retval = {} + currentlevel = retval + if isinstance(s, basestring): + try: + s.decode('utf8') + except AttributeError: + pass + sl = list(s) + openarr = 0 + openstring = False + arrayoftables = True + beginline = True + keygroup = False + delnum = 1 + for i in range(len(sl)): + if sl[i] == '"': + oddbackslash = False + try: + k = 1 + j = sl[i-k] + oddbackslash = False + while j == '\\': + oddbackslash = not oddbackslash + k += 1 + j = sl[i-k] + except IndexError: + pass + if not oddbackslash: + openstring = not openstring + if keygroup and (sl[i] == ' ' or sl[i] == '\t'): + keygroup = False + if arrayoftables and (sl[i] == ' ' or sl[i] == '\t'): + arrayoftables = False + if sl[i] == '#' and not openstring and not keygroup and not arrayoftables: + j = i + while sl[j] != '\n': + sl.insert(j, ' ') + sl.pop(j+1) + j += 1 + if sl[i] == '[' and not openstring and not keygroup and not arrayoftables: + if beginline: + if sl[i+1] == '[': + arrayoftables = True + else: + keygroup = True + else: + openarr += 1 + if sl[i] == ']' and not openstring and not keygroup and not arrayoftables: + if keygroup: + keygroup = False + elif arrayoftables: + if sl[i-1] == ']': + arrayoftables = False + else: + openarr -= 1 + if sl[i] == '\n': + if openstring: + raise Exception("Unbalanced quotes") + if openarr: + sl.insert(i, ' ') + sl.pop(i+1) + else: + beginline = True + elif beginline and sl[i] != ' ' and sl[i] != '\t': + beginline = False + keygroup = True + s = ''.join(sl) + s = s.split('\n') + else: + raise Exception("What exactly are you trying to pull?") + for line in s: + line = line.strip() + if line == "": + continue + if line[0] == '[': + arrayoftables = False + if line[1] == '[': + arrayoftables = True + line = line[2:].split(']]', 1) + else: + line = line[1:].split(']', 1) + if line[1].strip() != "": + raise Exception("Key group not on a line by itself.") + line = line[0] + if '[' in line: + raise Exception("Key group name cannot contain '['") + if ']' in line: + raise Exception("Key group name cannot contain']'") + groups = line.split('.') + currentlevel = retval + for i in range(len(groups)): + group = groups[i] + if group == "": + raise Exception("Can't have a keygroup with an empty name") + try: + currentlevel[group] + if i == len(groups) - 1: + if group in implicitgroups: + implicitgroups.remove(group) + if arrayoftables: + raise Exception("An implicitly defined table can't be an array") + elif arrayoftables: + currentlevel[group].append({}) + else: + raise Exception("What? "+group+" already exists?"+str(currentlevel)) + except TypeError: + if i != len(groups) - 1: + implicitgroups.append(group) + currentlevel = currentlevel[0] + if arrayoftables: + currentlevel[group] = [{}] + else: + currentlevel[group] = {} + except KeyError: + if i != len(groups) - 1: + implicitgroups.append(group) + currentlevel[group] = {} + if i == len(groups) - 1 and arrayoftables: + currentlevel[group] = [{}] + currentlevel = currentlevel[group] + if arrayoftables: + try: + currentlevel = currentlevel[-1] + except KeyError: + pass + elif "=" in line: + i = 1 + pair = line.split('=', i) + l = len(line) + while pair[-1][0] != ' ' and pair[-1][0] != '\t' and pair[-1][0] != '"' and pair[-1][0] != '[' and pair[-1] != 'true' and pair[-1] != 'false': + try: + float(pair[-1]) + break + except ValueError: + try: + datetime.datetime.strptime(pair[-1], "%Y-%m-%dT%H:%M:%SZ") + break + except ValueError: + i += 1 + pair = line.split('=', i) + newpair = [] + newpair.append('='.join(pair[:-1])) + newpair.append(pair[-1]) + pair = newpair + pair[0] = pair[0].strip() + pair[1] = pair[1].strip() + value, vtype = load_value(pair[1]) + try: + currentlevel[pair[0]] + raise Exception("Duplicate keys!") + except KeyError: + currentlevel[pair[0]] = value + return retval + +def load_value(v): + if v == 'true': + return (True, "bool") + elif v == 'false': + return (False, "bool") + elif v[0] == '"': + testv = v[1:].split('"') + closed = False + for tv in testv: + if tv == '': + closed = True + else: + oddbackslash = False + try: + i = -1 + j = tv[i] + while j == '\\': + oddbackslash = not oddbackslash + i -= 1 + j = tv[i] + except IndexError: + pass + if not oddbackslash: + if closed: + raise Exception("Stuff after closed string. WTF?") + else: + closed = True + escapes = ['0', 'b', 'f', '/', 'n', 'r', 't', '"', '\\'] + escapedchars = ['\0', '\b', '\f', '/', '\n', '\r', '\t', '\"', '\\'] + escapeseqs = v.split('\\')[1:] + backslash = False + for i in escapeseqs: + if i == '': + backslash = not backslash + else: + if i[0] not in escapes and i[0] != 'u' and not backslash: + raise Exception("Reserved escape sequence used") + if backslash: + backslash = False + if "\\u" in v: + hexchars = ['0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] + hexbytes = v.split('\\u') + newv = hexbytes[0] + hexbytes = hexbytes[1:] + for hx in hexbytes: + hxb = "" + try: + if hx[0].lower() in hexchars: + hxb += hx[0].lower() + if hx[1].lower() in hexchars: + hxb += hx[1].lower() + if hx[2].lower() in hexchars: + hxb += hx[2].lower() + if hx[3].lower() in hexchars: + hxb += hx[3].lower() + except IndexError: + if len(hxb) != 2: + raise Exception("Invalid escape sequence") + if len(hxb) != 4 and len(hxb) != 2: + raise Exception("Invalid escape sequence") + newv += unichr(int(hxb, 16)) + newv += unicode(hx[len(hxb):]) + v = newv + for i in range(len(escapes)): + v = v.replace("\\"+escapes[i], escapedchars[i]) + # (where (n) signifies a member of escapes: + # undo (\\)(\\)(n) -> (\\)(\n) + v = v.replace("\\"+escapedchars[i], "\\\\"+escapes[i]) + return (v[1:-1], "str") + elif v[0] == '[': + return (load_array(v), "array") + elif len(v) == 20 and v[-1] == 'Z': + if v[10] == 'T': + return (datetime.datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ"), "date") + else: + raise Exception("Wait, what?") + else: + itype = "int" + digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + neg = False + if v[0] == '-': + neg = True + v = v[1:] + if '.' in v: + if v.split('.', 1)[1] == '': + raise Exception("This float is missing digits after the point") + if v[0] not in digits: + raise Exception("This float doesn't have a leading digit") + v = float(v) + itype = "float" + else: + v = int(v) + if neg: + return (0 - v, itype) + return (v, itype) + + +def load_array(a): + atype = None + retval = [] + a = a.strip() + if '[' not in a[1:-1]: + strarray = False + tmpa = a[1:-1].strip() + if tmpa != '' and tmpa[0] == '"': + strarray = True + a = a[1:-1].split(',') + b = 0 + if strarray: + while b < len(a) - 1: + while a[b].strip()[-1] != '"' and a[b+1].strip()[0] != '"': + a[b] = a[b] + ',' + a[b+1] + if b < len(a) - 2: + a = a[:b+1] + a[b+2:] + else: + a = a[:b+1] + b += 1 + else: + al = list(a[1:-1]) + a = [] + openarr = 0 + j = 0 + for i in range(len(al)): + if al[i] == '[': + openarr += 1 + elif al[i] == ']': + openarr -= 1 + elif al[i] == ',' and not openarr: + a.append(''.join(al[j:i])) + j = i+1 + a.append(''.join(al[j:])) + for i in range(len(a)): + a[i] = a[i].strip() + if a[i] != '': + nval, ntype = load_value(a[i]) + if atype: + if ntype != atype: + raise Exception("Not a homogeneous array") + else: + atype = ntype + retval.append(nval) + return retval + +def dump(o, f): + """Writes out to f the toml corresponding to o. Returns said toml.""" + if f.write: + d = dumps(o) + f.write(d) + return d + else: + raise Exception("You can only dump an object to a file descriptor") + +def dumps(o): + """Returns a string containing the toml corresponding to o, a dictionary""" + retval = "" + addtoretval, sections = dump_sections(o, "") + retval += addtoretval + while sections != {}: + newsections = {} + for section in sections: + addtoretval, addtosections = dump_sections(sections[section], section) + if addtoretval: + retval += "["+section+"]\n" + retval += addtoretval + for s in addtosections: + newsections[section+"."+s] = addtosections[s] + sections = newsections + return retval + +def dump_sections(o, sup): + retstr = "" + if sup != "" and sup[-1] != ".": + sup += '.' + retdict = {} + arraystr = "" + for section in o: + if not isinstance(o[section], dict): + arrayoftables = False + if isinstance(o[section], list): + for a in o[section]: + if isinstance(a, dict): + arrayoftables = True + if arrayoftables: + for a in o[section]: + arraytabstr = "" + arraystr += "[["+sup+section+"]]\n" + s, d = dump_sections(a, sup+section) + if s: + if s[0] == "[": + arraytabstr += s + else: + arraystr += s + while d != {}: + newd = {} + for dsec in d: + s1, d1 = dump_sections(d[dsec], sup+section+dsec) + if s1: + arraytabstr += "["+sup+section+"."+dsec+"]\n" + arraytabstr += s1 + for s1 in d1: + newd[dsec+"."+s1] = d1[s1] + d = newd + arraystr += arraytabstr + else: + retstr += section + " = " + str(dump_value(o[section])) + '\n' + else: + retdict[section] = o[section] + retstr += arraystr + return (retstr, retdict) + +def dump_value(v): + if isinstance(v, list): + t = [] + retval = "[" + for u in v: + t.append(dump_value(u)) + while t != []: + s = [] + for u in t: + if isinstance(u, list): + for r in u: + s.append(r) + else: + retval += " " + str(u) + "," + t = s + retval += "]" + return retval + if isinstance(v, (str, unicode)): + escapes = ['\\', '0', 'b', 'f', '/', 'n', 'r', 't', '"'] + escapedchars = ['\\', '\0', '\b', '\f', '/', '\n', '\r', '\t', '\"'] + for i in range(len(escapes)): + v = v.replace(escapedchars[i], "\\"+escapes[i]) + return str('"'+v+'"') + if isinstance(v, bool): + return str(v).lower() + if isinstance(v, datetime.datetime): + return v.isoformat()[:19]+'Z' + if isinstance(v, float): + return '{0:f}'.format(decimal.Decimal(str(v))) + return v + +def toml_merge_dict(a, b): + for k in a: + if isinstance(a[k], dict): + try: + b[k] + except KeyError: + continue + if isinstance(b[k], dict): + b[k] = toml_merge_dict(a[k], b[k]) + else: + raise Exception("Can't merge dict and nondict in toml object") + a.update(b) + return a |