_W='Reserved escape sequence used'
_V='\\U'
_U='false'
_T='true'
_S='\t'
_R='}'
_Q='+'
_P='_'
_O='-'
_N=','
_M=']'
_L=' '
_K='{'
_J='='
_I='['
_H='\\'
_G='\n'
_F='.'
_E=None
_D="'"
_C='"'
_B=True
_A=False
import datetime,io
from os import linesep
import re,sys
from .tz import TomlTz
if sys.version_info<(3,):_range=xrange
else:unicode=str;_range=range;basestring=str;unichr=chr
def _detect_pathlib_path(p):
	if(3,4)<=sys.version_info:
		import pathlib as A
		if isinstance(p,A.PurePath):return _B
	return _A
def _ispath(p):
	if isinstance(p,(bytes,basestring)):return _B
	return _detect_pathlib_path(p)
def _getpath(p):
	if(3,6)<=sys.version_info:import os;return os.fspath(p)
	if _detect_pathlib_path(p):return str(p)
	return p
try:FNFError=FileNotFoundError
except NameError:FNFError=IOError
TIME_RE=re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.([0-9]{3,6}))?')
class TomlDecodeError(ValueError):
	def __init__(A,msg,doc,pos):C=doc;B=pos;D=C.count(_G,0,B)+1;E=B-C.rfind(_G,0,B);F='{} (line {} column {} char {})'.format(msg,D,E,B);ValueError.__init__(A,F);A.msg=msg;A.doc=C;A.pos=B;A.lineno=D;A.colno=E
_number_with_underscores=re.compile('([0-9])(_([0-9]))*')
class CommentValue:
	def __init__(A,val,comment,beginline,_dict):A.val=val;B=_G if beginline else _L;A.comment=B+comment;A._dict=_dict
	def __getitem__(A,key):return A.val[key]
	def __setitem__(A,key,value):A.val[key]=value
	def dump(A,dump_value_func):
		B=dump_value_func(A.val)
		if isinstance(A.val,A._dict):return A.comment+_G+unicode(B)
		else:return unicode(B)+A.comment
def _strictly_valid_num(n):
	n=n.strip()
	if not n:return _A
	if n[0]==_P:return _A
	if n[-1]==_P:return _A
	if'_.'in n or'._'in n:return _A
	if len(n)==1:return _B
	if n[0]=='0'and n[1]not in[_F,'o','b','x']:return _A
	if n[0]==_Q or n[0]==_O:
		n=n[1:]
		if len(n)>1 and n[0]=='0'and n[1]!=_F:return _A
	if'__'in n:return _A
	return _B
def load(f,_dict=dict,decoder=_E):
	B=_dict;A=decoder
	if _ispath(f):
		with io.open(_getpath(f),encoding='utf-8')as G:return loads(G.read(),B,A)
	elif isinstance(f,list):
		from os import path as D;from warnings import warn
		if not[A for A in f if D.exists(A)]:C='Load expects a list to contain filenames only.';C+=linesep;C+='The list needs to contain the path of at least one existing file.';raise FNFError(C)
		if A is _E:A=TomlDecoder(B)
		E=A.get_empty_table()
		for F in f:
			if D.exists(F):E.update(load(F,B,A))
			else:warn('Non-existent filename in list with at least one valid filename')
		return E
	else:
		try:return loads(f.read(),B,A)
		except AttributeError:raise TypeError('You can only load a file descriptor, filename or list')
