|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'''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:] |
|
|
|
|
|
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') |
|
|
|