2018年8月3日金曜日

開発環境

  • 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 コメント:

コメントを投稿