1
0
mirror of https://github.com/danog/tl-schema.git synced 2024-11-26 20:04:52 +01:00
tl-schema/utils/converter.py
2017-07-03 11:28:47 +03:00

223 lines
6.4 KiB
Python
Executable File

#!/usr/bin/env python3
# From https://gist.github.com/stek29/067ce1d4d764147e21cc0667ba5c063d
# for json
import json
from collections import OrderedDict
# for tl
import re
import struct
import binascii
__all__ = [
'TLToken',
'TLTokenJSON',
'TLTokenTL',
'TLTokenFileTL',
'TLTokenFileJSON'
]
class TLToken:
def __init__(self, name, crc, params, result, is_method=None):
# name
self.name = str(name)
# aka id, should be already "converted" to int8 (struct.unpack'd for example)
self.crc = int(crc)
# [{'name': 'foo', 'type': 'long'}, {'name': 'bar', 'type': 'flags.1?int'}, ...]
self.params = list(params)
# type
self.result = str(result)
if is_method is None:
is_method = '.' in name
self.is_method = is_method
def __str__(self):
return 'TLToken: ' + self.name
class TLTokenJSON(TLToken):
def __init__(self, json_obj):
if not isinstance(json_obj, dict):
json_obj = json.loads(json_obj)
is_method = 'method' in json_obj
super().__init__(
name=json_obj['method' if is_method else 'predicate'],
crc=json_obj['id'],
params=json_obj['params'],
result=json_obj['type'],
is_method=is_method
)
@staticmethod
def dump(inst, as_string=False):
if not isinstance(inst.crc, int):
raise Exception('lol kek cheburek')
dic = OrderedDict((
('id', str(inst.crc)),
('method' if inst.is_method else 'predicate', inst.name),
('params', inst.params),
('type', inst.result)
))
return dic if not as_string else json.dumps(dic)
def __repr__(self):
return self.dump(self, True)
VECTOR = 'vector#1cb5c415'
TL_line_regex = re.compile(r"([a-zA-Z\.0-9_]+)#([0-9a-f]+)([^=]*)=\s*([a-zA-Z\.<>0-9_]+);")
TL_param_regex = re.compile(r"\s([^:]+):(\S+)")
class TLTokenTL(TLToken):
def __init__(self, tl_line, is_method=None):
line = TL_line_regex.match(tl_line)
crc = struct.unpack('>i',
binascii.a2b_hex(
'{:>08}'.format(line.group(2))
)
)[0]
params = [
{'name': name, 'type': type}
for name, type in
TL_param_regex.findall(line.group(3))
if type != 'Type}'
]
super().__init__(
name=line.group(1),
crc=crc,
params=params,
result=line.group(4),
is_method=is_method
)
@staticmethod
def dump(inst):
return '{name}#{crc} {params} = {result};'.format(
name=inst.name,
crc=TLTokenTL.crc2hex(inst.crc),
params=' '.join('{name}:{type}'.format(**param) for param in inst.params),
result=inst.result
).replace(' ', ' ')
@staticmethod
def crc2hex(crc: int):
return str(binascii.b2a_hex(struct.pack('>i', crc)), encoding='ascii').lstrip('0')
def __repr__(self):
return self.dump(self)
class TLTokenFileTL:
@staticmethod
def read(file, no_verify=True):
def repack(line):
return re.sub(r'#0+', '#', ' '.join(re.split(r' *', line)))
def verify(line, token, is_method):
tl_line = repr(token)
return repack(line) == repack(tl_line)
tokens = list()
methods_now = False
for l in file:
l = l.strip()
if not l or l.startswith('//'):
# comment
continue
elif l == '---functions---':
methods_now = True
continue
elif l == '---types---':
methods_now = False
continue
elif l.startswith(VECTOR):
# vector's line isn't supported, and has to be skipped
continue
else:
try:
token = TLTokenTL(l, methods_now)
if no_verify or verify(l, token, methods_now):
tokens.append(token)
else:
print('MISMATCH:')
print(l)
print(repack(repr(token)), end='\n\n')
except Exception as e:
print('ERROR:')
print(e)
print(l, end='\n\n')
return tokens
@staticmethod
def write(file, lst):
file.write('---types---\n')
for token in lst:
if not token.is_method:
file.write(TLTokenTL.dump(token))
file.write('\n')
file.write('---functions---\n')
for token in lst:
if token.is_method:
file.write(TLTokenTL.dump(token))
file.write('\n')
class TLTokenFileJSON:
@staticmethod
def read(file, no_verify=True):
def process(inp, out, method):
for token in inp:
try:
out.append(TLTokenJSON(token))
assert no_verify or (out[-1].is_method == method)
except Exception as e:
print('ERROR:')
print(token)
print(e, end='\n\n')
data = json.load(file)
constructors = list()
process(data['constructors'], constructors, False)
methods = list()
process(data['methods'], methods, True)
return constructors + methods
def write(file, lst):
json.dump(
OrderedDict((
('constructors', [TLTokenJSON.dump(token, as_string=False) for token in lst if not token.is_method]),
('methods', [TLTokenJSON.dump(token, as_string=False) for token in lst if token.is_method])
)),
file,
indent=2)
"""
Usage example -- converting schema from TL to JSON:
from tl_master import *
with open('schema.tl') as tl, open('schema.json', 'w') as json:
TLTokenFileJSON.write(json, TLTokenFileTL.read(tl))
"""
from sys import argv
if argv[1].endswith('.tl'):
fromf = TLTokenFileTL
tof = TLTokenFileJSON
outn = argv[1].replace('tl', 'json')
elif argv[1].endswith('.json'):
fromf = TLTokenFileJSON
tof = TLTokenFileTL
outn = argv[1].replace('json', 'tl')
with open(argv[1]) as inf, open(outn, 'w') as outf:
tof.write(outf, fromf.read(inf))