cobiz / src /hwp5 /binspec.py
seawolf2357's picture
Add src
d94b56e verified
# -*- coding: utf-8 -*-
#
# pyhwp : hwp file format parser in python
# Copyright (C) 2010-2023 mete0r <https://github.com/mete0r>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
'''Generate HWPv5 Binary Spec Document
Usage::
hwp5spec xml [--loglevel=<loglevel>]
hwp5spec -h | --help
hwp5spec --version
Options::
-h --help Show this screen
--version Show version
--loglevel=<loglevel> Set log level [default: warning]
'''
import logging
import xml.etree.ElementTree as ET
logger = logging.getLogger(__name__)
def define_enum_type(enum_type):
attrs = dict(name=enum_type.__name__)
if enum_type.scoping_struct:
attrs['scope'] = enum_type.scoping_struct.__name__
elem = ET.Element('EnumType', attrs)
value_names = list((e, e.name) for e in enum_type.instances)
value_names.sort()
for value, name in value_names:
item = ET.Element('item', dict(name=name, value=str(value)))
elem.append(item)
return elem
def define_bitfield(bitgroup_name, bitgroup_desc):
attrs = dict(name=bitgroup_name,
lsb=str(bitgroup_desc.lsb),
msb=str(bitgroup_desc.msb))
elem = ET.Element('BitField', attrs)
elem.append(reference_type(bitgroup_desc.valuetype))
return elem
def define_flags_type(flags_type):
elem = ET.Element('FlagsType')
from hwp5.dataio import BitGroupDescriptor
base = ET.SubElement(elem, 'base')
base.append(reference_type(flags_type.basetype))
bitgroups = flags_type.__dict__.items()
bitgroups = ((v.lsb, (k, v)) for k, v in bitgroups
if isinstance(v, BitGroupDescriptor))
bitgroups = list(bitgroups)
bitgroups.sort()
bitgroups = reversed(bitgroups)
bitgroups = ((k, v) for lsb, (k, v) in bitgroups)
bitgroups = (define_bitfield(k, v) for k, v in bitgroups)
for bitgroup in bitgroups:
elem.append(bitgroup)
return elem
def define_fixed_array_type(array_type):
attrs = dict()
attrs['size'] = str(array_type.size)
elem = ET.Element('FixedArrayType', attrs)
item_type_elem = ET.SubElement(elem, 'item-type')
item_type_elem.append(reference_type(array_type.itemtype))
return elem
def define_variable_length_array_type(array_type):
elem = ET.Element('VariableLengthArrayType')
count_type_elem = ET.SubElement(elem, 'count-type')
count_type_elem.append(reference_type(array_type.counttype))
item_type_elem = ET.SubElement(elem, 'item-type')
item_type_elem.append(reference_type(array_type.itemtype))
return elem
def define_x_array_type(t):
elem = ET.Element('XArrayType', dict(size=t.count_reference.__doc__))
item_type_elem = ET.SubElement(elem, 'item-type')
item_type_elem.append(reference_type(t.itemtype))
return elem
def define_selective_type(t):
elem = ET.Element('SelectiveType',
dict(selector=t.selector_reference.__doc__))
for k, v in t.selections.items():
sel = ET.SubElement(elem, 'selection',
dict(when=make_condition_value(k)))
sel.append(reference_type(v))
return elem
def reference_type(t):
attrs = dict()
attrs['name'] = t.__name__
attrs['meta'] = type(t).__name__
elem = ET.Element('type-ref', attrs)
from hwp5.dataio import EnumType
from hwp5.dataio import FlagsType
from hwp5.dataio import FixedArrayType
from hwp5.dataio import X_ARRAY
from hwp5.dataio import VariableLengthArrayType
from hwp5.dataio import SelectiveType
if isinstance(t, EnumType):
if t.scoping_struct:
elem.attrib['scope'] = t.scoping_struct.__name__
elif isinstance(t, FlagsType):
elem.append(define_flags_type(t))
elif isinstance(t, FixedArrayType):
elem.append(define_fixed_array_type(t))
elif isinstance(t, X_ARRAY):
elem.append(define_x_array_type(t))
elif isinstance(t, VariableLengthArrayType):
elem.append(define_variable_length_array_type(t))
elif isinstance(t, SelectiveType):
elem.append(define_selective_type(t))
return elem
def referenced_types_by_member(member):
t = member.get('type')
if t:
yield t
for x in direct_referenced_types(t):
yield x
def define_member(struct_type, member):
attrs = dict(name=member['name'])
version = member.get('version')
if version:
version = '.'.join(str(x) for x in version)
attrs['version'] = version
elem = ET.Element('member', attrs)
t = member.get('type')
if t:
elem.append(reference_type(t))
condition = member.get('condition')
if condition:
condition = condition.__doc__ or condition.__name__ or ''
condition = condition.strip()
condition_elem = ET.Element('condition')
condition_elem.text = condition
elem.append(condition_elem)
return elem
def direct_referenced_types(t):
from hwp5.dataio import FlagsType
from hwp5.dataio import FixedArrayType
from hwp5.dataio import X_ARRAY
from hwp5.dataio import VariableLengthArrayType
from hwp5.dataio import StructType
from hwp5.dataio import SelectiveType
if isinstance(t, FlagsType):
for k, desc in t.bitfields.items():
yield desc.valuetype
elif isinstance(t, FixedArrayType):
yield t.itemtype
elif isinstance(t, X_ARRAY):
yield t.itemtype
elif isinstance(t, VariableLengthArrayType):
yield t.counttype
yield t.itemtype
elif isinstance(t, StructType):
if 'members' in t.__dict__:
for member in t.members:
for x in referenced_types_by_member(member):
yield x
elif isinstance(t, SelectiveType):
for selection in t.selections.values():
yield selection
def referenced_types_by_struct_type(t):
if 'members' in t.__dict__:
for member in t.members:
for x in referenced_types_by_member(member):
yield x
def extension_sort_key(cls):
import inspect
key = inspect.getmro(cls)
key = list(x.__name__ for x in key)
key = tuple(reversed(key))
return key
def sort_extensions(extension_types):
extension_types = extension_types.items()
extension_types = list((extension_sort_key(cls), (k, cls))
for k, cls in extension_types)
extension_types.sort()
extension_types = ((k, cls) for sort_key, (k, cls) in extension_types)
return extension_types
def extensions_of_tag_model(tag_model):
extension_types = getattr(tag_model, 'extension_types', None)
if extension_types:
extension_types = sort_extensions(extension_types)
key_condition = getattr(tag_model, 'get_extension_key', None)
key_condition = key_condition.__doc__.strip()
for key, extension_type in extension_types:
yield (key_condition, key), extension_type
def define_struct_type(t):
elem = ET.Element('StructType',
dict(name=t.__name__))
for extend in get_extends(t):
elem.append(define_extends(extend))
if 'members' in t.__dict__:
for member in t.members:
elem.append(define_member(t, member))
return elem
def define_tag_model(tag_id):
from hwp5.tagids import tagnames
from hwp5.binmodel import tag_models
tag_name = tagnames[tag_id]
tag_model = tag_models[tag_id]
elem = ET.Element('TagModel',
dict(tag_id=str(tag_id),
name=tag_name))
elem.append(define_base_type(tag_model))
for (name, value), extension_type in extensions_of_tag_model(tag_model):
elem.append(define_extension(extension_type,
tag_model,
name,
value))
return elem
def define_base_type(t):
elem = ET.Element('base', dict(name=t.__name__))
return elem
def make_condition_value(value):
from hwp5.dataio import EnumType
if isinstance(value, tuple):
value = tuple(make_condition_value(v) for v in value)
return '('+', '.join(value)+')'
elif isinstance(type(value), EnumType):
return repr(value)
elif isinstance(value, type):
return value.__name__
else:
return str(value)
def define_extension(t, up_to_type, name, value):
attrs = dict(name=t.__name__)
elem = ET.Element('extension', attrs)
condition = ET.Element('condition')
condition.text = name + ' == ' + make_condition_value(value)
elem.append(condition)
for extend in get_extends(t, up_to_type):
elem.append(define_extends(extend))
if 'members' in t.__dict__:
for member in t.members:
elem.append(define_member(t, member))
return elem
def get_extends(t, up_to_type=None):
def take_up_to(up_to_type, mro):
for t in mro:
yield t
if t is up_to_type:
return
from itertools import takewhile
import inspect
mro = inspect.getmro(t)
mro = mro[1:] # exclude self
# mro = take_up_to(up_to_type, mro)
mro = takewhile(lambda cls: cls is not up_to_type, mro)
mro = (t for t in mro if 'members' in t.__dict__)
mro = list(mro)
mro = reversed(mro)
return mro
def define_extends(t):
attrs = dict(name=t.__name__)
elem = ET.Element('extends', attrs)
return elem
def define_primitive_type(t):
attrs = dict(name=t.__name__)
fixed_size = getattr(t, 'fixed_size', None)
if fixed_size:
attrs['size'] = str(fixed_size)
elem = ET.Element('PrimitiveType', attrs)
binfmt = getattr(t, 'binfmt', None)
if binfmt:
binfmt_elem = ET.Element('binfmt')
binfmt_elem.text = binfmt
elem.append(binfmt_elem)
return elem
def main():
from docopt import docopt
from hwp5 import __version__
from hwp5.proc import rest_to_docopt
doc = rest_to_docopt(__doc__)
args = docopt(doc, version=__version__)
if '--loglevel' in args:
loglevel = args['--loglevel'].lower()
loglevel = dict(error=logging.ERROR,
warning=logging.WARNING,
info=logging.INFO,
debug=logging.DEBUG).get(loglevel, logging.WARNING)
logger.setLevel(loglevel)
logger.addHandler(logging.StreamHandler())
from hwp5 import binmodel
import sys
enum_types = set()
extensions = set()
struct_types = set()
primitive_types = set()
root = ET.Element('binspec', dict(version=__version__))
for tag_id, tag_model in binmodel.tag_models.items():
logger.debug('TAG_MODEL: %s', tag_model.__name__)
root.append(define_tag_model(tag_id))
struct_types.add(tag_model)
from hwp5.dataio import EnumType
from hwp5.dataio import StructType
from hwp5.dataio import PrimitiveType
for t in referenced_types_by_struct_type(tag_model):
if isinstance(t, EnumType):
enum_types.add(t)
if isinstance(t, StructType):
struct_types.add(t)
if isinstance(t, PrimitiveType):
logger.debug('- PrimitiveType: %s', t.__name__)
primitive_types.add(t)
for _, t in extensions_of_tag_model(tag_model):
extensions.add(t)
for t in extensions:
struct_types.add(t)
for extends in get_extends(t):
struct_types.add(extends)
for struct_type in struct_types:
for t in referenced_types_by_struct_type(struct_type):
if isinstance(t, EnumType):
enum_types.add(t)
if isinstance(t, PrimitiveType):
primitive_types.add(t)
enum_types = list((t.__name__, t) for t in enum_types)
enum_types.sort()
enum_types = (t for name, t in enum_types)
for t in enum_types:
root.append(define_enum_type(t))
struct_types = list((t.__name__, t) for t in struct_types)
struct_types.sort()
struct_types = (t for name, t in struct_types)
for t in struct_types:
root.append(define_struct_type(t))
primitive_types = list((t.__name__, t) for t in primitive_types)
primitive_types.sort()
primitive_types = (t for name, t in primitive_types)
for t in primitive_types:
root.append(define_primitive_type(t))
doc = ET.ElementTree(root)
doc.write(sys.stdout, 'utf-8')