_groupname_re=re.compile('^[A-Za-z0-9_-]+$')
def loads(s,_dict=dict,decoder=_E):
	q="Invalid group name '";K=decoder;d=[]
	if K is _E:K=TomlDecoder(_dict)
	e=K.get_empty_table();G=e
	if not isinstance(s,basestring):raise TypeError('Expecting something like a string')
	if not isinstance(s,unicode):s=s.decode('utf8')
	I=s;B=list(s);b=0;J=_A;Q='';D=_A;L=_A;V=_B;U=_A;W=_A;O=0;c='';j='';k=1
	for (A,E) in enumerate(B):
		if E=='\r'and B[A+1]==_G:B[A]=_L;continue
		if O:
			c+=E
			if E==_G:raise TomlDecodeError('Key name found without value. Reached end of line.',I,A)
			if J:
				if E==Q:
					S=_A;F=1
					while A>=F and B[A-F]==_H:S=not S;F+=1
					if not S:O=2;J=_A;Q=''
				continue
			elif O==1:
				if E.isspace():O=2;continue
				elif E==_F:W=_B;continue
				elif E.isalnum()or E==_P or E==_O:continue
				elif W and B[A-1]==_F and(E==_C or E==_D):J=_B;Q=E;continue
			elif O==2:
				if E.isspace():
					if W:
						X=B[A+1]
						if not X.isspace()and X!=_F:O=1
					continue
				if E==_F:
					W=_B;X=B[A+1]
					if not X.isspace()and X!=_F:O=1
					continue
			if E==_J:O=0;j=c[:-1].rstrip();c='';W=_A
			else:raise TomlDecodeError("Found invalid character in key name: '"+E+"'. Try quoting the key name.",I,A)
		if E==_D and Q!=_C:
			F=1
			try:
				while B[A-F]==_D:
					F+=1
					if F==3:break
			except IndexError:pass
			if F==3:D=not D;J=D
			else:J=not J
			if J:Q=_D
			else:Q=''
		if E==_C and Q!=_D:
			S=_A;F=1;f=_A
			try:
				while B[A-F]==_C:
					F+=1
					if F==3:f=_B;break
				if F==1 or F==3 and f:
					while B[A-F]==_H:S=not S;F+=1
			except IndexError:pass
			if not S:
				if f:D=not D;J=D
				else:J=not J
			if J:Q=_C
			else:Q=''
		if E=='#'and(not J and not U and not L):
			R=A;l=''
			try:
				while B[R]!=_G:l+=s[R];B[R]=_L;R+=1
			except IndexError:break
			if not b:K.preserve_comment(k,j,l,V)
		if E==_I and(not J and not U and not L):
			if V:
				if len(B)>A+1 and B[A+1]==_I:L=_B
				else:U=_B
			else:b+=1
		if E==_M and not J:
			if U:U=_A
			elif L:
				if B[A-1]==_M:L=_A
			else:b-=1
		if E==_G:
			if J or D:
				if not D:raise TomlDecodeError('Unbalanced quotes',I,A)
				if(B[A-1]==_D or B[A-1]==_C)and B[A-2]==B[A-1]:
					B[A]=B[A-1]
					if B[A-3]==B[A-1]:B[A-3]=_L
			elif b:B[A]=_L
			else:V=_B
			k+=1
		elif V and B[A]!=_L and B[A]!=_S:
			V=_A
			if not U and not L:
				if B[A]==_J:raise TomlDecodeError('Found empty keyname. ',I,A)
				O=1;c+=E
	if O:raise TomlDecodeError('Key name found without value. Reached end of file.',I,len(s))
	if J:raise TomlDecodeError('Unterminated string found. Reached end of file.',I,len(s))
	s=''.join(B);s=s.split(_G);T=_E;D='';P=_A;N=0
	for (g,C) in enumerate(s):
		if g>0:N+=len(s[g-1])+1
		K.embed_comments(g,G)
		if not D or P or _G not in D:C=C.strip()
		if C==''and(not T or P):continue
		if T:
			if P:D+=C
			else:D+=C
			P=_A;h=_A
			if D[0]==_I:h=C[-1]==_M
			elif len(C)>2:h=C[-1]==D[0]and C[-2]==D[0]and C[-3]==D[0]
			if h:
				try:o,r=K.load_value(D)
				except ValueError as Y:raise TomlDecodeError(str(Y),I,N)
				G[T]=o;T=_E;D=''
			else:
				F=len(D)-1
				while F>-1 and D[F]==_H:P=not P;F-=1
				if P:D=D[:-1]
				else:D+=_G
			continue
		if C[0]==_I:
			L=_A
			if len(C)==1:raise TomlDecodeError('Opening key group bracket on line by itself.',I,N)
			if C[1]==_I:L=_B;C=C[2:];Z=']]'
			else:C=C[1:];Z=_M
			A=1;p=K._get_split_on_quotes(C);i=_A
			for m in p:
				if not i and Z in m:break
				A+=m.count(Z);i=not i
			C=C.split(Z,A)
			if len(C)<A+1 or C[-1].strip()!='':raise TomlDecodeError('Key group not on a line by itself.',I,N)
			H=Z.join(C[:-1]).split(_F);A=0
			while A<len(H):
				H[A]=H[A].strip()
				if len(H[A])>0 and(H[A][0]==_C or H[A][0]==_D):
					a=H[A];R=A+1
					while not a[0]==a[-1]:
						R+=1
						if R>len(H)+2:raise TomlDecodeError(q+a+"' Something "+'went wrong.',I,N)
						a=_F.join(H[A:R]).strip()
					H[A]=a[1:-1];H[A+1:R]=[]
				elif not _groupname_re.match(H[A]):raise TomlDecodeError(q+H[A]+"'. Try quoting it.",I,N)
				A+=1
			G=e
			for A in _range(len(H)):
				M=H[A]
				if M=='':raise TomlDecodeError("Can't have a keygroup with an empty name",I,N)
				try:
					G[M]
					if A==len(H)-1:
						if M in d:
							d.remove(M)
							if L:raise TomlDecodeError("An implicitly defined table can't be an array",I,N)
						elif L:G[M].append(K.get_empty_table())
						else:raise TomlDecodeError('What? '+M+' already exists?'+str(G),I,N)
				except TypeError:
					G=G[-1]
					if M not in G:
						G[M]=K.get_empty_table()
						if A==len(H)-1 and L:G[M]=[K.get_empty_table()]
				except KeyError:
					if A!=len(H)-1:d.append(M)
					G[M]=K.get_empty_table()
					if A==len(H)-1 and L:G[M]=[K.get_empty_table()]
				G=G[M]
				if L:
					try:G=G[-1]
					except KeyError:pass
		elif C[0]==_K:
			if C[-1]!=_R:raise TomlDecodeError('Line breaks are not allowed in inlineobjects',I,N)
			try:K.load_inline_object(C,G,T,P)
			except ValueError as Y:raise TomlDecodeError(str(Y),I,N)
		elif _J in C:
			try:n=K.load_line(C,G,T,P)
			except ValueError as Y:raise TomlDecodeError(str(Y),I,N)
			if n is not _E:T,D,P=n
	return e
