<s> from __future__ import print_function <EOL> import base64 <EOL> from io import BytesIO <EOL> ALPHA = b"<STR_LIT>" <EOL> DIGITS = b"<STR_LIT>" <EOL> WHITESPACE = b"<STR_LIT>" <EOL> PSEUDO_ALPHA = b"<STR_LIT>" <EOL> PUNCTUATION = b'<STR_LIT>' <EOL> VERBATIM = b"<STR_LIT>" <EOL> TOKEN_CHARS = DIGITS + ALPHA + PSEUDO_ALPHA <EOL> HEX_DIGITS = b"<STR_LIT>" <EOL> B64_DIGITS = b"<STR_LIT>" <EOL> PRINTABLE_CHARS = bytes ( range ( <NUM_LIT> , <NUM_LIT> ) ) <EOL> ESCAPE_CHARS = { <EOL> b"<STR_LIT>" : b"<STR_LIT:b>" , <EOL> b"<STR_LIT:\t>" : b"<STR_LIT:t>" , <EOL> b"<STR_LIT>" : b"<STR_LIT:v>" , <EOL> b"<STR_LIT:\n>" : b"<STR_LIT:n>" , <EOL> b"<STR_LIT>" : b"<STR_LIT:f>" , <EOL> b"<STR_LIT:\r>" : b"<STR_LIT:r>" , <EOL> b"<STR_LIT:\\>" : b"<STR_LIT:\\>" , <EOL> } <EOL> class SexpParser ( object ) : <EOL> def __init__ ( self , buf ) : <EOL> self . bytesize = <NUM_LIT:8> <EOL> self . bits = <NUM_LIT:0> <EOL> self . nBits = <NUM_LIT:0> <EOL> self . buf = buf if hasattr ( buf , "<STR_LIT>" ) else BytesIO ( buf ) <EOL> self . char = b"<STR_LIT>" <EOL> self . advance ( ) <EOL> def __iter__ ( self ) : <EOL> return self <EOL> def __next__ ( self ) : <EOL> return self . next ( ) <EOL> def next ( self ) : <EOL> obj = self . scan_object ( ) <EOL> if obj : <EOL> return obj <EOL> else : <EOL> raise StopIteration <EOL> @ property <EOL> def pos ( self ) : <EOL> return self . buf . tell ( ) <EOL> def advance ( self ) : <EOL> """<STR_LIT>""" <EOL> while True : <EOL> self . last = self . char <EOL> self . char = self . buf . read ( <NUM_LIT:1> ) <EOL> if not self . char : <EOL> self . bytesize = <NUM_LIT:8> <EOL> self . char = None <EOL> return self . char <EOL> if self . char is None : <EOL> return self . char <EOL> elif ( self . bytesize == <NUM_LIT:6> and self . char in b"<STR_LIT>" ) or ( self . bytesize == <NUM_LIT:4> and self . char == b"<STR_LIT:#>" ) : <EOL> if self . nBits and ( <NUM_LIT:1> << self . nBits ) - <NUM_LIT:1> & self . bits : <EOL> raise IOError ( "<STR_LIT>" % <EOL> ( self . bytesize , self . nBits , self . pos ) ) <EOL> self . bytesize = <NUM_LIT:8> <EOL> return self . char <EOL> elif self . bytesize != <NUM_LIT:8> and self . char in WHITESPACE : <EOL> pass <EOL> elif self . bytesize == <NUM_LIT:6> and self . char == b"<STR_LIT:=>" : <EOL> self . nBits -= <NUM_LIT:2> <EOL> elif self . bytesize == <NUM_LIT:8> : <EOL> return self . char <EOL> elif self . bytesize < <NUM_LIT:8> : <EOL> self . bits <<= self . bytesize <EOL> self . nBits += self . bytesize <EOL> if self . bytesize == <NUM_LIT:6> and self . char in B64_DIGITS : <EOL> self . bits |= B64_DIGITS . index ( self . char ) <EOL> elif self . bytesize == <NUM_LIT:4> and self . char in HEX_DIGITS : <EOL> self . bits |= int ( self . char , <NUM_LIT:16> ) <EOL> else : <EOL> raise IOError ( "<STR_LIT>" % <EOL> ( self . char , self . bytesize ) ) <EOL> if self . nBits >= <NUM_LIT:8> : <EOL> self . nBits -= <NUM_LIT:8> <EOL> byte = ( self . bits >> self . nBits ) & <NUM_LIT> <EOL> self . bits &= ( <NUM_LIT:1> << self . nBits ) - <NUM_LIT:1> <EOL> self . char = bytes ( [ byte ] ) <EOL> return self . char <EOL> def skip_whitespace ( self ) : <EOL> while self . char : <EOL> if self . char in WHITESPACE : <EOL> self . advance ( ) <EOL> elif self . char == b"<STR_LIT:;>" and self . last in b"<STR_LIT:\r\n>" : <EOL> while self . char and self . char not in b"<STR_LIT:\r\n>" : <EOL> self . advance ( ) <EOL> else : <EOL> return <EOL> def skip_char ( self , char ) : <EOL> """<STR_LIT>""" <EOL> if len ( char ) != <NUM_LIT:1> : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> elif not self . char : <EOL> raise IOError ( "<STR_LIT>" % char ) <EOL> elif self . char == char : <EOL> self . advance ( ) <EOL> else : <EOL> raise IOError ( "<STR_LIT>" % ( <EOL> self . char , char ) ) <EOL> def scan_token ( self ) : <EOL> self . skip_whitespace ( ) <EOL> out = b"<STR_LIT>" <EOL> while self . char and self . char in TOKEN_CHARS : <EOL> out += self . char <EOL> self . advance ( ) <EOL> return out <EOL> def scan_decimal ( self ) : <EOL> i , value = <NUM_LIT:0> , <NUM_LIT:0> <EOL> while self . char and self . char in DIGITS : <EOL> value = value * <NUM_LIT:10> + int ( self . char ) <EOL> i += <NUM_LIT:1> <EOL> if i > <NUM_LIT:8> : <EOL> raise IOError ( "<STR_LIT>" % value ) <EOL> self . advance ( ) <EOL> return value <EOL> def scan_verbatim_string ( self , length = None ) : <EOL> """<STR_LIT>""" <EOL> self . skip_whitespace ( ) <EOL> self . skip_char ( b"<STR_LIT::>" ) <EOL> if not length : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> out , i = b"<STR_LIT>" , <NUM_LIT:0> <EOL> while i < length : <EOL> out += self . char <EOL> self . advance ( ) <EOL> i += <NUM_LIT:1> <EOL> return out <EOL> def scan_quoted_string ( self , length = None ) : <EOL> self . skip_char ( b"<STR_LIT>" ) <EOL> out = b"<STR_LIT>" <EOL> while length is None or len ( out ) <= length : <EOL> if not self . char : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> elif self . char == b"<STR_LIT>" : <EOL> if length is None or len ( out ) == length : <EOL> self . skip_char ( b"<STR_LIT>" ) <EOL> break <EOL> else : <EOL> raise ValueError ( "<STR_LIT>" % length ) <EOL> elif self . char == b"<STR_LIT:\\>" : <EOL> c = self . advance ( ) <EOL> if c in b"<STR_LIT:\r\n>" : <EOL> continue <EOL> elif c in b"<STR_LIT>" : <EOL> s = c + self . advance ( ) + self . advance ( ) <EOL> val = int ( s , <NUM_LIT:8> ) <EOL> out += chr ( val ) <EOL> elif c == b"<STR_LIT:b>" : out += b"<STR_LIT>" <EOL> elif c == b"<STR_LIT:f>" : out += b"<STR_LIT>" <EOL> elif c == b"<STR_LIT:n>" : out += b"<STR_LIT:\n>" <EOL> elif c == b"<STR_LIT:r>" : out += b"<STR_LIT:\r>" <EOL> elif c == b"<STR_LIT:t>" : out += b"<STR_LIT:\t>" <EOL> elif c == b"<STR_LIT:v>" : out += b"<STR_LIT>" <EOL> elif c == b"<STR_LIT:x>" : <EOL> s = self . advance ( ) + self . advance ( ) <EOL> val = int ( s , <NUM_LIT:16> ) <EOL> out += chr ( val ) <EOL> else : <EOL> raise ValueError ( "<STR_LIT>" % ( c , self . pos ) ) <EOL> else : <EOL> out += self . char <EOL> self . advance ( ) <EOL> return out <EOL> def scan_hex_string ( self , length = None ) : <EOL> self . bytesize = <NUM_LIT:4> <EOL> self . skip_char ( b"<STR_LIT:#>" ) <EOL> out = b"<STR_LIT>" <EOL> while self . char and ( self . char != b"<STR_LIT:#>" or self . bytesize == <NUM_LIT:4> ) : <EOL> out += self . char <EOL> self . advance ( ) <EOL> self . skip_char ( b"<STR_LIT:#>" ) <EOL> if length and length != len ( out ) : <EOL> raise ValueError ( "<STR_LIT>" % <EOL> ( len ( out ) , length ) ) <EOL> return out <EOL> def scan_base64_string ( self , length = None ) : <EOL> self . bytesize = <NUM_LIT:6> <EOL> self . skip_char ( b"<STR_LIT:|>" ) <EOL> out = b"<STR_LIT>" <EOL> while self . char and ( self . char != b"<STR_LIT:|>" or self . bytesize == <NUM_LIT:6> ) : <EOL> out += self . char <EOL> self . advance ( ) <EOL> self . skip_char ( b"<STR_LIT:|>" ) <EOL> if length and length != len ( out ) : <EOL> raise ValueError ( "<STR_LIT>" % <EOL> ( len ( out ) , length ) ) <EOL> return out <EOL> def scan_simple_string ( self ) : <EOL> self . skip_whitespace ( ) <EOL> if not self . char : <EOL> return None <EOL> elif self . char in TOKEN_CHARS and self . char not in DIGITS : <EOL> return self . scan_token ( ) <EOL> elif self . char in DIGITS or self . char in b"<STR_LIT>" : <EOL> if self . char in DIGITS : <EOL> length = self . scan_decimal ( ) <EOL> else : <EOL> length = None <EOL> if self . char == b"<STR_LIT>" : <EOL> return self . scan_quoted_string ( length ) <EOL> elif self . char == b"<STR_LIT:#>" : <EOL> return self . scan_hex_string ( length ) <EOL> elif self . char == b"<STR_LIT:|>" : <EOL> return self . scan_base64_string ( length ) <EOL> elif self . char == b"<STR_LIT::>" : <EOL> return self . scan_verbatim_string ( length ) <EOL> else : <EOL> raise ValueError ( "<STR_LIT>" % ( self . char , self . pos ) ) <EOL> else : <EOL> raise ValueError ( "<STR_LIT>" % ( self . char , self . pos ) ) <EOL> def scan_string ( self ) : <EOL> hint = None <EOL> if self . char == b"<STR_LIT:[>" : <EOL> self . skip_char ( b"<STR_LIT:[>" ) <EOL> hint = self . scan_simple_string ( ) <EOL> self . skip_whitespace ( ) <EOL> self . skip_char ( b"<STR_LIT:]>" ) <EOL> self . skip_whitespace ( ) <EOL> out = self . scan_simple_string ( ) <EOL> return ( hint , out ) if hint else out <EOL> def scan_list ( self ) : <EOL> out = [ ] <EOL> self . skip_char ( b"<STR_LIT:(>" ) <EOL> while True : <EOL> self . skip_whitespace ( ) <EOL> if not self . char : <EOL> raise ValueError ( "<STR_LIT>" ) <EOL> elif self . char == b"<STR_LIT:)>" : <EOL> self . skip_char ( b"<STR_LIT:)>" ) <EOL> return out <EOL> else : <EOL> out . append ( self . scan_object ( ) ) <EOL> def scan_object ( self ) : <EOL> """<STR_LIT>""" <EOL> self . skip_whitespace ( ) <EOL> if not self . char : <EOL> out = None <EOL> elif self . char == b"<STR_LIT:{>" : <EOL> self . bytesize = <NUM_LIT:6> <EOL> self . skip_char ( b"<STR_LIT:{>" ) <EOL> out = self . scan_object ( ) <EOL> self . skip_char ( b"<STR_LIT:}>" ) <EOL> elif self . char == b"<STR_LIT:(>" : <EOL> out = self . scan_list ( ) <EOL> else : <EOL> out = self . scan_string ( ) <EOL> return out <EOL> def load ( buf ) : <EOL> out = list ( SexpParser ( buf ) ) <EOL> if not out : <EOL> return None <EOL> elif len ( out ) == <NUM_LIT:1> : <EOL> return out [ <NUM_LIT:0> ] <EOL> else : <EOL> return out <EOL> def dump ( obj , canonical = False , transport = False ) : <EOL> if transport : <EOL> canonical = True <EOL> if isinstance ( obj , ( str , bytes ) ) : <EOL> exp = dump_string ( obj , canonical ) <EOL> elif isinstance ( obj , dict ) : <EOL> exp = dump_list ( obj . items ( ) , canonical ) <EOL> elif isinstance ( obj , ( list , tuple ) ) : <EOL> exp = dump_list ( obj , canonical ) <EOL> elif isinstance ( obj , int ) : <EOL> exp = dump_string ( str ( obj ) , canonical ) <EOL> else : <EOL> raise TypeError ( "<STR_LIT>" % ( type ( obj ) , obj ) ) <EOL> if transport : <EOL> return b"<STR_LIT:{>" + base64 . b64encode ( exp ) + b"<STR_LIT:}>" <EOL> else : <EOL> return exp <EOL> def dump_string ( obj , canonical = False , hex = False , hint = None ) : <EOL> if hasattr ( obj , "<STR_LIT>" ) : <EOL> obj = obj . encode ( "<STR_LIT:utf-8>" ) <EOL> if canonical : <EOL> out = ( "<STR_LIT>" % len ( obj ) ) . encode ( "<STR_LIT:utf-8>" ) + obj <EOL> elif is_token ( obj ) : <EOL> out = bytes ( obj ) <EOL> elif is_quoteable ( obj ) : <EOL> out = bytearray ( b'<STR_LIT:">' ) <EOL> for char in obj : <EOL> if char in ESCAPE_CHARS : <EOL> out += b"<STR_LIT:\\>" <EOL> out += ESCAPE_CHARS [ char ] <EOL> elif char in b"<STR_LIT>" : <EOL> out += b"<STR_LIT:\\>" <EOL> out . append ( char ) <EOL> else : <EOL> out . append ( char ) <EOL> out += b'<STR_LIT:">' <EOL> out = bytes ( out ) <EOL> elif hex : <EOL> out = b"<STR_LIT:#>" + obj . encode ( "<STR_LIT>" ) + b"<STR_LIT:#>" <EOL> else : <EOL> out = b"<STR_LIT:|>" + base64 . b64encode ( obj ) + b"<STR_LIT:|>" <EOL> if hint : <EOL> return b"<STR_LIT:[>" + dump_string ( hint , canonical , hex , None ) + b"<STR_LIT:]>" + out <EOL> else : <EOL> return out <EOL> def dump_hint ( obj , canonical = False ) : <EOL> return b"<STR_LIT:[>" + dump_string ( obj , canonical ) + b"<STR_LIT:]>" <EOL> def dump_list ( obj , canonical = False ) : <EOL> out = b"<STR_LIT:(>" <EOL> if canonical : <EOL> out += b"<STR_LIT>" . join ( dump ( x , canonical = True ) for x in obj ) <EOL> else : <EOL> out += b"<STR_LIT:U+0020>" . join ( dump ( x ) for x in obj ) <EOL> out += b"<STR_LIT:)>" <EOL> return out <EOL> def to_int ( buf ) : <EOL> num = <NUM_LIT:0> <EOL> for byte in buf : <EOL> num <<= <NUM_LIT:8> <EOL> num |= ord ( byte ) <EOL> return num <EOL> def is_token ( string ) : <EOL> if string [ <NUM_LIT:0> ] in DIGITS : <EOL> return False <EOL> for char in string : <EOL> if char not in TOKEN_CHARS : <EOL> return False <EOL> return True <EOL> def is_quoteable ( string ) : <EOL> for char in string : <EOL> if char in VERBATIM : <EOL> return False <EOL> elif char in PRINTABLE_CHARS : <EOL> pass <EOL> elif char in ESCAPE_CHARS : <EOL> pass <EOL> else : <EOL> return False <EOL> return True </s>
