開発環境
- OS: macOS High Sierra - Apple
- Text Editor: Emacs
- プログラミング言語: Python3
- モジュール: sion (GitHub)
- ANTLR4(parser generator)
load、loads、dump、dumps関数でtype関数を利用していたのを、isinstance関数を利用するように変更。この変更によって、SIONの型を対応させたPythonの型(None、bool、int、float、datetime.datetime、list、tuple、dict)を継承した派生クラスのオブジェクトも扱えるようにしてみた。
sion.py
# Created by kamimura on 2018/07/21. # Copyright © 2018 kamimura. All rights reserved. import sys import datetime from antlr4 import * from SIONLexer import SIONLexer from SIONParser import SIONParser from SIONVisitor import SIONVisitor def load(file, encoding: str='utf-8', errors: str='strict') -> object: data = file.read() if isinstance(data, (bytes, bytearray)): data = data.decode(encoding, errors) stream = InputStream(data) lexer = SIONLexer(stream) tokens = CommonTokenStream(lexer) parser = SIONParser(tokens) tree = parser.si_self() visitor = SIONVisitor() return visitor.visit(tree) def loads(s): if isinstance(s, (bytes, bytearray)): s = s.decode() stream = InputStream(s) lexer = SIONLexer(stream) tokens = CommonTokenStream(lexer) parser = SIONParser(tokens) tree = parser.si_self() visitor = SIONVisitor() return visitor.visit(tree) def str_esc(s): for o, n in [('"', '\\"'), ('\n', '\\n'), ('\r', '\\r'), ('\\', '\\\\')]: s = s.replace(o, n) return s def dump(obj, file): if obj is None: print('nil', file=file, end='') elif isinstance(obj, bool): if obj: print('ture', file=file, end='') else: print('false', file=file, end='') elif isinstance(obj, (int, float)): print(obj, file=file, end='') elif isinstance(obj, str): print(f'"{str_esc(obj)}"', file=file, end='') elif isinstance(obj, (bytes, bytearray)): print(f'.Data("{str(obj)[2:-1]}")', file=file, end='') elif isinstance(obj, datetime.datetime): print(f'.Date({obj.timestamp()})', file=file, end='') elif isinstance(obj, (list, tuple)): print(f'[', file=file, end='') if len(obj) > 0: for o in obj[:-1]: dump(o, file) print(',', file=file, end='') dump(obj[-1], file) print(']', file=file, end='') elif isinstance(obj, dict): print('[', file=file, end='') ks = list(obj.keys()) if len(ks) == 0: print(':', file=file, end='') elif len(ks) == 1: dump(ks[0], file) print(':', file=file, end='') dump(obj[ks[0]], file) else: for k in ks[:-1]: dump(k, file) print(':', file=file, end='') dump(obj[k], file) print(',', file=file, end='') dump(ks[-1], file) print(':', file=file, end='') dump(obj[ks[-1]], file) print(']', file=file, end='') else: raise TypeError( f"Object of type '{obj.__class__.__name__}' is not SION serializable") def dumps(obj: object): if obj is None: return 'nil' if isinstance(obj, bool): if obj: return 'true' return 'false' if isinstance(obj, (int, float)): return str(obj) if isinstance(obj, str): return f'"{str_esc(obj)}"' if isinstance(obj, (bytes, bytearray)): return f'.Data("{str(obj)[2:-1]}")' if isinstance(obj, datetime.datetime): return f'.Date({obj.timestamp(obj)})' if isinstance(obj, (list, tuple)): res = '[' if len(obj) > 0: for o in obj[:-1]: res += dumps(o) + ',' res += dumps(obj[-1]) res += ']' return res if isinstance(obj, dict): res = '[' ks = list(obj.keys()) if len(ks) == 0: res += ':' elif len(ks) == 1: res += dumps(ks[0]) + ':' + dumps(obj[ks[0]]) else: for k in ks[:-1]: res += dumps(k) + ':' + str(obj[k]) + ',' res += dumps(ks[-1]) + ':' + dumps(obj[ks[-1]]) res += ']' return res raise TypeError( f"Object of type '{obj.__class__.__name__}' is not SION serializable") if __name__ == '__main__': import pprint if len(sys.argv) > 1: filename = sys.argv[1] else: filename = '../test/t.sion' with open(filename) as f: obj = load(f) pprint.pprint(obj) with open('../test/output.sion', 'w') as f: dump(obj, f) s = ''' [ "array" : [ nil, true, 1, // Int in decimal 1.0, // Double in decimal "one", [1], ["one" : 1.0] ], "bool" : true, "data" : .Data("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"), "date" : .Date(0x0p+0), "dictionary" : [ "array" : [], "bool" : false, "double" : 0x0p+0, "int" : 0, "nil" : nil, "object" : [:], "string" : "" ], "double" : 0x1.518f5c28f5c29p+5, // Double in hexadecimal "int" : -0x2a, // Int in hexadecimal "nil" : nil, "string" : "漢字、カタカナ、ひらがなの入ったstring😇", "url" : "https://github.com/dankogai/", nil : "Unlike JSON and Property Lists,", true : "Yes, SION", 1 : "does accept", 1.0 : "non-String keys.", [] : "like", [:] : "Map of ECMAScript." ] ''' obj = loads(s) pprint.pprint(obj) s = dumps(obj) print(s)
入出力結果(Terminal, Jupyter(IPython))
$ ./test.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.013s OK $ python3 sion.py {None: 'Unlike JSON and Property Lists,', True: 'non-String keys.', 'array': [None, True, 1, 1.0, 'one', [1], {'one': 1.0}], 'bool': True, 'data': b'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'date': datetime.datetime(1970, 1, 1, 9, 0), 'dictionary': {'array': [], 'bool': False, 'double': 0.0, 'int': 0, 'nil': None, 'object': {}, 'string': ''}, 'double': 42.195, 'int': -42, 'nil': None, 'string': '漢字、カタカナ、ひらがなの入ったstring😇', 'url': 'https://github.com/dankogai/', (): 'Map of ECMAScript.'} {None: 'Unlike JSON and Property Lists,', True: 'non-String keys.', 'array': [None, True, 1, 1.0, 'one', [1], {'one': 1.0}], 'bool': True, 'data': b'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'date': datetime.datetime(1970, 1, 1, 9, 0), 'dictionary': {'array': [], 'bool': False, 'double': 0.0, 'int': 0, 'nil': None, 'object': {}, 'string': ''}, 'double': 42.195, 'int': -42, 'nil': None, 'string': '漢字、カタカナ、ひらがなの入ったstring😇', 'url': 'https://github.com/dankogai/', (): 'Map of ECMAScript.'} ["array":[None, True, 1, 1.0, 'one', [1], {'one': 1.0}],"bool":True,"data":b'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',"date":1970-01-01 09:00:00,"dictionary":{'array': [], 'bool': False, 'double': 0.0, 'int': 0, 'nil': None, 'object': {}, 'string': ''},"double":42.195,"int":-42,"nil":None,"string":漢字、カタカナ、ひらがなの入ったstring😇,"url":https://github.com/dankogai/,nil:Unlike JSON and Property Lists,,true:non-String keys.,[]:"Map of ECMAScript."] $
0 コメント:
コメントを投稿