def _load_date(val):
	I='Z';A=val;G=0;F=_E
	try:
		if len(A)>19:
			if A[19]==_F:
				if A[-1].upper()==I:C=A[20:-1];D=I
				else:
					B=A[20:]
					if _Q in B:E=B.index(_Q);C=B[:E];D=B[E:]
					elif _O in B:E=B.index(_O);C=B[:E];D=B[E:]
					else:D=_E;C=B
				if D is not _E:F=TomlTz(D)
				G=int(int(C)*10**(6-len(C)))
			else:F=TomlTz(A[19:])
	except ValueError:F=_E
	if _O not in A[1:]:return _E
	try:
		if len(A)==10:H=datetime.date(int(A[:4]),int(A[5:7]),int(A[8:10]))
		else:H=datetime.datetime(int(A[:4]),int(A[5:7]),int(A[8:10]),int(A[11:13]),int(A[14:16]),int(A[17:19]),G,F)
	except ValueError:return _E
	return H
def _load_unicode_escapes(v,hexbytes,prefix):
	G='Invalid escape sequence: ';E=prefix;C=_A;A=len(v)-1
	while A>-1 and v[A]==_H:C=not C;A-=1
	for D in hexbytes:
		if C:
			C=_A;A=len(D)-1
			while A>-1 and D[A]==_H:C=not C;A-=1
			v+=E;v+=D;continue
		B='';A=0;F=4
		if E==_V:F=8
		B=''.join(D[A:A+F]).lower()
		if B.strip('0123456789abcdef'):raise ValueError(G+B)
		if B[0]=='d'and B[1].strip('01234567'):raise ValueError(G+B+'. Only scalar unicode points are allowed.')
		v+=unichr(int(B,16));v+=unicode(D[len(B):])
	return v
_escapes=['0','b','f','n','r','t',_C]
_escapedchars=['\x00','\x08','\x0c',_G,'\r',_S,_C]
_escape_to_escapedchars=dict(zip(_escapes,_escapedchars))
def _unescape(v):
	A=0;B=_A
	while A<len(v):
		if B:
			B=_A
			if v[A]in _escapes:v=v[:A-1]+_escape_to_escapedchars[v[A]]+v[A+1:]
			elif v[A]==_H:v=v[:A-1]+v[A:]
			elif v[A]=='u'or v[A]=='U':A+=1
			else:raise ValueError(_W)
			continue
		elif v[A]==_H:B=_B
		A+=1
	return v
class InlineTableDict:0
class TomlDecoder:
	def __init__(A,_dict=dict):A._dict=_dict
	def get_empty_table(A):return A._dict()
	def get_empty_inline_table(A):
		class B(A._dict,InlineTableDict):0
		return B()
	def load_inline_object(E,line,currentlevel,multikey=_A,multibackslash=_A):
		B=line[1:-1].split(_N);D=[]
		if len(B)==1 and not B[0].strip():B.pop()
		while len(B)>0:
			C=B.pop(0)
			try:H,A=C.split(_J,1)
			except ValueError:raise ValueError('Invalid inline table encountered')
			A=A.strip()
			if A[0]==A[-1]and A[0]in(_C,_D)or(A[0]in'-0123456789'or A in(_T,_U)or A[0]==_I and A[-1]==_M or A[0]==_K and A[-1]==_R):D.append(C)
			elif len(B)>0:B[0]=C+_N+B[0]
			else:raise ValueError('Invalid inline table value encountered')
		for F in D:
			G=E.load_line(F,currentlevel,multikey,multibackslash)
			if G is not _E:break
	def _get_split_on_quotes(F,line):
		A=line.split(_C);D=_A;C=[]
		if len(A)>1 and _D in A[0]:
			B=A[0].split(_D);A=A[1:]
			while len(B)%2==0 and len(A):
				B[-1]+=_C+A[0];A=A[1:]
				if _D in B[-1]:B=B[:-1]+B[-1].split(_D)
			C+=B
		for E in A:
			if D:C.append(E)
			else:C+=E.split(_D);D=not D
		return C
	def load_line(E,line,currentlevel,multikey,multibackslash):
		S='Duplicate keys!';L=multikey;K=line;G=multibackslash;D=currentlevel;H=1;M=E._get_split_on_quotes(K);C=_A
		for F in M:
			if not C and _J in F:break
			H+=F.count(_J);C=not C
		A=K.split(_J,H);N=_strictly_valid_num(A[-1])
		if _number_with_underscores.match(A[-1]):A[-1]=A[-1].replace(_P,'')
		while len(A[-1])and(A[-1][0]!=_L and A[-1][0]!=_S and A[-1][0]!=_D and A[-1][0]!=_C and A[-1][0]!=_I and A[-1][0]!=_K and A[-1].strip()!=_T and A[-1].strip()!=_U):
			try:float(A[-1]);break
			except ValueError:pass
			if _load_date(A[-1])is not _E:break
			if TIME_RE.match(A[-1]):break
			H+=1;P=A[-1];A=K.split(_J,H)
			if P==A[-1]:raise ValueError('Invalid date or number')
			if N:N=_strictly_valid_num(A[-1])
		A=[_J.join(A[:-1]).strip(),A[-1].strip()]
		if _F in A[0]:
			if _C in A[0]or _D in A[0]:
				M=E._get_split_on_quotes(A[0]);C=_A;B=[]
				for F in M:
					if C:B.append(F)
					else:B+=[A.strip()for A in F.split(_F)]
					C=not C
			else:B=A[0].split(_F)
			while B[-1]=='':B=B[:-1]
			for I in B[:-1]:
				if I=='':continue
				if I not in D:D[I]=E.get_empty_table()
				D=D[I]
			A[0]=B[-1].strip()
		elif(A[0][0]==_C or A[0][0]==_D)and A[0][-1]==A[0][0]:A[0]=_unescape(A[0][1:-1])
		J,Q=E._load_line_multiline_str(A[1])
		if J>-1:
			while J>-1 and A[1][J+Q]==_H:G=not G;J-=1
			if G:O=A[1][:-1]
			else:O=A[1]+_G
			L=A[0]
		else:R,T=E.load_value(A[1],N)
		try:D[A[0]];raise ValueError(S)
		except TypeError:raise ValueError(S)
		except KeyError:
			if L:return L,O,G
			else:D[A[0]]=R
	def _load_line_multiline_str(C,p):
		B=0
		if len(p)<3:return-1,B
		if p[0]==_I and(p.strip()[-1]!=_M and C._load_array_isstrarray(p)):
			A=p[1:].strip().split(_N)
			while len(A)>1 and A[-1][0]!=_C and A[-1][0]!=_D:A=A[:-2]+[A[-2]+_N+A[-1]]
			A=A[-1];B=len(p)-len(A);p=A
		if p[0]!=_C and p[0]!=_D:return-1,B
		if p[1]!=p[0]or p[2]!=p[0]:return-1,B
		if len(p)>5 and p[-1]==p[0]and p[-2]==p[0]and p[-3]==p[0]:return-1,B
		return len(p)-1,B
	def load_value(E,v,strictly_valid=_B):
		a='float';Z='int';Y='bool'
		if not v:raise ValueError('Empty value is invalid')
		if v==_T:return _B,Y
		elif v==_U:return _A,Y
		elif v[0]==_C or v[0]==_D:
			F=v[0];B=v[1:].split(F);G=_A;H=0
			if len(B)>1 and B[0]==''and B[1]=='':B=B[2:];G=_B
			I=_A
			for J in B:
				if J=='':
					if G:H+=1
					else:I=_B
				else:
					K=_A
					try:
						A=-1;N=J[A]
						while N==_H:K=not K;A-=1;N=J[A]
					except IndexError:pass
					if not K:
						if I:raise ValueError('Found tokens after a closed '+'string. Invalid TOML.')
						elif not G or H>1:I=_B
						else:H=0
			if F==_C:
				T=v.split(_H)[1:];C=_A
				for A in T:
					if A=='':C=not C
					else:
						if A[0]not in _escapes and(A[0]!='u'and A[0]!='U'and not C):raise ValueError(_W)
						if C:C=_A
				for L in ['\\u',_V]:
					if L in v:O=v.split(L);v=_load_unicode_escapes(O[0],O[1:],L)
				v=_unescape(v)
			if len(v)>1 and v[1]==F and(len(v)<3 or v[1]==v[2]):v=v[2:-2]
			return v[1:-1],'str'
		elif v[0]==_I:return E.load_array(v),'array'
		elif v[0]==_K:P=E.get_empty_inline_table();E.load_inline_object(v,P);return P,'inline_object'
		elif TIME_RE.match(v):U,V,W,b,Q=TIME_RE.match(v).groups();X=datetime.time(int(U),int(V),int(W),int(Q)if Q else 0);return X,'time'
		else:
			R=_load_date(v)
			if R is not _E:return R,'date'
			if not strictly_valid:raise ValueError('Weirdness with leading zeroes or underscores in your number.')
			D=Z;S=_A
			if v[0]==_O:S=_B;v=v[1:]
			elif v[0]==_Q:v=v[1:]
			v=v.replace(_P,'');M=v.lower()
			if _F in v or'x'not in v and('e'in v or'E'in v):
				if _F in v and v.split(_F,1)[1]=='':raise ValueError('This float is missing digits after the point')
				if v[0]not in'0123456789':raise ValueError("This float doesn't have a leading digit")
				v=float(v);D=a
			elif len(M)==3 and(M=='inf'or M=='nan'):v=float(v);D=a
			if D==Z:v=int(v,0)
			if S:return 0-v,D
			return v,D
	def bounded_string(C,s):
		if len(s)==0:return _B
		if s[-1]!=s[0]:return _A
		A=-2;B=_A
		while len(s)+A>0:
			if s[A]==_H:B=not B;A-=1
			else:break
		return not B
	def _load_array_isstrarray(A,a):
		a=a[1:-1].strip()
		if a!=''and(a[0]==_C or a[0]==_D):return _B
		return _A
	def load_array(H,a):
		I=_E;N=[];a=a.strip()
		if _I not in a[1:-1]or''!=a[1:-1].split(_I)[0].strip():
			Q=H._load_array_isstrarray(a)
			if not a[1:-1].strip().startswith(_K):a=a[1:-1].split(_N)
			else:
				O=[];E=1;A=2;J=1 if a[E]==_K else 0;F=_A
				while A<len(a[1:]):
					if a[A]==_C or a[A]==_D:
						if F:
							K=A-1
							while K>-1 and a[K]==_H:F=not F;K-=1
						F=not F
					if not F and a[A]==_K:J+=1
					if F or a[A]!=_R:A+=1;continue
					elif a[A]==_R and J>1:J-=1;A+=1;continue
					A+=1;O.append(a[E:A]);E=A+1
					while E<len(a[1:])and a[E]!=_K:E+=1
					A=E+1
				a=O
			B=0
			if Q:
				while B<len(a)-1:
					C=a[B].strip()
					while not H.bounded_string(C)or len(C)>2 and C[0]==C[1]==C[2]and C[-2]!=C[0]and C[-3]!=C[0]:
						a[B]=a[B]+_N+a[B+1];C=a[B].strip()
						if B<len(a)-2:a=a[:B+1]+a[B+2:]
						else:a=a[:B+1]
					B+=1
		else:
			G=list(a[1:-1]);a=[];L=0;M=0
			for D in _range(len(G)):
				if G[D]==_I:L+=1
				elif G[D]==_M:L-=1
				elif G[D]==_N and not L:a.append(''.join(G[M:D]));M=D+1
			a.append(''.join(G[M:]))
		for D in _range(len(a)):
			a[D]=a[D].strip()
			if a[D]!='':
				R,P=H.load_value(a[D])
				if I:
					if P!=I:raise ValueError('Not a homogeneous array')
				else:I=P
				N.append(R)
		return N
	def preserve_comment(A,line_no,key,comment,beginline):0
	def embed_comments(A,idx,currentlevel):0
class TomlPreserveCommentDecoder(TomlDecoder):
	def __init__(A,_dict=dict):A.saved_comments={};super(TomlPreserveCommentDecoder,A).__init__(_dict)
	def preserve_comment(A,line_no,key,comment,beginline):A.saved_comments[line_no]=key,comment,beginline
	def embed_comments(A,idx,currentlevel):
		B=currentlevel
		if idx not in A.saved_comments:return
		C,D,E=A.saved_comments[idx];B[C]=CommentValue(B[C],D,E,A._dict)