From edc0af7b96e1537c0cec309272abad6996789d05 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 28 Jun 2017 15:55:35 -0400 Subject: [PATCH 01/27] Update nifxml submodule --- nifxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nifxml b/nifxml index 959cb9c..c360a1b 160000 --- a/nifxml +++ b/nifxml @@ -1 +1 @@ -Subproject commit 959cb9c3dd59a319e60e819fc8a1402f821f3684 +Subproject commit c360a1b9db5d52237df018c2f631dea34fa34045 From 7d7cc381125039e89f84b92b6d17f236fc10fe59 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 28 Jun 2017 15:56:30 -0400 Subject: [PATCH 02/27] Prefix/suffix attribute support for nif.xml See: https://github.com/niftools/nifxml/pull/62 --- gen_niflib.py | 6 +++--- nifxml.py | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index f7ce49f..6dc4ebf 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -546,7 +546,7 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() for n in block_names: x = block_types[n] - out.code( 'ObjectRegistry::RegisterObject( "' + x.cname + '", ' + x.cname + '::Create );' ) + out.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' ) out.code() out.code( '}' ) out.code( '}' ) @@ -736,9 +736,9 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code( '//Definition of TYPE constant' ) if x.inherit: - out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &' + x.inherit.cname + '::TYPE );' ) + out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &' + x.inherit.cname + '::TYPE );' ) else: - out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.cname + '\", &RefObject::TYPE );' ) + out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &RefObject::TYPE );' ) out.code() x_code_construct = x.code_construct() if x_code_construct: diff --git a/nifxml.py b/nifxml.py index 9559ddb..bc25672 100644 --- a/nifxml.py +++ b/nifxml.py @@ -1250,6 +1250,7 @@ def __init__(self, element): # member attributes self.name = element.getAttribute('name') + self.suffix = element.getAttribute('suffix') self.type = element.getAttribute('type') self.arg = element.getAttribute('arg') self.template = element.getAttribute('template') @@ -1326,7 +1327,7 @@ def __init__(self, element): while sis: if sis.nodeType == Node.ELEMENT_NODE: sis_name = sis.getAttribute('name') - if sis_name == self.name: + if sis_name == self.name and not self.suffix: self.is_duplicate = True sis_arr1 = Expr(sis.getAttribute('arr1')) sis_arr2 = Expr(sis.getAttribute('arr2')) @@ -1354,7 +1355,7 @@ def __init__(self, element): sis = sis.nextSibling # C++ names - self.cname = member_name(self.name) + self.cname = member_name(self.name if not self.suffix else self.name + "_" + self.suffix) self.ctype = class_name(self.type) self.carg = member_name(self.arg) self.ctemplate = class_name(self.template) @@ -1501,6 +1502,7 @@ def __init__(self, element): Basic.__init__(self, element) self.storage = element.getAttribute('storage') + self.prefix = element.getAttribute('prefix') #find the Niflib type of the storage self.storage = basic_types[self.storage].niflibtype self.description = element.firstChild.nodeValue.strip() @@ -1510,6 +1512,8 @@ def __init__(self, element): # Locate all special enumeration options for option in element.getElementsByTagName('option'): + if self.prefix and option.hasAttribute('name'): + option.setAttribute('name', self.prefix + "_" + option.getAttribute('name')) x = Option(option) self.options.append(x) From a38d49f4370391a4726eaeb7d7d1584e7f381f97 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 28 Jun 2017 17:44:49 -0400 Subject: [PATCH 03/27] Update for userver2 attribute --- nifxml.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nifxml.py b/nifxml.py index bc25672..e087bf1 100644 --- a/nifxml.py +++ b/nifxml.py @@ -301,6 +301,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", lastver1 = None lastver2 = None lastuserver = None + lastuserver2 = None lastcond = None lastvercond = None # stream name @@ -446,10 +447,10 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", y_cond = y.cond.code(y_cond_prefix) y_vercond = y.vercond.code('info.') if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: - if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastvercond != y_vercond: + if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastuserver2 != y.userver2 or lastvercond != y_vercond: # we must switch to a new version block # close old version block - if lastver1 or lastver2 or lastuserver or lastvercond: self.code("};") + if lastver1 or lastver2 or lastuserver or lastuserver2 or lastvercond: self.code("};") # close old condition block as well if lastcond: self.code("};") @@ -467,6 +468,9 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if y.userver != None: verexpr = "%s%s( info.userVersion == %s )"%(verexpr, concat, y.userver) concat = " && " + if y.userver2 != None: + verexpr = "%s%s( info.userVersion2 == %s )"%(verexpr, concat, y.userver2) + concat = " && " if y_vercond: verexpr = "%s%s( %s )"%(verexpr, concat, y_vercond) if verexpr: @@ -627,11 +631,12 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", lastver1 = y.ver1 lastver2 = y.ver2 lastuserver = y.userver + lastuserver2 = y.userver2 lastcond = y_cond lastvercond = y_vercond if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: - if lastver1 or lastver2 or not(lastuserver is None) or lastvercond: + if lastver1 or lastver2 or not(lastuserver is None) or not(lastuserver2 is None) or lastvercond: self.code("};") if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_OUT]: if lastcond: @@ -1195,6 +1200,8 @@ class Member: @type ver2: string @ivar userver: The user version where this member exists. Comes from the "userver" attribute of the tag. @type userver: string + @ivar userver2: The user version 2 where this member exists. Comes from the "userver2" attribute of the tag. + @type userver2: string @ivar vercond: The version condition of this member variable. Comes from the "vercond" attribute of the tag. @type vercond: Eval @ivar is_public: Whether this member will be declared public. Comes from the "public" attribute of the tag. @@ -1264,6 +1271,7 @@ def __init__(self, element): self.ver1 = version2number(element.getAttribute('ver1')) self.ver2 = version2number(element.getAttribute('ver2')) self.userver = userversion2number(element.getAttribute('userver')) + self.userver2 = userversion2number(element.getAttribute('userver2')) self.vercond = Expr(element.getAttribute('vercond')) self.is_public = (element.getAttribute('public') == "1") self.next_dup = None From 318ca605689ec13122140cfbf2a35f504f00f2f6 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 28 Jun 2017 17:45:51 -0400 Subject: [PATCH 04/27] Update for colons in niobject names --- nifxml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nifxml.py b/nifxml.py index e087bf1..8e1333a 100644 --- a/nifxml.py +++ b/nifxml.py @@ -674,7 +674,7 @@ def class_name(n): try: return native_types[n] except KeyError: - return n.replace(' ', '_') + return n.replace(' ', '_').replace(":", "_") if n == None: return None try: @@ -1170,7 +1170,7 @@ def __init__(self, element): self.description = element.firstChild.nodeValue.strip() else: self.description = self.name - self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_") + self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_").replace(":", "_") class Member: """ From 98356ae65e2b2d5c74c129764e20aa63b9de9492 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 28 Jun 2017 18:35:42 -0400 Subject: [PATCH 05/27] Add mult and div operators --- nifxml.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nifxml.py b/nifxml.py index 8e1333a..bd51d58 100644 --- a/nifxml.py +++ b/nifxml.py @@ -847,7 +847,7 @@ class Expression(object): >>> bool(Expression('1 != 1').eval()) False """ - operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<' ] + operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<', '/', '*' ] def __init__(self, expr_str, name_filter = None): self._code = expr_str left, self._op, right = self._partition(expr_str) @@ -899,6 +899,10 @@ def eval(self, data = None): return left - right elif self._op == '+': return left + right + elif self._op == '/': + return left / right + elif self._op == '*': + return left * right elif self._op == '!': return not left else: From 34e8324a92de2c3e82ecc0f1b427e96d0e9d40f1 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 26 Jul 2017 08:43:56 -0400 Subject: [PATCH 06/27] Attempt cast for duplicate names with differing types On several occasions the newer nif.xml now has version-exclusive names which are identical yet have different types. Such as uint in one version and ushort in another. Adding the cast to read/write will adjust the read/write size appropriately. Note this does not fix all duplicate names, just the ones where a cast during read/write is all that is necessary. The first type also has to be the largest as declaration for the type depends on the first name, and subsequent smaller types will reuse that name. --- nifxml.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nifxml.py b/nifxml.py index bd51d58..8c19520 100644 --- a/nifxml.py +++ b/nifxml.py @@ -568,7 +568,10 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", self.code("};") # the usual thing elif not y.arg: - self.code("NifStream( %s, %s, info );"%(z, stream)) + cast = "" + if ( y.is_duplicate ): + cast = "(%s&)" % y.ctype + self.code("NifStream( %s%s, %s, info );"%(cast, z, stream)) else: self.code("NifStream( %s, %s, info, %s%s );"%(z, stream, y_prefix, y.carg)) else: From de0f5e0d0ac070325f7dde9ecf0baa4dc2eebf42 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Fri, 6 Oct 2017 14:40:31 -0400 Subject: [PATCH 07/27] Support abstract members NiDataStream uses two members to store args extracted from the RTTI string for the name that are not actually read/written during I/O so the current generation for this object is incorrect. --- nifxml.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nifxml.py b/nifxml.py index 8c19520..ae2bb1b 100644 --- a/nifxml.py +++ b/nifxml.py @@ -554,7 +554,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: if (not subblock.is_link) and (not subblock.is_crossref): # not a ref - if action in [ACTION_READ, ACTION_WRITE]: + if action in [ACTION_READ, ACTION_WRITE] and y.is_abstract is False: # hack required for vector if y.type == "bool" and y.arr1.lhs: self.code("{"); @@ -1213,6 +1213,8 @@ class Member: @type vercond: Eval @ivar is_public: Whether this member will be declared public. Comes from the "public" attribute of the tag. @type is_public: string + @ivar is_abstract: Whether this member is abstract. This means that it does not factor into read/write. + @type is_abstract: bool @ivar description: The description of this member variable. Comes from the text between and . @type description: string @ivar uses_argument: Specifies whether this attribute uses an argument. @@ -1280,7 +1282,8 @@ def __init__(self, element): self.userver = userversion2number(element.getAttribute('userver')) self.userver2 = userversion2number(element.getAttribute('userver2')) self.vercond = Expr(element.getAttribute('vercond')) - self.is_public = (element.getAttribute('public') == "1") + self.is_public = (element.getAttribute('public') == "1") + self.is_abstract = (element.getAttribute('abstract') == "1") self.next_dup = None self.is_manual_update = False self.is_calculated = (element.getAttribute('calculated') == "1") From f98dc21e0a14951cb402ca277cd1926c00ab046f Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Fri, 6 Oct 2017 14:42:39 -0400 Subject: [PATCH 08/27] Nested compounds update For some reason nifxml.py lists a bunch of compounds which are often nested in order to write the toString code correctly. Added the new constraint descriptors to it. --- nifxml.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nifxml.py b/nifxml.py index ae2bb1b..1c55034 100644 --- a/nifxml.py +++ b/nifxml.py @@ -320,7 +320,8 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", self.code("stringstream out;") # declare array_output_count, only if it will actually be used for y in block.members: - if y.arr1.lhs or (y.ctype in ["BoundingVolume", "ByteArray", "KeyGroup"]): + if y.arr1.lhs or (y.ctype in ["BoundingVolume", "ByteArray", "KeyGroup", + "ConstraintData", "MalleableDescriptor","PrismaticDescriptor"]): self.code("unsigned int array_output_count = 0;") break if action == ACTION_GETREFS: From 1d2f395f9e3dd1d9c24e24a906c52f921082e153 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Tue, 21 Nov 2017 05:23:40 -0500 Subject: [PATCH 09/27] nifxml 0.9 niflibtype removal The mapping for XML type to niflib type is now internal instead of in nif.xml. Type names are synced to nifxml 0.9. --- gen_niflib.py | 3 +-- nifxml.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index 6dc4ebf..bb17988 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -232,8 +232,7 @@ def OverwriteIfChanged( original_file, candidate_file ): x = compound_types[n] # skip natively implemented types - if x.niflibtype: continue - if n[:3] == 'ns ': continue + if x.name in NATIVETYPES.keys(): continue if not GENALLFILES and not x.cname in GENBLOCKS: continue diff --git a/nifxml.py b/nifxml.py index 1c55034..52ddb81 100644 --- a/nifxml.py +++ b/nifxml.py @@ -114,6 +114,51 @@ block_names = [] version_names = [] +NATIVETYPES = { + 'bool' : 'bool', + 'byte' : 'byte', + 'uint' : 'unsigned int', + 'ulittle32' : 'unsigned int', + 'ushort' : 'unsigned short', + 'int' : 'int', + 'short' : 'short', + 'BlockTypeIndex' : 'unsigned short', + 'char' : 'byte', + 'FileVersion' : 'unsigned int', + 'Flags' : 'unsigned short', + 'float' : 'float', + 'hfloat' : 'hfloat', + 'HeaderString' : 'HeaderString', + 'LineString' : 'LineString', + 'Ptr' : '*', + 'Ref' : 'Ref', + 'StringOffset' : 'unsigned int', + 'StringIndex' : 'IndexString', + 'SizedString' : 'string', + 'string' : 'IndexString', + 'Color3' : 'Color3', + 'Color4' : 'Color4', + #'ByteColor3' : 'ByteColor3', # TODO: Niflib type + 'ByteColor4' : 'ByteColor4', + 'FilePath' : 'IndexString', + 'Vector3' : 'Vector3', + 'Vector4' : 'Vector4', + 'Quaternion' : 'Quaternion', + 'Matrix22' : 'Matrix22', + 'Matrix33' : 'Matrix33', + 'Matrix34' : 'Matrix34', + 'Matrix44' : 'Matrix44', + 'hkMatrix3' : 'InertiaMatrix', + 'ShortString' : 'ShortString', + 'Key' : 'Key', + 'QuatKey' : 'Key', + 'TexCoord' : 'TexCoord', + 'Triangle' : 'Triangle', + 'BSVertexData' : 'BSVertexData', + 'BSVertexDataSSE' : 'BSVertexData', + 'BSVertexDesc' : 'BSVertexDesc' +} + ACTION_READ = 0 ACTION_WRITE = 1 ACTION_OUT = 2 @@ -1491,7 +1536,7 @@ def __init__(self, element): self.name = element.getAttribute('name') assert(self.name) # debug self.cname = class_name(self.name) - self.niflibtype = element.getAttribute('niflibtype') + self.niflibtype = NATIVETYPES.get(self.name) if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE: self.description = element.firstChild.nodeValue.strip() else: From 03da4decaa55398ae4e9b5ff204beda5c705f133 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Tue, 21 Nov 2017 05:24:07 -0500 Subject: [PATCH 10/27] Remove duplicates from cpp file includes The list of includes was not first reduced to unique includes before writing, so the same file would be included over and over again. --- nifxml.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/nifxml.py b/nifxml.py index 52ddb81..1e7292c 100644 --- a/nifxml.py +++ b/nifxml.py @@ -1715,39 +1715,42 @@ def code_fwd_decl(self): return result - def code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None): + def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): if self.niflibtype: return "" if not usedirs: gen_dir = self.gen_file_prefix obj_dir = self.obj_file_prefix - result = "" + result = [] if self.name in compound_names: - result += '#include "%s%s.h"\n'%(gen_dir, self.cname) + result.append('#include "%s%s.h"\n'%(gen_dir, self.cname)) elif self.name in block_names: - result += '#include "%s%s.h"\n'%(obj_dir, self.cname) + result.append('#include "%s%s.h"\n'%(obj_dir, self.cname)) else: assert(False) # bug # include referenced blocks used_blocks = [] for y in self.members: if y.template in block_names and y.template != self.name: - file_name = "%s%s.h"%(obj_dir, y.ctemplate) + file_name = '#include "%s%s.h"\n'%(obj_dir, y.ctemplate) if file_name not in used_blocks: used_blocks.append( file_name ) if y.type in compound_names: subblock = compound_types[y.type] - result += subblock.code_include_cpp(True, gen_dir, obj_dir) + used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir)) for terminal in y.cond.get_terminals(): if terminal in block_types: - used_blocks.append("%s%s.h"%(obj_dir, terminal)) + used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal)) for file_name in sorted(set(used_blocks)): - result += '#include "%s"\n'%file_name + result.append(file_name) return result + def code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None): + return ''.join(self.code_include_cpp_set(True, gen_dir, obj_dir)) + # find member by name def find_member(self, name, inherit=False): for y in self.members: From 735de171650b53b626b06244e03da69b2b6005ac Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Tue, 21 Nov 2017 14:04:11 -0500 Subject: [PATCH 11/27] Correct handling of array_output_count for nested compounds Added Compound.has_arr() which recursively checks all members for an array size. This removes the need for a manually maintained list of compounds with arrays. --- nifxml.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/nifxml.py b/nifxml.py index 1e7292c..8d6a943 100644 --- a/nifxml.py +++ b/nifxml.py @@ -364,11 +364,9 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if action == ACTION_OUT: self.code("stringstream out;") # declare array_output_count, only if it will actually be used - for y in block.members: - if y.arr1.lhs or (y.ctype in ["BoundingVolume", "ByteArray", "KeyGroup", - "ConstraintData", "MalleableDescriptor","PrismaticDescriptor"]): - self.code("unsigned int array_output_count = 0;") - break + if block.has_arr(): + self.code("unsigned int array_output_count = 0;") + if action == ACTION_GETREFS: self.code("list > refs;") if action == ACTION_GETPTRS: @@ -1766,7 +1764,13 @@ def find_first_ref(self, name): elif y.arr2 and y.arr2.lhs == name: return y return None - + + # Tests recursively for members with an array size. + def has_arr(self): + for y in self.members: + if y.arr1.lhs or (y.type in compound_types and compound_types[y.type].has_arr()): + return True + return False class Block(Compound): def __init__(self, element): From 0a37f76be2682841556aa76d45b6273ec39e77d5 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sun, 17 Dec 2017 17:23:38 -0500 Subject: [PATCH 12/27] Python 3 support --- gen_niflib.py | 28 +++++++++-------- nifxml.py | 87 +++++++++++++++++++++++---------------------------- nifxml_doc.py | 30 ++++++++++-------- 3 files changed, 72 insertions(+), 73 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index bb17988..3095dc0 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -56,9 +56,11 @@ # ***** END LICENSE BLOCK ***** # -------------------------------------------------------------------------- +from __future__ import unicode_literals from nifxml import * from distutils.dir_util import mkpath import os +import io import hashlib import itertools @@ -129,7 +131,7 @@ def ExtractCustomCode( file_name ): custom_lines['DESTRUCTOR'].append( "\n" ) return custom_lines - f = file( file_name, 'r' ) + f = io.open(file_name, 'rt', 1, 'utf-8') lines = f.readlines() f.close() @@ -241,7 +243,7 @@ def OverwriteIfChanged( original_file, candidate_file ): file_name = ROOT_DIR + '/include/gen/' + x.cname + '.h' custom_lines = ExtractCustomCode( file_name ); - h = CFile(file_name, 'w') + h = CFile(io.open(file_name, 'wb')) h.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) h.code( 'All rights reserved. Please see niflib.h for license. */' ) h.code() @@ -313,7 +315,7 @@ def OverwriteIfChanged( original_file, candidate_file ): file_name = ROOT_DIR + '/src/gen/' + x.cname + '.cpp' custom_lines = ExtractCustomCode( file_name ); - cpp = CFile(file_name, 'w') + cpp = CFile(io.open(file_name, 'wb')) cpp.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) cpp.code( 'All rights reserved. Please see niflib.h for license. */' ) cpp.code() @@ -410,7 +412,7 @@ def OverwriteIfChanged( original_file, candidate_file ): # Write out Public Enumeration header Enumerations if GENALLFILES: - out = CFile(ROOT_DIR + '/include/gen/enums.h', 'w') + out = CFile(io.open(ROOT_DIR + '/include/gen/enums.h', 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code('#ifndef _NIF_ENUMS_H_') @@ -425,7 +427,7 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.write('namespace Niflib {\n') out.code() - for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()): + for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: if x.description: out.comment(x.description) @@ -443,7 +445,7 @@ def OverwriteIfChanged( original_file, candidate_file ): # Write out Internal Enumeration header (NifStream functions) if GENALLFILES: - out = CFile(ROOT_DIR + '/include/gen/enums_intl.h', 'w') + out = CFile(io.open(ROOT_DIR + '/include/gen/enums_intl.h', 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code() @@ -461,7 +463,7 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.write('namespace Niflib {\n') out.code() - for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()): + for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: if x.description: out.code() @@ -477,7 +479,7 @@ def OverwriteIfChanged( original_file, candidate_file ): #Write out Enumeration Implementation if GENALLFILES: - out = CFile(ROOT_DIR + '/src/gen/enums.cpp', 'w') + out = CFile(io.open(ROOT_DIR + '/src/gen/enums.cpp', 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code() @@ -497,7 +499,7 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code() - for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()): + for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: out.code() out.code('//--' + x.cname + '--//') @@ -527,7 +529,7 @@ def OverwriteIfChanged( original_file, candidate_file ): # # NiObject Registration Function # - out = CFile(ROOT_DIR + '/src/gen/register.cpp', 'w') + out = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code() @@ -571,7 +573,7 @@ def OverwriteIfChanged( original_file, candidate_file ): custom_lines = ExtractCustomCode( file_name ); #output new file - out = CFile(file_name, 'w') + out = CFile(io.open(file_name, 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code() @@ -708,8 +710,8 @@ def OverwriteIfChanged( original_file, candidate_file ): #Get existing custom code file_name = ROOT_DIR + '/src/obj/' + x.cname + '.cpp' custom_lines = ExtractCustomCode( file_name ); - - out = CFile( file_name, 'w') + + out = CFile(io.open(file_name, 'wb')) out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) out.code( 'All rights reserved. Please see niflib.h for license. */' ) out.code() diff --git a/nifxml.py b/nifxml.py index 8d6a943..ed7c63e 100644 --- a/nifxml.py +++ b/nifxml.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # TODO: split in multiple files """ @@ -86,11 +88,14 @@ @type ACTION_GETPTRS: C{int} """ +from __future__ import unicode_literals + from xml.dom.minidom import * from textwrap import fill import sys import os +import io import re import types @@ -185,13 +190,13 @@ def set_var(self, var_name, value): def parse(self, file_name): #Open file and read contents to txt variable - f = file(file_name, 'r') + f = io.open(file_name, 'rt', 1, 'utf-8') txt = f.read() f.close() #Loop through all variables, replacing them in the template text for i in self.vars: - txt = txt.replace( "{" + i + "}", str(self.vars[i]) ) + txt = txt.replace( '{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict') ) #return result return txt @@ -200,7 +205,7 @@ def parse(self, file_name): # C++ code formatting functions # -class CFile(file): +class CFile(io.TextIOWrapper): """ This class represents a C++ source file. It is used to open the file for output and automatically handles indentation by detecting brackets and colons. @@ -210,15 +215,8 @@ class CFile(file): @ivar backslash_mode: Determines whether a backslash is appended to each line for creation of multi-line defines @type backslash_mode: bool """ - def __init__(self, filename, mode): - """ - This constructor requires the name of the file to open and the IO mode to open it in. - @param filename: The name of the ouput file to open - @type filename: string - @param mode: The IO Mode. Same as fopen? Usually should be 'r', 'w', or 'a' - @type mode: char - """ - file.__init__(self, filename, mode) + def __init__(self, buffer, encoding='utf-8', errors=None, newline=None, line_buffering=False, write_through=False): + io.TextIOWrapper.__init__(self, buffer, encoding, errors, newline, line_buffering) self.indent = 0 self.backslash_mode = False @@ -259,8 +257,8 @@ def code(self, txt = None): if txt[-1:] == "{": self.indent += 1 # special, private:, public:, and protected: if txt[-1:] == ":": self.indent += 1 - - self.write(result) + + self.write(result.encode('utf-8').decode('utf-8', 'strict')) # @@ -593,7 +591,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent)) z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1) - if native_types.has_key(y.type): + if y.type in native_types: # these actions distinguish between refs and non-refs if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: if (not subblock.is_link) and (not subblock.is_crossref): @@ -624,23 +622,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", self.code("NifStream( block_num, %s, info );"%stream) self.code("link_stack.push_back( block_num );") elif action == ACTION_WRITE: - self.code("if ( info.version < VER_3_3_0_13 ) {") - self.code("WritePtr32( &(*%s), %s );"%(z, stream)) - self.code("} else {") - self.code("if ( %s != NULL ) {"%z) - self.code("map::const_iterator it = link_map.find( StaticCast(%s) );" % z) - self.code("if (it != link_map.end()) {") - self.code("NifStream( it->second, %s, info );"%stream) - self.code("missing_link_stack.push_back( NULL );") - self.code("} else {") - self.code("NifStream( 0xFFFFFFFF, %s, info );"%stream) - self.code("missing_link_stack.push_back( %s );" %z) - self.code("}") - self.code("} else {") - self.code("NifStream( 0xFFFFFFFF, %s, info );"%stream) - self.code("missing_link_stack.push_back( NULL );") - self.code("}") - self.code("}") + self.code("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );" % (z, stream)) elif action == ACTION_FIXLINKS: self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z,y.ctemplate)) @@ -850,7 +832,7 @@ def scanBrackets(expr_str, fromIndex = 0): startpos = -1 endpos = -1 scandepth = 0 - for scanpos in xrange(fromIndex, len(expr_str)): + for scanpos in range(fromIndex, len(expr_str)): scanchar = expr_str[scanpos] if scanchar == "(": if startpos == -1: @@ -912,7 +894,7 @@ def eval(self, data = None): elif isinstance(self._left, basestring): left = getattr(data, self._left) if self._left != '""' else "" else: - assert(isinstance(self._left, (int, long))) # debug + assert(isinstance(self._left, int)) # debug left = self._left if not self._op: @@ -923,7 +905,7 @@ def eval(self, data = None): elif isinstance(self._right, basestring): right = getattr(data, self._right) if self._right != '""' else "" else: - assert(isinstance(self._right, (int, long))) # debug + assert(isinstance(self._right, int)) # debug right = self._right if self._op == '==': @@ -963,6 +945,13 @@ def __str__(self): right = str(self._right) return left + ' ' + self._op + ' ' + right + def encode(self, encoding): + """ + To allow encode() to be called on an Expression directly as if it were a string + (For Python 2/3 cross-compatibility.) + """ + return self.__str__().encode(encoding) + @classmethod def _parse(cls, expr_str, name_filter = None): """Returns an Expression, string, or int, depending on the @@ -1029,7 +1018,7 @@ def _partition(cls, expr_str): # to avoid confusion between && and &, and || and |, # let's first scan for operators of two characters # and then for operators of one character - for op_endpos in xrange(op_startpos+1, op_startpos-1, -1): + for op_endpos in range(op_startpos+1, op_startpos-1, -1): op_str = expr_str[op_startpos:op_endpos+1] if op_str in cls.operators: break @@ -1045,7 +1034,7 @@ def _partition(cls, expr_str): raise ValueError("expression syntax error: expected operator before '%s'"%expr_str[op_startpos:]) # to avoid confusion between && and &, and || and |, # let's first scan for operators of two characters - for op_endpos in xrange(op_startpos+1, op_startpos-1, -1): + for op_endpos in range(op_startpos+1, op_startpos-1, -1): op_str = expr_str[op_startpos:op_endpos+1] if op_str in cls.operators: break @@ -1081,7 +1070,7 @@ def _scanBrackets(expr_str, fromIndex = 0): startpos = -1 endpos = -1 scandepth = 0 - for scanpos in xrange(fromIndex, len(expr_str)): + for scanpos in range(fromIndex, len(expr_str)): scanchar = expr_str[scanpos] if scanchar == "(": if startpos == -1: @@ -1110,7 +1099,7 @@ def code(self, prefix = '', brackets = True, name_filter = None): rbracket = ")" if brackets else "" if not self._op: if not self.lhs: return '' - if isinstance(self.lhs, types.IntType): + if isinstance(self.lhs, int): return self.lhs elif self.lhs in block_types: return 'IsDerivedType(%s::TYPE)' % self.lhs @@ -1336,8 +1325,10 @@ def __init__(self, element): if element.firstChild: assert element.firstChild.nodeType == Node.TEXT_NODE self.description = element.firstChild.nodeValue.strip() - else: + elif self.name.lower().find("unk") == 0: self.description = "Unknown." + else: + self.description = "" # Format default value so that it can be used in a C++ initializer list if not self.default and (not self.arr1.lhs and not self.arr2.lhs): @@ -1379,7 +1370,7 @@ def __init__(self, element): # calculate other stuff self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') - self.type_is_native = native_types.has_key(self.name) # true if the type is implemented natively + self.type_is_native = self.name in native_types # true if the type is implemented natively # calculate stuff from reference to previous members # true if this is a duplicate of a previously declared member @@ -1537,8 +1528,10 @@ def __init__(self, element): self.niflibtype = NATIVETYPES.get(self.name) if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE: self.description = element.firstChild.nodeValue.strip() - else: + elif self.name.lower().find("unk") == 0: self.description = "Unknown." + else: + self.description = "" self.count = element.getAttribute('count') @@ -1847,30 +1840,30 @@ def find_first_ref(self, name): for element in doc.getElementsByTagName('basic'): x = Basic(element) - assert not basic_types.has_key(x.name) + assert not x.name in basic_types basic_types[x.name] = x basic_names.append(x.name) for element in doc.getElementsByTagName('enum'): x = Enum(element) - assert not enum_types.has_key(x.name) + assert not x.name in enum_types enum_types[x.name] = x enum_names.append(x.name) for element in doc.getElementsByTagName('bitflags'): x = Flag(element) - assert not flag_types.has_key(x.name) + assert not x.name in flag_types flag_types[x.name] = x flag_names.append(x.name) for element in doc.getElementsByTagName("compound"): x = Compound(element) - assert not compound_types.has_key(x.name) + assert not x.name in compound_types compound_types[x.name] = x compound_names.append(x.name) for element in doc.getElementsByTagName("niobject"): x = Block(element) - assert not block_types.has_key(x.name) + assert not x.name in block_types block_types[x.name] = x block_names.append(x.name) diff --git a/nifxml_doc.py b/nifxml_doc.py index 46822a0..dec3443 100644 --- a/nifxml_doc.py +++ b/nifxml_doc.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # nifxml_doc.py # # This script generates HTML documentation from the XML file. @@ -46,9 +48,11 @@ # ***** END LICENCE BLOCK ***** # -------------------------------------------------------------------------- +from __future__ import unicode_literals from nifxml import * from distutils.dir_util import mkpath import os +import io import itertools # @@ -76,7 +80,7 @@ def tohex(value, nbytes=4): """Improved version of hex.""" - return ("0x%%0%dX" % (2*nbytes)) % (long(str(value)) & (2**(nbytes*8)-1)) + return ("0x%%0%dX" % (2*nbytes)) % (int(str(value)) & (2**(nbytes*8)-1)) def ListAttributes( compound ): attr_list = "" @@ -146,7 +150,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/version_list.html") ) -f = file(ROOT_DIR + '/doc/version_list.html', 'w') +f = io.open(ROOT_DIR + '/doc/version_list.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -182,7 +186,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/list.html") ) -f = file(ROOT_DIR + '/doc/basic_list.html', 'w') +f = io.open(ROOT_DIR + '/doc/basic_list.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -224,7 +228,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/basic.html") ) - f = file(ROOT_DIR + '/doc/' + x.cname.replace('\\', '_') + '.html', 'w') + f = io.open(ROOT_DIR + '/doc/' + x.cname.replace('\\', '_') + '.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -239,7 +243,7 @@ def ListAttributes( compound ): count = 0 enum_list = "" -for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()): +for n, x in itertools.chain(enum_types.items(), flag_types.items()): if count % 2 == 0: temp.set_var( "row-class", "reg0" ) else: @@ -257,7 +261,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/list.html") ) -f = file(ROOT_DIR + '/doc/enum_list.html', 'w') +f = io.open(ROOT_DIR + '/doc/enum_list.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -268,7 +272,7 @@ def ListAttributes( compound ): # count = 0 -for n, x in itertools.chain(enum_types.iteritems(), flag_types.iteritems()): +for n, x in itertools.chain(enum_types.items(), flag_types.items()): temp = Template() temp.set_var( "title", x.name ) @@ -319,7 +323,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/enum.html") ) - f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w') + f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -355,7 +359,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/list.html") ) -f = file(ROOT_DIR + '/doc/compound_list.html', 'w') +f = io.open(ROOT_DIR + '/doc/compound_list.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -398,7 +402,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/compound.html") ) - f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w') + f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -434,7 +438,7 @@ def ListAttributes( compound ): temp.set_var( "niobject-contents", temp.parse( "templates/list.html") ) temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") ) -f = file(ROOT_DIR + '/doc/niobject_list.html', 'w') +f = io.open(ROOT_DIR + '/doc/niobject_list.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -488,7 +492,7 @@ def ListAttributes( compound ): temp.set_var( "contents", temp.parse( "templates/niobject.html") ) - f = file(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'w') + f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() @@ -548,7 +552,7 @@ def ListObjectTree( root ): temp.set_var( "niobject-contents", temp.parse( "templates/hierarchy.html") ) temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") ) -f = file(ROOT_DIR + '/doc/index.html', 'w') +f = io.open(ROOT_DIR + '/doc/index.html', 'wt', 1, 'utf-8') f.write( temp.parse( "templates/main.html" ) ) f.close() From 5eb23217c516516d9c19fa54b96208e259dcfb66 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sun, 17 Dec 2017 17:54:46 -0500 Subject: [PATCH 13/27] Update nifxml and kfmxml --- kfmxml | 2 +- nifxml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kfmxml b/kfmxml index 9d12e2f..91eff92 160000 --- a/kfmxml +++ b/kfmxml @@ -1 +1 @@ -Subproject commit 9d12e2faf0984b469e1bd4ddaaf3ea4bdcf60101 +Subproject commit 91eff92daf197f0c5376740ed4b000d064138ec2 diff --git a/nifxml b/nifxml index c360a1b..7e3677e 160000 --- a/nifxml +++ b/nifxml @@ -1 +1 @@ -Subproject commit c360a1b9db5d52237df018c2f631dea34fa34045 +Subproject commit 7e3677e97b8c7a41516ad3f03286d8737da96261 From 117561392893a364cb369d24ded1e9e23b744050 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sun, 17 Dec 2017 18:20:34 -0500 Subject: [PATCH 14/27] nifxml 0.9 member accessor support for arg Required to pass a child member of BSVertexDesc through to BSVertexData. Comment out BSVertexDesc as a native type for now, as niflib doesn't have such a type yet anyway. --- nifxml.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nifxml.py b/nifxml.py index ed7c63e..60e0147 100644 --- a/nifxml.py +++ b/nifxml.py @@ -161,7 +161,7 @@ 'Triangle' : 'Triangle', 'BSVertexData' : 'BSVertexData', 'BSVertexDataSSE' : 'BSVertexData', - 'BSVertexDesc' : 'BSVertexDesc' + #'BSVertexDesc' : 'BSVertexDesc' } ACTION_READ = 0 @@ -765,6 +765,8 @@ def member_name(n): else: n2 += c.upper() lower = True + elif c == '\\': # arg member access operator + n2 += '.' else: n2 += '_' lower = True From 07dc05fe2c09ac8f1defd4e94c90fe60202cb191 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sun, 17 Dec 2017 20:21:56 -0500 Subject: [PATCH 15/27] ICO file update for new logo --- doc/favicon.ico | Bin 22486 -> 13748 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/favicon.ico b/doc/favicon.ico index fc12e79902e4b7e3956c8a8688c5287bf85bfff0..e8740d40f971bc1ecbaab42920e065da5106d3a1 100644 GIT binary patch literal 13748 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�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 Date: Tue, 19 Dec 2017 02:24:11 -0500 Subject: [PATCH 16/27] Move CFile to gen_niflib, rename niflibtype This is only the first step of many... --- gen_niflib.py | 518 +++++++++++++++++++++++++++++++++++++++++++++++ nifxml.py | 543 ++------------------------------------------------ 2 files changed, 532 insertions(+), 529 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index 3095dc0..9588eb9 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -55,6 +55,25 @@ # # ***** END LICENSE BLOCK ***** # -------------------------------------------------------------------------- +""" +@var ACTION_READ: Constant for use with CFile::stream. Causes it to generate Niflib's Read function. +@type ACTION_READ: C{int} + +@var ACTION_WRITE: Constant for use with CFile::stream. Causes it to generate Niflib's Write function. +@type ACTION_WRITE: C{int} + +@var ACTION_OUT: Constant for use with CFile::stream. Causes it to generate Niflib's asString function. +@type ACTION_OUT: C{int} + +@var ACTION_FIXLINKS: Constant for use with CFile::stream. Causes it to generate Niflib's FixLinks function. +@type ACTION_FIXLINKS: C{int} + +@var ACTION_GETREFS: Constant for use with CFile::stream. Causes it to generate Niflib's GetRefs function. +@type ACTION_GETREFS: C{int} + +@var ACTION_GETPTRS: Constant for use with CFile::stream. Causes it to generate Niflib's GetPtrs function. +@type ACTION_GETPTRS: C{int} +""" from __future__ import unicode_literals from nifxml import * @@ -96,6 +115,505 @@ block_types["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True #block_types["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True + +ACTION_READ = 0 +ACTION_WRITE = 1 +ACTION_OUT = 2 +ACTION_FIXLINKS = 3 +ACTION_GETREFS = 4 +ACTION_GETPTRS = 5 + + +# +# C++ code formatting functions +# + +class CFile(io.TextIOWrapper): + """ + This class represents a C++ source file. It is used to open the file for output + and automatically handles indentation by detecting brackets and colons. + It also handles writing the generated Niflib C++ code. + @ivar indent: The current level of indentation. + @type indent: int + @ivar backslash_mode: Determines whether a backslash is appended to each line for creation of multi-line defines + @type backslash_mode: bool + """ + def __init__(self, buffer, encoding='utf-8', errors=None, newline=None, line_buffering=False, write_through=False): + io.TextIOWrapper.__init__(self, buffer, encoding, errors, newline, line_buffering) + self.indent = 0 + self.backslash_mode = False + + + def code(self, txt = None): + r""" + Formats a line of C++ code; the returned result always ends with a newline. + If txt starts with "E{rb}", indent is decreased, if it ends with "E{lb}", indent is increased. + Text ending in "E{:}" de-indents itself. For example "publicE{:}" + Result always ends with a newline + @param txt: None means just a line break. This will also break the backslash, which is kind of handy. + "\n" will create a backslashed newline in backslash mode. + @type txt: string, None + """ + # txt + # this will also break the backslash, which is kind of handy + # call code("\n") if you want a backslashed newline in backslash mode + if txt == None: + self.write("\n") + return + + # block end + if txt[:1] == "}": self.indent -= 1 + # special, private:, public:, and protected: + if txt[-1:] == ":": self.indent -= 1 + # endline string + if self.backslash_mode: + endl = " \\\n" + else: + endl = "\n" + # indent string + prefix = "\t" * self.indent + # strip trailing whitespace, including newlines + txt = txt.rstrip() + # indent, and add newline + result = prefix + txt.replace("\n", endl + prefix) + endl + # block start + if txt[-1:] == "{": self.indent += 1 + # special, private:, public:, and protected: + if txt[-1:] == ":": self.indent += 1 + + self.write(result.encode('utf-8').decode('utf-8', 'strict')) + + + # + def comment(self, txt, doxygen = True): + """ + Wraps text in C++ comments and outputs it to the file. Handles multilined comments as well. + Result always ends with a newline + @param txt: The text to enclose in a Doxygen comment + @type txt: string + """ + + # skip comments when we are in backslash mode + if self.backslash_mode: return + + lines = txt.split( '\n' ) + + txt = "" + for l in lines: + txt = txt + fill(l, 80) + "\n" + + txt = txt.strip() + + num_line_ends = txt.count( '\n' ) + + + if doxygen: + if num_line_ends > 0: + txt = txt.replace("\n", "\n * ") + self.code("/*!\n * " + txt + "\n */") + else: + self.code("/*! " + txt + " */" ) + else: + lines = txt.split('\n') + for l in lines: + self.code( "// " + l ) + + def declare(self, block): + """ + Formats the member variables for a specific class as described by the XML and outputs the result to the file. + @param block: The class or struct to generate member functions for. + @type block: Block, Compound + """ + if isinstance(block, Block): + #self.code('protected:') + prot_mode = True + for y in block.members: + if not y.is_duplicate: + if isinstance(block, Block): + if y.is_public and prot_mode: + self.code('public:') + prot_mode = False + elif not y.is_public and not prot_mode: + self.code('protected:') + prot_mode = True + self.comment(y.description) + self.code(y.code_declare()) + if y.func: + self.comment(y.description) + self.code("%s %s() const;"%(y.ctype,y.func)) + + def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", arg_member = None): + """ + Generates the function code for various functions in Niflib and outputs it to the file. + @param block: The class or struct to generate the function for. + @type block: Block, Compound + @param action: The type of function to generate, valid values are:: + ACTION_READ - Read function. + ACTION_WRITE - Write function + ACTION_OUT - asString function + ACTION_FIXLINKS - FixLinks function + ACTION_GETREFS - GetRefs function + ACTION_GETPTRS - GetPtrs function + @type action: ACTION_X constant + @param localprefix: ? + @type localprefix: string + @param prefix: ? + @type prefix: string + @param arg_prefix: ? + @type arg_prefix: string + @param arg_member: ? + @type arg_member: None, ? + """ + lastver1 = None + lastver2 = None + lastuserver = None + lastuserver2 = None + lastcond = None + lastvercond = None + # stream name + if action == ACTION_READ: + stream = "in" + else: + stream = "out" + + + # preperation + if isinstance(block, Block) or block.name in ["Footer", "Header"]: + if action == ACTION_READ: + if block.has_links or block.has_crossrefs: + self.code("unsigned int block_num;") + if action == ACTION_OUT: + self.code("stringstream out;") + # declare array_output_count, only if it will actually be used + if block.has_arr(): + self.code("unsigned int array_output_count = 0;") + + if action == ACTION_GETREFS: + self.code("list > refs;") + if action == ACTION_GETPTRS: + self.code("list ptrs;") + + # stream the ancestor + if isinstance(block, Block): + if block.inherit: + if action == ACTION_READ: + self.code("%s::Read( %s, link_stack, info );"%(block.inherit.cname, stream)) + elif action == ACTION_WRITE: + self.code("%s::Write( %s, link_map, missing_link_stack, info );"%(block.inherit.cname, stream)) + elif action == ACTION_OUT: + self.code("%s << %s::asString();"%(stream, block.inherit.cname)) + elif action == ACTION_FIXLINKS: + self.code("%s::FixLinks( objects, link_stack, missing_link_stack, info );"%block.inherit.cname) + elif action == ACTION_GETREFS: + self.code("refs = %s::GetRefs();"%block.inherit.cname) + elif action == ACTION_GETPTRS: + self.code("ptrs = %s::GetPtrs();"%block.inherit.cname) + + # declare and calculate local variables (TODO: GET RID OF THIS; PREFERABLY NO LOCAL VARIABLES AT ALL) + if action in [ACTION_READ, ACTION_WRITE, ACTION_OUT]: + block.members.reverse() # calculated data depends on data further down the structure + for y in block.members: + if not y.is_duplicate and not y.is_manual_update and action in [ACTION_WRITE, ACTION_OUT]: + if y.func: + self.code('%s%s = %s%s();'%(prefix, y.cname, prefix, y.func)) + elif y.is_calculated: + if action in [ACTION_READ, ACTION_WRITE]: + self.code('%s%s = %s%sCalc(info);'%(prefix, y.cname, prefix, y.cname)) + # ACTION_OUT is in asString(), which doesn't take version info + # so let's simply not print the field in this case + elif y.arr1_ref: + if not y.arr1 or not y.arr1.lhs: # Simple Scalar + cref = block.find_member(y.arr1_ref[0], True) + # if not cref.is_duplicate and not cref.next_dup and (not cref.cond.lhs or cref.cond.lhs == y.name): + # self.code('assert(%s%s == (%s)(%s%s.size()));'%(prefix, y.cname, y.ctype, prefix, cref.cname)) + self.code('%s%s = (%s)(%s%s.size());'%(prefix, y.cname, y.ctype, prefix, cref.cname)) + elif y.arr2_ref: # 1-dimensional dynamic array + cref = block.find_member(y.arr2_ref[0], True) + if not y.arr1 or not y.arr1.lhs: # Second dimension + # if not cref.is_duplicate and not cref.next_dup (not cref.cond.lhs or cref.cond.lhs == y.name): + # self.code('assert(%s%s == (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0));'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) + self.code('%s%s = (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0);'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) + else: + # index of dynamically sized array + self.code('for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++)'%(self.indent, self.indent, prefix, cref.cname, self.indent)) + self.code('\t%s%s[i%i] = (%s)(%s%s[i%i].size());'%(prefix, y.cname, self.indent, y.ctype, prefix, cref.cname, self.indent)) + # else: #has duplicates needs to be selective based on version + # self.code('assert(!"%s");'%(y.name)) + block.members.reverse() # undo reverse + + + # now comes the difficult part: processing all members recursively + for y in block.members: + # get block + if y.type in basic_types: + subblock = basic_types[y.type] + elif y.type in compound_types: + subblock = compound_types[y.type] + elif y.type in enum_types: + subblock = enum_types[y.type] + elif y.type in flag_types: + subblock = flag_types[y.type] + + # check for links + if action in [ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: + if not subblock.has_links and not subblock.has_crossrefs: + continue # contains no links, so skip this member! + if action == ACTION_OUT: + if y.is_duplicate: + continue # don't write variables twice + # resolve array & cond references + y_arr1_lmember = None + y_arr2_lmember = None + y_cond_lmember = None + y_arg = None + y_arr1_prefix = "" + y_arr2_prefix = "" + y_cond_prefix = "" + y_arg_prefix = "" + if y.arr1.lhs or y.arr2.lhs or y.cond.lhs or y.arg: + for z in block.members: + if not y_arr1_lmember and y.arr1.lhs == z.name: + y_arr1_lmember = z + if not y_arr2_lmember and y.arr2.lhs == z.name: + y_arr2_lmember = z + if not y_cond_lmember: + if y.cond.lhs == z.name: + y_cond_lmember = z + elif y.cond.op == '&&' and y.cond.lhs == z.name: + y_cond_lmember = z + elif y.cond.op == '||' and y.cond.lhs == z.name: + y_cond_lmember = z + if not y_arg and y.arg == z.name: + y_arg = z + if y_arr1_lmember: + y_arr1_prefix = prefix + if y_arr2_lmember: + y_arr2_prefix = prefix + if y_cond_lmember: + y_cond_prefix = prefix + if y_arg: + y_arg_prefix = prefix + # resolve this prefix + y_prefix = prefix + # resolve arguments + if y.arr1 and y.arr1.lhs == 'ARG': + y.arr1.lhs = arg_member.name + y.arr1.clhs = arg_member.cname + y_arr1_prefix = arg_prefix + if y.arr2 and y.arr2.lhs == 'ARG': + y.arr2.lhs = arg_member.name + y.arr2.clhs = arg_member.cname + y_arr2_prefix = arg_prefix + if y.cond and y.cond.lhs == 'ARG': + y.cond.lhs = arg_member.name + y.cond.clhs = arg_member.cname + y_cond_prefix = arg_prefix + # conditioning + y_cond = y.cond.code(y_cond_prefix) + y_vercond = y.vercond.code('info.') + if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: + if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastuserver2 != y.userver2 or lastvercond != y_vercond: + # we must switch to a new version block + # close old version block + if lastver1 or lastver2 or lastuserver or lastuserver2 or lastvercond: self.code("};") + # close old condition block as well + if lastcond: + self.code("};") + lastcond = None + # start new version block + + concat = '' + verexpr = '' + if y.ver1: + verexpr = "( info.version >= 0x%08X )"%y.ver1 + concat = " && " + if y.ver2: + verexpr = "%s%s( info.version <= 0x%08X )"%(verexpr, concat, y.ver2) + concat = " && " + if y.userver != None: + verexpr = "%s%s( info.userVersion == %s )"%(verexpr, concat, y.userver) + concat = " && " + if y.userver2 != None: + verexpr = "%s%s( info.userVersion2 == %s )"%(verexpr, concat, y.userver2) + concat = " && " + if y_vercond: + verexpr = "%s%s( %s )"%(verexpr, concat, y_vercond) + if verexpr: + # remove outer redundant parenthesis + bleft, bright = scanBrackets(verexpr) + if bleft == 0 and bright == (len(verexpr) - 1): + self.code("if %s {"%verexpr) + else: + self.code("if ( %s ) {"%verexpr) + + # start new condition block + if lastcond != y_cond and y_cond: + self.code("if ( %s ) {"%y_cond) + else: + # we remain in the same version block + # check condition block + if lastcond != y_cond: + if lastcond: + self.code("};") + if y_cond: + self.code("if ( %s ) {"%y_cond) + elif action == ACTION_OUT: + # check condition block + if lastcond != y_cond: + if lastcond: + self.code("};") + if y_cond: + self.code("if ( %s ) {"%y_cond) + + # loop over arrays + # and resolve variable name + if not y.arr1.lhs: + z = "%s%s"%(y_prefix, y.cname) + else: + if action == ACTION_OUT: + self.code("array_output_count = 0;") + if y.arr1.lhs.isdigit() == False: + if action == ACTION_READ: + # default to local variable, check if variable is in current scope if not then try to use + # definition from resized child + memcode = "%s%s.resize(%s);"%(y_prefix, y.cname, y.arr1.code(y_arr1_prefix)) + mem = block.find_member(y.arr1.lhs, True) # find member in self or parents + self.code(memcode) + + self.code(\ + "for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++) {"%(self.indent, self.indent, y_prefix, y.cname, self.indent)) + else: + self.code(\ + "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\ + %(self.indent, self.indent, y.arr1.code(y_arr1_prefix), self.indent)) + if action == ACTION_OUT: + self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') + self.code('%s << "" << endl;'%stream) + self.code('break;') + self.code('};') + + if not y.arr2.lhs: + z = "%s%s[i%i]"%(y_prefix, y.cname, self.indent-1) + else: + if not y.arr2_dynamic: + if y.arr2.lhs.isdigit() == False: + if action == ACTION_READ: + self.code("%s%s[i%i].resize(%s);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix))) + self.code(\ + "for (unsigned int i%i = 0; i%i < %s%s[i%i].size(); i%i++) {"\ + %(self.indent, self.indent, y_prefix, y.cname, self.indent-1, self.indent)) + else: + self.code(\ + "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\ + %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent)) + else: + if action == ACTION_READ: + self.code("%s%s[i%i].resize(%s[i%i]);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix), self.indent-1)) + self.code(\ + "for (unsigned int i%i = 0; i%i < %s[i%i]; i%i++) {"\ + %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent)) + z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1) + + if y.type in native_types: + # these actions distinguish between refs and non-refs + if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: + if (not subblock.is_link) and (not subblock.is_crossref): + # not a ref + if action in [ACTION_READ, ACTION_WRITE] and y.is_abstract is False: + # hack required for vector + if y.type == "bool" and y.arr1.lhs: + self.code("{"); + if action == ACTION_READ: + self.code("bool tmp;") + self.code("NifStream( tmp, %s, info );"%(stream)) + self.code("%s = tmp;" % z) + else: # ACTION_WRITE + self.code("bool tmp = %s;" % z) + self.code("NifStream( tmp, %s, info );"%(stream)) + self.code("};") + # the usual thing + elif not y.arg: + cast = "" + if ( y.is_duplicate ): + cast = "(%s&)" % y.ctype + self.code("NifStream( %s%s, %s, info );"%(cast, z, stream)) + else: + self.code("NifStream( %s, %s, info, %s%s );"%(z, stream, y_prefix, y.carg)) + else: + # a ref + if action == ACTION_READ: + self.code("NifStream( block_num, %s, info );"%stream) + self.code("link_stack.push_back( block_num );") + elif action == ACTION_WRITE: + self.code("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );" % (z, stream)) + elif action == ACTION_FIXLINKS: + self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z,y.ctemplate)) + + elif action == ACTION_GETREFS and subblock.is_link: + if not y.is_duplicate: + self.code('if ( %s != NULL )\n\trefs.push_back(StaticCast(%s));'%(z,z)) + elif action == ACTION_GETPTRS and subblock.is_crossref: + if not y.is_duplicate: + self.code('if ( %s != NULL )\n\tptrs.push_back((NiObject *)(%s));'%(z,z)) + # the following actions don't distinguish between refs and non-refs + elif action == ACTION_OUT: + if not y.arr1.lhs: + self.code('%s << "%*s%s: " << %s << endl;'%(stream, 2*self.indent, "", y.name, z)) + else: + self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') + self.code('break;') + self.code('};') + self.code('%s << "%*s%s[" << i%i << "]: " << %s << endl;'%(stream, 2*self.indent, "", y.name, self.indent-1, z)) + self.code('array_output_count++;') + else: + subblock = compound_types[y.type] + if not y.arr1.lhs: + self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) + elif not y.arr2.lhs: + self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) + else: + self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) + + # close array loops + if y.arr1.lhs: + self.code("};") + if y.arr2.lhs: + self.code("};") + + lastver1 = y.ver1 + lastver2 = y.ver2 + lastuserver = y.userver + lastuserver2 = y.userver2 + lastcond = y_cond + lastvercond = y_vercond + + if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: + if lastver1 or lastver2 or not(lastuserver is None) or not(lastuserver2 is None) or lastvercond: + self.code("};") + if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_OUT]: + if lastcond: + self.code("};") + + # the end + if isinstance(block, Block) or block.name in ["Header", "Footer"]: + if action == ACTION_OUT: + self.code("return out.str();") + if action == ACTION_GETREFS: + self.code("return refs;") + if action == ACTION_GETPTRS: + self.code("return ptrs;") + + # declaration + # print "$t Get$n() const; \nvoid Set$n($t value);\n\n"; + def getset_declare(self, block, prefix = ""): # prefix is used to tag local variables only + for y in block.members: + if not y.func: + if y.cname.lower().find("unk") == -1: + self.code( y.getter_declare("", ";") ) + self.code( y.setter_declare("", ";") ) + self.code() + + # # Function to extract custom code from existing file # diff --git a/nifxml.py b/nifxml.py index 60e0147..42f7c40 100644 --- a/nifxml.py +++ b/nifxml.py @@ -68,24 +68,6 @@ @var block_names: Sorted keys of L{block_types}. @type block_names: C{list} - -@var ACTION_READ: Constant for use with CFile::stream. Causes it to generate Niflib's Read function. -@type ACTION_READ: C{int} - -@var ACTION_WRITE: Constant for use with CFile::stream. Causes it to generate Niflib's Write function. -@type ACTION_WRITE: C{int} - -@var ACTION_OUT: Constant for use with CFile::stream. Causes it to generate Niflib's asString function. -@type ACTION_OUT: C{int} - -@var ACTION_FIXLINKS: Constant for use with CFile::stream. Causes it to generate Niflib's FixLinks function. -@type ACTION_FIXLINKS: C{int} - -@var ACTION_GETREFS: Constant for use with CFile::stream. Causes it to generate Niflib's GetRefs function. -@type ACTION_GETREFS: C{int} - -@var ACTION_GETPTRS: Constant for use with CFile::stream. Causes it to generate Niflib's GetPtrs function. -@type ACTION_GETPTRS: C{int} """ from __future__ import unicode_literals @@ -164,13 +146,6 @@ #'BSVertexDesc' : 'BSVertexDesc' } -ACTION_READ = 0 -ACTION_WRITE = 1 -ACTION_OUT = 2 -ACTION_FIXLINKS = 3 -ACTION_GETREFS = 4 -ACTION_GETPTRS = 5 - # # HTML Template class # @@ -200,496 +175,6 @@ def parse(self, file_name): #return result return txt - -# -# C++ code formatting functions -# - -class CFile(io.TextIOWrapper): - """ - This class represents a C++ source file. It is used to open the file for output - and automatically handles indentation by detecting brackets and colons. - It also handles writing the generated Niflib C++ code. - @ivar indent: The current level of indentation. - @type indent: int - @ivar backslash_mode: Determines whether a backslash is appended to each line for creation of multi-line defines - @type backslash_mode: bool - """ - def __init__(self, buffer, encoding='utf-8', errors=None, newline=None, line_buffering=False, write_through=False): - io.TextIOWrapper.__init__(self, buffer, encoding, errors, newline, line_buffering) - self.indent = 0 - self.backslash_mode = False - - - def code(self, txt = None): - r""" - Formats a line of C++ code; the returned result always ends with a newline. - If txt starts with "E{rb}", indent is decreased, if it ends with "E{lb}", indent is increased. - Text ending in "E{:}" de-indents itself. For example "publicE{:}" - Result always ends with a newline - @param txt: None means just a line break. This will also break the backslash, which is kind of handy. - "\n" will create a backslashed newline in backslash mode. - @type txt: string, None - """ - # txt - # this will also break the backslash, which is kind of handy - # call code("\n") if you want a backslashed newline in backslash mode - if txt == None: - self.write("\n") - return - - # block end - if txt[:1] == "}": self.indent -= 1 - # special, private:, public:, and protected: - if txt[-1:] == ":": self.indent -= 1 - # endline string - if self.backslash_mode: - endl = " \\\n" - else: - endl = "\n" - # indent string - prefix = "\t" * self.indent - # strip trailing whitespace, including newlines - txt = txt.rstrip() - # indent, and add newline - result = prefix + txt.replace("\n", endl + prefix) + endl - # block start - if txt[-1:] == "{": self.indent += 1 - # special, private:, public:, and protected: - if txt[-1:] == ":": self.indent += 1 - - self.write(result.encode('utf-8').decode('utf-8', 'strict')) - - - # - def comment(self, txt, doxygen = True): - """ - Wraps text in C++ comments and outputs it to the file. Handles multilined comments as well. - Result always ends with a newline - @param txt: The text to enclose in a Doxygen comment - @type txt: string - """ - - # skip comments when we are in backslash mode - if self.backslash_mode: return - - lines = txt.split( '\n' ) - - txt = "" - for l in lines: - txt = txt + fill(l, 80) + "\n" - - txt = txt.strip() - - num_line_ends = txt.count( '\n' ) - - - if doxygen: - if num_line_ends > 0: - txt = txt.replace("\n", "\n * ") - self.code("/*!\n * " + txt + "\n */") - else: - self.code("/*! " + txt + " */" ) - else: - lines = txt.split('\n') - for l in lines: - self.code( "// " + l ) - - def declare(self, block): - """ - Formats the member variables for a specific class as described by the XML and outputs the result to the file. - @param block: The class or struct to generate member functions for. - @type block: Block, Compound - """ - if isinstance(block, Block): - #self.code('protected:') - prot_mode = True - for y in block.members: - if not y.is_duplicate: - if isinstance(block, Block): - if y.is_public and prot_mode: - self.code('public:') - prot_mode = False - elif not y.is_public and not prot_mode: - self.code('protected:') - prot_mode = True - self.comment(y.description) - self.code(y.code_declare()) - if y.func: - self.comment(y.description) - self.code("%s %s() const;"%(y.ctype,y.func)) - - def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", arg_member = None): - """ - Generates the function code for various functions in Niflib and outputs it to the file. - @param block: The class or struct to generate the function for. - @type block: Block, Compound - @param action: The type of function to generate, valid values are:: - ACTION_READ - Read function. - ACTION_WRITE - Write function - ACTION_OUT - asString function - ACTION_FIXLINKS - FixLinks function - ACTION_GETREFS - GetRefs function - ACTION_GETPTRS - GetPtrs function - @type action: ACTION_X constant - @param localprefix: ? - @type localprefix: string - @param prefix: ? - @type prefix: string - @param arg_prefix: ? - @type arg_prefix: string - @param arg_member: ? - @type arg_member: None, ? - """ - lastver1 = None - lastver2 = None - lastuserver = None - lastuserver2 = None - lastcond = None - lastvercond = None - # stream name - if action == ACTION_READ: - stream = "in" - else: - stream = "out" - - - # preperation - if isinstance(block, Block) or block.name in ["Footer", "Header"]: - if action == ACTION_READ: - if block.has_links or block.has_crossrefs: - self.code("unsigned int block_num;") - if action == ACTION_OUT: - self.code("stringstream out;") - # declare array_output_count, only if it will actually be used - if block.has_arr(): - self.code("unsigned int array_output_count = 0;") - - if action == ACTION_GETREFS: - self.code("list > refs;") - if action == ACTION_GETPTRS: - self.code("list ptrs;") - - # stream the ancestor - if isinstance(block, Block): - if block.inherit: - if action == ACTION_READ: - self.code("%s::Read( %s, link_stack, info );"%(block.inherit.cname, stream)) - elif action == ACTION_WRITE: - self.code("%s::Write( %s, link_map, missing_link_stack, info );"%(block.inherit.cname, stream)) - elif action == ACTION_OUT: - self.code("%s << %s::asString();"%(stream, block.inherit.cname)) - elif action == ACTION_FIXLINKS: - self.code("%s::FixLinks( objects, link_stack, missing_link_stack, info );"%block.inherit.cname) - elif action == ACTION_GETREFS: - self.code("refs = %s::GetRefs();"%block.inherit.cname) - elif action == ACTION_GETPTRS: - self.code("ptrs = %s::GetPtrs();"%block.inherit.cname) - - # declare and calculate local variables (TODO: GET RID OF THIS; PREFERABLY NO LOCAL VARIABLES AT ALL) - if action in [ACTION_READ, ACTION_WRITE, ACTION_OUT]: - block.members.reverse() # calculated data depends on data further down the structure - for y in block.members: - if not y.is_duplicate and not y.is_manual_update and action in [ACTION_WRITE, ACTION_OUT]: - if y.func: - self.code('%s%s = %s%s();'%(prefix, y.cname, prefix, y.func)) - elif y.is_calculated: - if action in [ACTION_READ, ACTION_WRITE]: - self.code('%s%s = %s%sCalc(info);'%(prefix, y.cname, prefix, y.cname)) - # ACTION_OUT is in asString(), which doesn't take version info - # so let's simply not print the field in this case - elif y.arr1_ref: - if not y.arr1 or not y.arr1.lhs: # Simple Scalar - cref = block.find_member(y.arr1_ref[0], True) - # if not cref.is_duplicate and not cref.next_dup and (not cref.cond.lhs or cref.cond.lhs == y.name): - # self.code('assert(%s%s == (%s)(%s%s.size()));'%(prefix, y.cname, y.ctype, prefix, cref.cname)) - self.code('%s%s = (%s)(%s%s.size());'%(prefix, y.cname, y.ctype, prefix, cref.cname)) - elif y.arr2_ref: # 1-dimensional dynamic array - cref = block.find_member(y.arr2_ref[0], True) - if not y.arr1 or not y.arr1.lhs: # Second dimension - # if not cref.is_duplicate and not cref.next_dup (not cref.cond.lhs or cref.cond.lhs == y.name): - # self.code('assert(%s%s == (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0));'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) - self.code('%s%s = (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0);'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) - else: - # index of dynamically sized array - self.code('for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++)'%(self.indent, self.indent, prefix, cref.cname, self.indent)) - self.code('\t%s%s[i%i] = (%s)(%s%s[i%i].size());'%(prefix, y.cname, self.indent, y.ctype, prefix, cref.cname, self.indent)) - # else: #has duplicates needs to be selective based on version - # self.code('assert(!"%s");'%(y.name)) - block.members.reverse() # undo reverse - - - # now comes the difficult part: processing all members recursively - for y in block.members: - # get block - if y.type in basic_types: - subblock = basic_types[y.type] - elif y.type in compound_types: - subblock = compound_types[y.type] - elif y.type in enum_types: - subblock = enum_types[y.type] - elif y.type in flag_types: - subblock = flag_types[y.type] - - # check for links - if action in [ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: - if not subblock.has_links and not subblock.has_crossrefs: - continue # contains no links, so skip this member! - if action == ACTION_OUT: - if y.is_duplicate: - continue # don't write variables twice - # resolve array & cond references - y_arr1_lmember = None - y_arr2_lmember = None - y_cond_lmember = None - y_arg = None - y_arr1_prefix = "" - y_arr2_prefix = "" - y_cond_prefix = "" - y_arg_prefix = "" - if y.arr1.lhs or y.arr2.lhs or y.cond.lhs or y.arg: - for z in block.members: - if not y_arr1_lmember and y.arr1.lhs == z.name: - y_arr1_lmember = z - if not y_arr2_lmember and y.arr2.lhs == z.name: - y_arr2_lmember = z - if not y_cond_lmember: - if y.cond.lhs == z.name: - y_cond_lmember = z - elif y.cond.op == '&&' and y.cond.lhs == z.name: - y_cond_lmember = z - elif y.cond.op == '||' and y.cond.lhs == z.name: - y_cond_lmember = z - if not y_arg and y.arg == z.name: - y_arg = z - if y_arr1_lmember: - y_arr1_prefix = prefix - if y_arr2_lmember: - y_arr2_prefix = prefix - if y_cond_lmember: - y_cond_prefix = prefix - if y_arg: - y_arg_prefix = prefix - # resolve this prefix - y_prefix = prefix - # resolve arguments - if y.arr1 and y.arr1.lhs == 'ARG': - y.arr1.lhs = arg_member.name - y.arr1.clhs = arg_member.cname - y_arr1_prefix = arg_prefix - if y.arr2 and y.arr2.lhs == 'ARG': - y.arr2.lhs = arg_member.name - y.arr2.clhs = arg_member.cname - y_arr2_prefix = arg_prefix - if y.cond and y.cond.lhs == 'ARG': - y.cond.lhs = arg_member.name - y.cond.clhs = arg_member.cname - y_cond_prefix = arg_prefix - # conditioning - y_cond = y.cond.code(y_cond_prefix) - y_vercond = y.vercond.code('info.') - if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: - if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastuserver2 != y.userver2 or lastvercond != y_vercond: - # we must switch to a new version block - # close old version block - if lastver1 or lastver2 or lastuserver or lastuserver2 or lastvercond: self.code("};") - # close old condition block as well - if lastcond: - self.code("};") - lastcond = None - # start new version block - - concat = '' - verexpr = '' - if y.ver1: - verexpr = "( info.version >= 0x%08X )"%y.ver1 - concat = " && " - if y.ver2: - verexpr = "%s%s( info.version <= 0x%08X )"%(verexpr, concat, y.ver2) - concat = " && " - if y.userver != None: - verexpr = "%s%s( info.userVersion == %s )"%(verexpr, concat, y.userver) - concat = " && " - if y.userver2 != None: - verexpr = "%s%s( info.userVersion2 == %s )"%(verexpr, concat, y.userver2) - concat = " && " - if y_vercond: - verexpr = "%s%s( %s )"%(verexpr, concat, y_vercond) - if verexpr: - # remove outer redundant parenthesis - bleft, bright = scanBrackets(verexpr) - if bleft == 0 and bright == (len(verexpr) - 1): - self.code("if %s {"%verexpr) - else: - self.code("if ( %s ) {"%verexpr) - - # start new condition block - if lastcond != y_cond and y_cond: - self.code("if ( %s ) {"%y_cond) - else: - # we remain in the same version block - # check condition block - if lastcond != y_cond: - if lastcond: - self.code("};") - if y_cond: - self.code("if ( %s ) {"%y_cond) - elif action == ACTION_OUT: - # check condition block - if lastcond != y_cond: - if lastcond: - self.code("};") - if y_cond: - self.code("if ( %s ) {"%y_cond) - - # loop over arrays - # and resolve variable name - if not y.arr1.lhs: - z = "%s%s"%(y_prefix, y.cname) - else: - if action == ACTION_OUT: - self.code("array_output_count = 0;") - if y.arr1.lhs.isdigit() == False: - if action == ACTION_READ: - # default to local variable, check if variable is in current scope if not then try to use - # definition from resized child - memcode = "%s%s.resize(%s);"%(y_prefix, y.cname, y.arr1.code(y_arr1_prefix)) - mem = block.find_member(y.arr1.lhs, True) # find member in self or parents - self.code(memcode) - - self.code(\ - "for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++) {"%(self.indent, self.indent, y_prefix, y.cname, self.indent)) - else: - self.code(\ - "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\ - %(self.indent, self.indent, y.arr1.code(y_arr1_prefix), self.indent)) - if action == ACTION_OUT: - self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') - self.code('%s << "" << endl;'%stream) - self.code('break;') - self.code('};') - - if not y.arr2.lhs: - z = "%s%s[i%i]"%(y_prefix, y.cname, self.indent-1) - else: - if not y.arr2_dynamic: - if y.arr2.lhs.isdigit() == False: - if action == ACTION_READ: - self.code("%s%s[i%i].resize(%s);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix))) - self.code(\ - "for (unsigned int i%i = 0; i%i < %s%s[i%i].size(); i%i++) {"\ - %(self.indent, self.indent, y_prefix, y.cname, self.indent-1, self.indent)) - else: - self.code(\ - "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\ - %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent)) - else: - if action == ACTION_READ: - self.code("%s%s[i%i].resize(%s[i%i]);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix), self.indent-1)) - self.code(\ - "for (unsigned int i%i = 0; i%i < %s[i%i]; i%i++) {"\ - %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent)) - z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1) - - if y.type in native_types: - # these actions distinguish between refs and non-refs - if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: - if (not subblock.is_link) and (not subblock.is_crossref): - # not a ref - if action in [ACTION_READ, ACTION_WRITE] and y.is_abstract is False: - # hack required for vector - if y.type == "bool" and y.arr1.lhs: - self.code("{"); - if action == ACTION_READ: - self.code("bool tmp;") - self.code("NifStream( tmp, %s, info );"%(stream)) - self.code("%s = tmp;" % z) - else: # ACTION_WRITE - self.code("bool tmp = %s;" % z) - self.code("NifStream( tmp, %s, info );"%(stream)) - self.code("};") - # the usual thing - elif not y.arg: - cast = "" - if ( y.is_duplicate ): - cast = "(%s&)" % y.ctype - self.code("NifStream( %s%s, %s, info );"%(cast, z, stream)) - else: - self.code("NifStream( %s, %s, info, %s%s );"%(z, stream, y_prefix, y.carg)) - else: - # a ref - if action == ACTION_READ: - self.code("NifStream( block_num, %s, info );"%stream) - self.code("link_stack.push_back( block_num );") - elif action == ACTION_WRITE: - self.code("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );" % (z, stream)) - elif action == ACTION_FIXLINKS: - self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z,y.ctemplate)) - - elif action == ACTION_GETREFS and subblock.is_link: - if not y.is_duplicate: - self.code('if ( %s != NULL )\n\trefs.push_back(StaticCast(%s));'%(z,z)) - elif action == ACTION_GETPTRS and subblock.is_crossref: - if not y.is_duplicate: - self.code('if ( %s != NULL )\n\tptrs.push_back((NiObject *)(%s));'%(z,z)) - # the following actions don't distinguish between refs and non-refs - elif action == ACTION_OUT: - if not y.arr1.lhs: - self.code('%s << "%*s%s: " << %s << endl;'%(stream, 2*self.indent, "", y.name, z)) - else: - self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') - self.code('break;') - self.code('};') - self.code('%s << "%*s%s[" << i%i << "]: " << %s << endl;'%(stream, 2*self.indent, "", y.name, self.indent-1, z)) - self.code('array_output_count++;') - else: - subblock = compound_types[y.type] - if not y.arr1.lhs: - self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) - elif not y.arr2.lhs: - self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) - else: - self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) - - # close array loops - if y.arr1.lhs: - self.code("};") - if y.arr2.lhs: - self.code("};") - - lastver1 = y.ver1 - lastver2 = y.ver2 - lastuserver = y.userver - lastuserver2 = y.userver2 - lastcond = y_cond - lastvercond = y_vercond - - if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: - if lastver1 or lastver2 or not(lastuserver is None) or not(lastuserver2 is None) or lastvercond: - self.code("};") - if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_OUT]: - if lastcond: - self.code("};") - - # the end - if isinstance(block, Block) or block.name in ["Header", "Footer"]: - if action == ACTION_OUT: - self.code("return out.str();") - if action == ACTION_GETREFS: - self.code("return refs;") - if action == ACTION_GETPTRS: - self.code("return ptrs;") - - # declaration - # print "$t Get$n() const; \nvoid Set$n($t value);\n\n"; - def getset_declare(self, block, prefix = ""): # prefix is used to tag local variables only - for y in block.members: - if not y.func: - if y.cname.lower().find("unk") == -1: - self.code( y.getter_declare("", ";") ) - self.code( y.setter_declare("", ";") ) - self.code() - def class_name(n): """ @@ -1527,7 +1012,6 @@ def __init__(self, element): self.name = element.getAttribute('name') assert(self.name) # debug self.cname = class_name(self.name) - self.niflibtype = NATIVETYPES.get(self.name) if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE: self.description = element.firstChild.nodeValue.strip() elif self.name.lower().find("unk") == 0: @@ -1542,12 +1026,13 @@ def __init__(self, element): self.has_links = False self.has_crossrefs = False - if self.niflibtype: - native_types[self.name] = self.niflibtype - if self.niflibtype == "Ref": + self.nativetype = NATIVETYPES.get(self.name) + if self.nativetype: + native_types[self.name] = self.nativetype + if self.nativetype == "Ref": self.is_link = True self.has_links = True - if self.niflibtype == "*": + if self.nativetype == "*": self.is_crossref = True self.has_crossrefs = True @@ -1560,12 +1045,12 @@ def __init__(self, element): self.storage = element.getAttribute('storage') self.prefix = element.getAttribute('prefix') - #find the Niflib type of the storage - self.storage = basic_types[self.storage].niflibtype + # Find the native storage type + self.storage = basic_types[self.storage].nativetype self.description = element.firstChild.nodeValue.strip() - self.niflibtype = self.cname - native_types[self.name] = self.niflibtype + self.nativetype = self.cname + native_types[self.name] = self.nativetype # Locate all special enumeration options for option in element.getElementsByTagName('option'): @@ -1667,7 +1152,7 @@ def code_construct(self): return result def code_include_h(self): - if self.niflibtype: return "" + if self.nativetype: return "" result = "" @@ -1677,10 +1162,10 @@ def code_include_h(self): file_name = None if y.type != self.name: if y.type in compound_names: - if not compound_types[y.type].niflibtype: + if not compound_types[y.type].nativetype: file_name = "%s%s.h"%(self.gen_file_prefix, y.ctype) elif y.type in basic_names: - if basic_types[y.type].niflibtype == "Ref": + if basic_types[y.type].nativetype == "Ref": file_name = "%sRef.h"%(self.root_file_prefix) if file_name and file_name not in used_structs: used_structs.append( file_name ) @@ -1691,7 +1176,7 @@ def code_include_h(self): return result def code_fwd_decl(self): - if self.niflibtype: return "" + if self.nativetype: return "" result = "" @@ -1709,7 +1194,7 @@ def code_fwd_decl(self): return result def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): - if self.niflibtype: return "" + if self.nativetype: return "" if not usedirs: gen_dir = self.gen_file_prefix From fff14dc851a9205a536011c12d5d05a0770910e6 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Tue, 19 Dec 2017 02:24:34 -0500 Subject: [PATCH 17/27] Assure valid float default strings --- nifxml.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nifxml.py b/nifxml.py index 42f7c40..f87e331 100644 --- a/nifxml.py +++ b/nifxml.py @@ -847,7 +847,8 @@ def __init__(self, element): elif self.type == "string" or self.type == "IndexString": self.default = "\"" + self.default + "\"" elif self.type == "float": - self.default += "f" + # Cast to float then back to string to add any missing ".0" + self.default = str(float(self.default)) + "f" elif self.type in ["Ref", "Ptr", "bool", "Vector3"]: pass elif self.default.find(',') != -1: From 92df2366a8924448050e61a9ba21fe77a65c2019 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Tue, 19 Dec 2017 06:34:40 -0500 Subject: [PATCH 18/27] Move repetitious strings to variables, specialize code() for namespace/include Reduced the constant code() calls for each line and used multi-line string templates instead. Created specialized CFile methods for includes, include guards, and namespaces to make it even more legible. --- gen_niflib.py | 490 +++++++++++++++++++++++++++----------------------- 1 file changed, 263 insertions(+), 227 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index 9588eb9..56e87d6 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -87,6 +87,128 @@ # global data # +copyright_year = 2017 + +copyright_notice = r'''/* Copyright (c) {0}, NIF File Format Library and Tools +All rights reserved. Please see niflib.h for license. */'''.format(copyright_year) + +incl_guard = r''' +#ifndef _{0}_H_ +#define _{0}_H_ +''' + +# Partially generated notice +partgen_notice = copyright_notice + r''' + +//-----------------------------------NOTICE----------------------------------// +// Some of this file is automatically filled in by a Python script. Only // +// add custom code in the designated areas or it will be overwritten during // +// the next update. // +//-----------------------------------NOTICE----------------------------------//''' + +# Fully generated notice +fullgen_notice = copyright_notice + r''' + +//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---// + +// To change this file, alter the gen_niflib.py script.''' + +# NiObject standard declaration +classdecl = r'''/*! Constructor */ +NIFLIB_API {0}(); + +/*! Destructor */ +NIFLIB_API virtual ~{0}(); + +/*! + * A constant value which uniquly identifies objects of this type. + */ +NIFLIB_API static const Type TYPE; + +/*! + * A factory function used during file reading to create an instance of this type of object. + * \return A pointer to a newly allocated instance of this type of object. + */ +NIFLIB_API static NiObject * Create(); + +/*! + * Summarizes the information contained in this object in English. + * \param[in] verbose Determines whether or not detailed information about large areas of data will be printed out. + * \return A string containing a summary of the information within the object in English. This is the function that Niflyze calls to generate its analysis, so the output is the same. + */ +NIFLIB_API virtual string asString( bool verbose = false ) const; + +/*! + * Used to determine the type of a particular instance of this object. + * \return The type constant for the actual type of the object. + */ +NIFLIB_API virtual const Type & GetType() const;''' + +# NiObject internals +classinternal = r'''/*! NIFLIB_HIDDEN function. For internal use only. */ +NIFLIB_HIDDEN virtual void Read( istream& in, list & link_stack, const NifInfo & info ); +/*! NIFLIB_HIDDEN function. For internal use only. */ +NIFLIB_HIDDEN virtual void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const; +/*! NIFLIB_HIDDEN function. For internal use only. */ +NIFLIB_HIDDEN virtual void FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ); +/*! NIFLIB_HIDDEN function. For internal use only. */ +NIFLIB_HIDDEN virtual list GetRefs() const; +/*! NIFLIB_HIDDEN function. For internal use only. */ +NIFLIB_HIDDEN virtual list GetPtrs() const;''' + +# Compound standard declaration +compound_decl = r'''/*! Default Constructor */ +NIFLIB_API {0}(); +/*! Default Destructor */ +NIFLIB_API ~{0}(); +/*! Copy Constructor */ +NIFLIB_API {0}( const {0} & src ); +/*! Copy Operator */ +NIFLIB_API {0} & operator=( const {0} & src );''' + +# Enum stream implementation +enum_impl = r'''//--{0}--// + +void NifStream( {0} & val, istream& in, const NifInfo & info ) {{ + {1} temp; + NifStream( temp, in, info ); + val = {0}(temp); +}} + +void NifStream( {0} const & val, ostream& out, const NifInfo & info ) {{ + NifStream( ({1})(val), out, info ); +}} + +ostream & operator<<( ostream & out, {0} const & val ) {{ + switch ( val ) {{ + {2}default: return out << "Invalid Value! - " << ({1})(val); + }} +}}''' + +# Enum stream implementation switch case +enum_impl_case = r'''case {0}: return out << "{1}"; + ''' + + +# Custom Code section comments + +BEG_MISC = '//--BEGIN MISC CUSTOM CODE--//' +BEG_HEAD = '//--BEGIN FILE HEAD CUSTOM CODE--//' +BEG_FOOT = '//--BEGIN FILE FOOT CUSTOM CODE--//' +BEG_PRE_READ = '//--BEGIN PRE-READ CUSTOM CODE--//' +BEG_POST_READ = '//--BEGIN POST-READ CUSTOM CODE--//' +BEG_PRE_WRITE = '//--BEGIN PRE-WRITE CUSTOM CODE--//' +BEG_POST_WRITE = '//--BEGIN POST-WRITE CUSTOM CODE--//' +BEG_PRE_STRING = '//--BEGIN PRE-STRING CUSTOM CODE--//' +BEG_POST_STRING = '//--BEGIN POST-STRING CUSTOM CODE--//' +BEG_PRE_FIXLINK = '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' +BEG_POST_FIXLINK = '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' +BEG_CTOR = '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' +BEG_DTOR = '//--BEGIN DESTRUCTOR CUSTOM CODE--//' +BEG_INCL = '//--BEGIN INCLUDE CUSTOM CODE--//' + +END_CUSTOM = '//--END CUSTOM CODE--//' + ROOT_DIR = ".." BOOTSTRAP = False GENIMPL = True @@ -107,7 +229,6 @@ elif prev == "-n": GENBLOCKS.append(i) GENALLFILES = False - prev = i @@ -142,7 +263,20 @@ def __init__(self, buffer, encoding='utf-8', errors=None, newline=None, line_buf io.TextIOWrapper.__init__(self, buffer, encoding, errors, newline, line_buffering) self.indent = 0 self.backslash_mode = False + self.guarding = False + self.namespaced = False + def end(self): + """ + Closes any namespaces and include guards and closes the file. + """ + if self.namespaced: + self.write('}\n') + self.namespaced = False + if self.guarding: + self.code('#endif') + self.guarding = False + self.close() def code(self, txt = None): r""" @@ -183,7 +317,36 @@ def code(self, txt = None): self.write(result.encode('utf-8').decode('utf-8', 'strict')) + def guard(self, txt): + """ + Begins an include guard scope for the file + @param txt: The unique identifier for the header. + @type txt: str + """ + if self.guarding: + return + self.guarding = True + self.code( incl_guard.format(txt) ) + def namespace(self, txt): + """ + Begins a namespace scope for the file + @param txt: The namespace. + @type txt: str + """ + if self.namespaced: + return + self.namespaced = True + self.write( 'namespace {0} {{\n'.format(txt) ) + + def include(self, txt): + """ + Includes a file + @param txt: The include filepath. + @type txt: str + """ + self.write( '#include {0}\n'.format(txt) ) + # def comment(self, txt, doxygen = True): """ @@ -203,6 +366,8 @@ def comment(self, txt, doxygen = True): txt = txt + fill(l, 80) + "\n" txt = txt.strip() + if not txt: + return num_line_ends = txt.count( '\n' ) @@ -658,53 +823,53 @@ def ExtractCustomCode( file_name ): for l in lines: if custom_flag == True: - if l.find( '//--END CUSTOM CODE--//' ) != -1: + if l.find( END_CUSTOM ) != -1: custom_flag = False else: if not custom_lines[custom_name]: custom_lines[custom_name] = [l] else: custom_lines[custom_name].append(l) - if l.find( '//--BEGIN MISC CUSTOM CODE--//' ) != -1: + if l.find( BEG_MISC ) != -1: custom_flag = True custom_name = 'MISC' - elif l.find( '//--BEGIN FILE HEAD CUSTOM CODE--//' ) != -1: + elif l.find( BEG_HEAD ) != -1: custom_flag = True custom_name = 'FILE HEAD' - elif l.find( '//--BEGIN FILE FOOT CUSTOM CODE--//' ) != -1: + elif l.find( BEG_FOOT ) != -1: custom_flag = True custom_name = 'FILE FOOT' - elif l.find( '//--BEGIN PRE-READ CUSTOM CODE--//' ) != -1: + elif l.find( BEG_PRE_READ ) != -1: custom_flag = True custom_name = 'PRE-READ' - elif l.find( '//--BEGIN POST-READ CUSTOM CODE--//' ) != -1: + elif l.find( BEG_POST_READ ) != -1: custom_flag = True custom_name = 'POST-READ' - elif l.find( '//--BEGIN PRE-WRITE CUSTOM CODE--//' ) != -1: + elif l.find( BEG_PRE_WRITE ) != -1: custom_flag = True custom_name = 'PRE-WRITE' - elif l.find( '//--BEGIN POST-WRITE CUSTOM CODE--//' ) != -1: + elif l.find( BEG_POST_WRITE ) != -1: custom_flag = True custom_name = 'POST-WRITE' - elif l.find( '//--BEGIN PRE-STRING CUSTOM CODE--//' ) != -1: + elif l.find( BEG_PRE_STRING ) != -1: custom_flag = True custom_name = 'PRE-STRING' - elif l.find( '//--BEGIN POST-STRING CUSTOM CODE--//' ) != -1: + elif l.find( BEG_POST_STRING ) != -1: custom_flag = True custom_name = 'POST-STRING' - elif l.find( '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' ) != -1: + elif l.find( BEG_PRE_FIXLINK ) != -1: custom_flag = True custom_name = 'PRE-FIXLINKS' - elif l.find( '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' ) != -1: + elif l.find( BEG_POST_FIXLINK ) != -1: custom_flag = True custom_name = 'POST-FIXLINKS' - elif l.find( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' ) != -1: + elif l.find( BEG_CTOR ) != -1: custom_flag = True custom_name = 'CONSTRUCTOR' - elif l.find( '//--BEGIN DESTRUCTOR CUSTOM CODE--//' ) != -1: + elif l.find( BEG_DTOR ) != -1: custom_flag = True custom_name = 'DESTRUCTOR' - elif l.find( '//--BEGIN INCLUDE CUSTOM CODE--//' ) != -1: + elif l.find( BEG_INCL ) != -1: custom_flag = True custom_name = 'INCLUDE' @@ -762,21 +927,14 @@ def OverwriteIfChanged( original_file, candidate_file ): custom_lines = ExtractCustomCode( file_name ); h = CFile(io.open(file_name, 'wb')) - h.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - h.code( 'All rights reserved. Please see niflib.h for license. */' ) - h.code() - h.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) + h.code( fullgen_notice ) + h.guard( x.cname.upper() ) h.code() - h.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) - h.code() - h.code( '#ifndef _' + x.cname.upper() + '_H_' ) - h.code( '#define _' + x.cname.upper() + '_H_' ) - h.code() - h.code( '#include "../NIF_IO.h"' ) + h.include( '"../NIF_IO.h"' ) if n in ["Header", "Footer"]: - h.code( '#include "../obj/NiObject.h"' ) + h.include( '"../obj/NiObject.h"' ) h.code( x.code_include_h() ) - h.write( "namespace Niflib {\n" ) + h.namespace( 'Niflib' ) h.code( x.code_fwd_decl() ) h.code() # header @@ -788,16 +946,7 @@ def OverwriteIfChanged( original_file, candidate_file ): #constructor/destructor/assignment if not x.template: - h.code( '/*! Default Constructor */' ) - h.code( "NIFLIB_API %s();"%x.cname ) - h.code( '/*! Default Destructor */' ) - h.code( "NIFLIB_API ~%s();"%x.cname ) - h.code( '/*! Copy Constructor */' ) - h.code( 'NIFLIB_API %s( const %s & src );'%(x.cname, x.cname) ) - h.code( '/*! Copy Operator */' ) - h.code( 'NIFLIB_API %s & operator=( const %s & src );'%(x.cname, x.cname) ) - - + h.code( compound_decl.format(x.cname) ) # declaration h.declare(x) @@ -813,20 +962,18 @@ def OverwriteIfChanged( original_file, candidate_file ): h.code( 'NIFLIB_HIDDEN void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' ) h.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' ) - h.code( '//--BEGIN MISC CUSTOM CODE--//' ) + h.code( BEG_MISC ) #Preserve Custom code from before for l in custom_lines['MISC']: h.write(l); - h.code( '//--END CUSTOM CODE--//' ) + h.code( END_CUSTOM ) # done h.code("};") h.code() - h.write( "}\n" ) - h.code( '#endif' ) - h.close() + h.end() if not x.template: #Get existing custom code @@ -834,12 +981,7 @@ def OverwriteIfChanged( original_file, candidate_file ): custom_lines = ExtractCustomCode( file_name ); cpp = CFile(io.open(file_name, 'wb')) - cpp.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - cpp.code( 'All rights reserved. Please see niflib.h for license. */' ) - cpp.code() - cpp.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) - cpp.code() - cpp.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) + cpp.code( partgen_notice ) cpp.code() cpp.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) cpp.code( "using namespace Niflib;" ) @@ -918,32 +1060,26 @@ def OverwriteIfChanged( original_file, candidate_file ): cpp.code( '}' ) cpp.code() - cpp.code( '//--BEGIN MISC CUSTOM CODE--//' ) + cpp.code( BEG_MISC ) #Preserve Custom code from before for l in custom_lines['MISC']: cpp.write(l); - cpp.code( '//--END CUSTOM CODE--//' ) + cpp.code( END_CUSTOM ) - cpp.close() + cpp.end() # Write out Public Enumeration header Enumerations if GENALLFILES: out = CFile(io.open(ROOT_DIR + '/include/gen/enums.h', 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) - out.code('#ifndef _NIF_ENUMS_H_') - out.code('#define _NIF_ENUMS_H_') + out.code( fullgen_notice ) + out.guard( 'NIF_ENUMS' ) out.code() - out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) - out.code() - out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) - out.code() - out.code( '#include ' ) + out.include( '' ) out.code( 'using namespace std;' ) out.code() - out.write('namespace Niflib {\n') + out.namespace( 'Niflib' ) out.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: @@ -956,30 +1092,20 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code('ostream & operator<<( ostream & out, %s const & val );'%x.cname) out.code() - - out.write('}\n') - out.code('#endif') - out.close() + out.end() # Write out Internal Enumeration header (NifStream functions) if GENALLFILES: out = CFile(io.open(ROOT_DIR + '/include/gen/enums_intl.h', 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) + out.code( fullgen_notice ) + out.guard( 'NIF_ENUMS_INTL' ) out.code() - out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) - out.code() - out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) - out.code() - out.code('#ifndef _NIF_ENUMS_INTL_H_') - out.code('#define _NIF_ENUMS_INTL_H_') - out.code() - out.code( '#include ' ) + out.include( '' ) out.code( 'using namespace std;' ) out.code() - out.code('#include "../nif_basic_types.h"') + out.include('"../nif_basic_types.h"') out.code() - out.write('namespace Niflib {\n') + out.namespace( 'Niflib' ) out.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: @@ -990,77 +1116,42 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code('void NifStream( %s & val, istream& in, const NifInfo & info = NifInfo() );'%x.cname) out.code('void NifStream( %s const & val, ostream& out, const NifInfo & info = NifInfo() );'%x.cname) out.code() - - out.write('}\n') - out.code('#endif') - out.close() + out.end() #Write out Enumeration Implementation if GENALLFILES: out = CFile(io.open(ROOT_DIR + '/src/gen/enums.cpp', 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) - out.code() - out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) - out.code() - out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) + out.code( fullgen_notice ) out.code() - out.code('#include ') - out.code('#include ') - out.code('#include "../../include/NIF_IO.h"') - out.code('#include "../../include/gen/enums.h"') - out.code('#include "../../include/gen/enums_intl.h"') + out.include('') + out.include('') + out.include('"../../include/NIF_IO.h"') + out.include('"../../include/gen/enums.h"') + out.include('"../../include/gen/enums_intl.h"') out.code() out.code('using namespace std;') out.code() - out.write('namespace Niflib {\n') + out.namespace( 'Niflib' ) out.code() - out.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): if x.options: + out.code( enum_impl.format(x.cname, x.storage, r''.join((enum_impl_case.format(o.cname, o.name) for o in x.options))) ) out.code() - out.code('//--' + x.cname + '--//') - out.code() - out.code('void NifStream( %s & val, istream& in, const NifInfo & info ) {'%(x.cname)) - out.code('%s temp;'%(x.storage)) - out.code('NifStream( temp, in, info );') - out.code('val = %s(temp);'%(x.cname)) - out.code('}') - out.code() - out.code('void NifStream( %s const & val, ostream& out, const NifInfo & info ) {'%(x.cname)) - out.code('NifStream( (%s)(val), out, info );'%(x.storage)) - out.code('}') - out.code() - out.code('ostream & operator<<( ostream & out, %s const & val ) { '%(x.cname)) - out.code('switch ( val ) {') - for o in x.options: - out.code('case %s: return out << "%s";'%(o.cname, o.name)) - out.code('default: return out << "Invalid Value! - " << (unsigned int)(val);') - out.code('}') - out.code('}') - out.code() - - out.write('}\n') - out.close() + out.end() # # NiObject Registration Function # out = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) - out.code() - out.code( '//---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---//' ) - out.code() - out.code( '//To change this file, alter the niftools/docsys/gen_niflib.py Python script.' ) + out.code( fullgen_notice ) out.code() - out.code( '#include "../../include/ObjectRegistry.h"' ) + out.include( '"../../include/ObjectRegistry.h"' ) for n in block_names: x = block_types[n] - out.code( '#include "../../include/obj/' + x.cname + '.h"' ) + out.include( '"../../include/obj/' + x.cname + '.h"' ) out.code() - out.code( 'namespace Niflib {' ) + out.namespace( 'Niflib' ) out.code( 'void RegisterObjects() {' ) out.code() for n in block_names: @@ -1068,8 +1159,7 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' ) out.code() out.code( '}' ) - out.code( '}' ) - out.close() + out.end() # @@ -1092,28 +1182,19 @@ def OverwriteIfChanged( original_file, candidate_file ): #output new file out = CFile(io.open(file_name, 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) - out.code() - out.code( '//-----------------------------------NOTICE----------------------------------//' ) - out.code( '// Some of this file is automatically filled in by a Python script. Only //' ) - out.code( '// add custom code in the designated areas or it will be overwritten during //' ) - out.code( '// the next update. //' ) - out.code( '//-----------------------------------NOTICE----------------------------------//' ) - out.code() - out.code( '#ifndef _' + x.cname.upper() + '_H_' ) - out.code( '#define _' + x.cname.upper() + '_H_' ) + out.code( partgen_notice ) + out.guard( x.cname.upper() ) out.code() - out.code( '//--BEGIN FILE HEAD CUSTOM CODE--//' ) + out.code( BEG_HEAD ) #Preserve Custom code from before for l in custom_lines['FILE HEAD']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() out.code( x.code_include_h() ) - out.write( "namespace Niflib {\n" ) + out.namespace( 'Niflib' ) if not x.inherit: out.code( 'using namespace std;' ) out.code( x.code_fwd_decl() ) @@ -1125,36 +1206,9 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code( 'class ' + x.cname + ' : public ' + x.inherit.cname + ' {' ) else: out.code( 'class ' + x.cname + ' : public RefObject {' ) + out.code( 'public:' ) - out.code( '/*! Constructor */' ) - out.code( 'NIFLIB_API ' + x.cname + '();' ) - out.code() - out.code( '/*! Destructor */' ) - out.code( 'NIFLIB_API virtual ~' + x.cname + '();' ) - out.code() - out.code( '/*!' ) - out.code( ' * A constant value which uniquly identifies objects of this type.' ) - out.code( ' */' ) - out.code( 'NIFLIB_API static const Type TYPE;' ) - out.code() - out.code( '/*!' ) - out.code( ' * A factory function used during file reading to create an instance of this type of object.' ) - out.code( ' * \\return A pointer to a newly allocated instance of this type of object.' ) - out.code( ' */' ) - out.code( 'NIFLIB_API static NiObject * Create();' ) - out.code() - out.code( '/*!' ) - out.code( ' * Summarizes the information contained in this object in English.' ) - out.code( ' * \\param[in] verbose Determines whether or not detailed information about large areas of data will be printed out.' ) - out.code( ' * \\return A string containing a summary of the information within the object in English. This is the function that Niflyze calls to generate its analysis, so the output is the same.' ) - out.code( ' */' ) - out.code( 'NIFLIB_API virtual string asString( bool verbose = false ) const;' ) - out.code() - out.code( '/*!' ) - out.code( ' * Used to determine the type of a particular instance of this object.' ) - out.code( ' * \\return The type constant for the actual type of the object.' ) - out.code( ' */' ) - out.code( 'NIFLIB_API virtual const Type & GetType() const;' ) + out.code( classdecl.format(x.cname) ) out.code() # @@ -1180,43 +1234,32 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code( '****End Example Naive Implementation***/' ) else: - out.code ( '//--This object has no eligable attributes. No example implementation generated--//' ) + out.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) out.code() - out.code( '//--BEGIN MISC CUSTOM CODE--//' ) + out.code( BEG_MISC ) #Preserve Custom code from before for l in custom_lines['MISC']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) if x.members: out.code( 'protected:' ) out.declare(x) out.code( 'public:' ) - out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' ) - out.code( 'NIFLIB_HIDDEN virtual void Read( istream& in, list & link_stack, const NifInfo & info );' ) - out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' ) - out.code( 'NIFLIB_HIDDEN virtual void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' ) - out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' ) - out.code( 'NIFLIB_HIDDEN virtual void FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info );' ) - out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' ) - out.code( 'NIFLIB_HIDDEN virtual list GetRefs() const;' ) - out.code( '/*! NIFLIB_HIDDEN function. For internal use only. */' ) - out.code( 'NIFLIB_HIDDEN virtual list GetPtrs() const;' ) + out.code( classinternal ) out.code( '};' ) out.code() - out.code( '//--BEGIN FILE FOOT CUSTOM CODE--//' ) + out.code( BEG_FOOT ) #Preserve Custom code from before for l in custom_lines['FILE FOOT']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() - out.write( "} //End Niflib namespace\n" ) - out.code( '#endif' ) - out.close() + out.end() ##Check if the temp file is identical to the target file #OverwriteIfChanged( file_name, 'temp' ) @@ -1230,26 +1273,19 @@ def OverwriteIfChanged( original_file, candidate_file ): custom_lines = ExtractCustomCode( file_name ); out = CFile(io.open(file_name, 'wb')) - out.code( '/* Copyright (c) 2006, NIF File Format Library and Tools' ) - out.code( 'All rights reserved. Please see niflib.h for license. */' ) - out.code() - out.code( '//-----------------------------------NOTICE----------------------------------//' ) - out.code( '// Some of this file is automatically filled in by a Python script. Only //' ) - out.code( '// add custom code in the designated areas or it will be overwritten during //' ) - out.code( '// the next update. //' ) - out.code( '//-----------------------------------NOTICE----------------------------------//' ) + out.code( partgen_notice ) out.code() - out.code( '//--BEGIN FILE HEAD CUSTOM CODE--//' ) + out.code( BEG_HEAD ) #Preserve Custom code from before for l in custom_lines['FILE HEAD']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() - out.code( '#include "../../include/FixLink.h"' ) - out.code( '#include "../../include/ObjectRegistry.h"' ) - out.code( '#include "../../include/NIF_IO.h"' ) + out.include( '"../../include/FixLink.h"' ) + out.include( '"../../include/ObjectRegistry.h"' ) + out.include( '"../../include/NIF_IO.h"' ) out.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) out.code( "using namespace Niflib;" ); out.code() @@ -1264,24 +1300,24 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' ) else: out.code( x.cname + '::' + x.cname + '() {' ) - out.code ( '//--BEGIN CONSTRUCTOR CUSTOM CODE--//' ) + out.code ( BEG_CTOR ) #Preserve Custom code from before for l in custom_lines['CONSTRUCTOR']: out.write(l); - out.code ( '//--END CUSTOM CODE--//') + out.code ( END_CUSTOM ) out.code ( '}' ) out.code() out.code( x.cname + '::' + '~' + x.cname + '() {' ) - out.code ( '//--BEGIN DESTRUCTOR CUSTOM CODE--//' ) + out.code ( BEG_DTOR ) #Preserve Custom code from before for l in custom_lines['DESTRUCTOR']: out.write(l); - out.code ( '//--END CUSTOM CODE--//') + out.code ( END_CUSTOM ) out.code ( '}' ) out.code() out.code( 'const Type & %s::GetType() const {'%x.cname ) @@ -1294,86 +1330,86 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code("void %s::Read( istream& in, list & link_stack, const NifInfo & info ) {"%x.cname) - out.code( '//--BEGIN PRE-READ CUSTOM CODE--//' ) + out.code( BEG_PRE_READ ) #Preserve Custom code from before for l in custom_lines['PRE-READ']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() out.stream(x, ACTION_READ) out.code() - out.code( '//--BEGIN POST-READ CUSTOM CODE--//' ) + out.code( BEG_POST_READ ) #Preserve Custom code from before for l in custom_lines['POST-READ']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code("}") out.code() out.code("void %s::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {"%x.cname) - out.code( '//--BEGIN PRE-WRITE CUSTOM CODE--//' ) + out.code( BEG_PRE_WRITE ) #Preserve Custom code from before for l in custom_lines['PRE-WRITE']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() out.stream(x, ACTION_WRITE) out.code() - out.code( '//--BEGIN POST-WRITE CUSTOM CODE--//' ) + out.code( BEG_POST_WRITE ) #Preserve Custom code from before for l in custom_lines['POST-WRITE']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code("}") out.code() out.code("std::string %s::asString( bool verbose ) const {"%x.cname) - out.code( '//--BEGIN PRE-STRING CUSTOM CODE--//' ) + out.code( BEG_PRE_STRING ) #Preserve Custom code from before for l in custom_lines['PRE-STRING']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() out.stream(x, ACTION_OUT) out.code() - out.code( '//--BEGIN POST-STRING CUSTOM CODE--//' ) + out.code( BEG_POST_STRING ) #Preserve Custom code from before for l in custom_lines['POST-STRING']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code("}") out.code() out.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname) - out.code( '//--BEGIN PRE-FIXLINKS CUSTOM CODE--//' ) + out.code( BEG_PRE_FIXLINK ) #Preserve Custom code from before for l in custom_lines['PRE-FIXLINKS']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code() out.stream(x, ACTION_FIXLINKS) out.code() - out.code( '//--BEGIN POST-FIXLINKS CUSTOM CODE--//' ) + out.code( BEG_POST_FIXLINK ) #Preserve Custom code from before for l in custom_lines['POST-FIXLINKS']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) out.code("}") out.code() @@ -1409,18 +1445,18 @@ def OverwriteIfChanged( original_file, candidate_file ): out.code() out.code( '****End Example Naive Implementation***/' ) else: - out.code ( '//--This object has no eligable attributes. No example implementation generated--//' ) + out.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) out.code() - out.code( '//--BEGIN MISC CUSTOM CODE--//' ) + out.code( BEG_MISC ) #Preserve Custom code from before for l in custom_lines['MISC']: out.write(l); - out.code( '//--END CUSTOM CODE--//' ) + out.code( END_CUSTOM ) ##Check if the temp file is identical to the target file #OverwriteIfChanged( file_name, 'temp' ) - out.close() + out.end() From ebcee67350a4a0719a9b6c9d2efd0ecd83f698af Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Wed, 20 Dec 2017 01:43:32 -0500 Subject: [PATCH 19/27] Pylint first pass --- gen_niflib.py | 1203 +++++++++++++++++++++++++------------------------ nifxml.py | 596 ++++++++++++------------ 2 files changed, 910 insertions(+), 889 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index 56e87d6..d8ff066 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -7,10 +7,10 @@ # -------------------------------------------------------------------------- # Command line options # -# -p /path/to/niflib : specifies the path where niflib can be found +# -p /path/to/niflib : specifies the path where niflib can be found # # -b : enable bootstrap mode (generates templates) -# +# # -i : do NOT generate implmentation; place all code in defines.h # # -a : generate accessors for data in classes @@ -76,29 +76,35 @@ """ from __future__ import unicode_literals -from nifxml import * + +from textwrap import fill from distutils.dir_util import mkpath +import sys import os import io -import hashlib import itertools +from nifxml import Block, native_types, NATIVETYPES +from nifxml import block_types, basic_types, compound_types, enum_types, flag_types +from nifxml import block_names, compound_names +from nifxml import scanBrackets, define_name + # # global data # -copyright_year = 2017 +COPYRIGHT_YEAR = 2017 -copyright_notice = r'''/* Copyright (c) {0}, NIF File Format Library and Tools -All rights reserved. Please see niflib.h for license. */'''.format(copyright_year) +COPYRIGHT_NOTICE = r'''/* Copyright (c) {0}, NIF File Format Library and Tools +All rights reserved. Please see niflib.h for license. */'''.format(COPYRIGHT_YEAR) -incl_guard = r''' +INCL_GUARD = r''' #ifndef _{0}_H_ #define _{0}_H_ ''' # Partially generated notice -partgen_notice = copyright_notice + r''' +PARTGEN_NOTICE = COPYRIGHT_NOTICE + r''' //-----------------------------------NOTICE----------------------------------// // Some of this file is automatically filled in by a Python script. Only // @@ -107,14 +113,14 @@ //-----------------------------------NOTICE----------------------------------//''' # Fully generated notice -fullgen_notice = copyright_notice + r''' +FULLGEN_NOTICE = COPYRIGHT_NOTICE + r''' //---THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT---// // To change this file, alter the gen_niflib.py script.''' # NiObject standard declaration -classdecl = r'''/*! Constructor */ +CLASS_DECL = r'''/*! Constructor */ NIFLIB_API {0}(); /*! Destructor */ @@ -145,7 +151,7 @@ NIFLIB_API virtual const Type & GetType() const;''' # NiObject internals -classinternal = r'''/*! NIFLIB_HIDDEN function. For internal use only. */ +CLASS_INTL = r'''/*! NIFLIB_HIDDEN function. For internal use only. */ NIFLIB_HIDDEN virtual void Read( istream& in, list & link_stack, const NifInfo & info ); /*! NIFLIB_HIDDEN function. For internal use only. */ NIFLIB_HIDDEN virtual void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const; @@ -157,7 +163,7 @@ NIFLIB_HIDDEN virtual list GetPtrs() const;''' # Compound standard declaration -compound_decl = r'''/*! Default Constructor */ +COMPOUND_DECL = r'''/*! Default Constructor */ NIFLIB_API {0}(); /*! Default Destructor */ NIFLIB_API ~{0}(); @@ -167,7 +173,7 @@ NIFLIB_API {0} & operator=( const {0} & src );''' # Enum stream implementation -enum_impl = r'''//--{0}--// +ENUM_IMPL = r'''//--{0}--// void NifStream( {0} & val, istream& in, const NifInfo & info ) {{ {1} temp; @@ -186,7 +192,7 @@ }}''' # Enum stream implementation switch case -enum_impl_case = r'''case {0}: return out << "{1}"; +ENUM_IMPL_CASE = r'''case {0}: return out << "{1}"; ''' @@ -231,7 +237,7 @@ GENALLFILES = False prev = i - + # Fix known manual update attributes. For now hard code here. block_types["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True #block_types["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True @@ -265,7 +271,7 @@ def __init__(self, buffer, encoding='utf-8', errors=None, newline=None, line_buf self.backslash_mode = False self.guarding = False self.namespaced = False - + def end(self): """ Closes any namespaces and include guards and closes the file. @@ -278,7 +284,7 @@ def end(self): self.guarding = False self.close() - def code(self, txt = None): + def code(self, txt=None): r""" Formats a line of C++ code; the returned result always ends with a newline. If txt starts with "E{rb}", indent is decreased, if it ends with "E{lb}", indent is increased. @@ -288,17 +294,19 @@ def code(self, txt = None): "\n" will create a backslashed newline in backslash mode. @type txt: string, None """ - # txt + # txt # this will also break the backslash, which is kind of handy # call code("\n") if you want a backslashed newline in backslash mode - if txt == None: + if txt is None: self.write("\n") return - + # block end - if txt[:1] == "}": self.indent -= 1 + if txt[:1] == "}": + self.indent -= 1 # special, private:, public:, and protected: - if txt[-1:] == ":": self.indent -= 1 + if txt[-1:] == ":": + self.indent -= 1 # endline string if self.backslash_mode: endl = " \\\n" @@ -311,12 +319,14 @@ def code(self, txt = None): # indent, and add newline result = prefix + txt.replace("\n", endl + prefix) + endl # block start - if txt[-1:] == "{": self.indent += 1 + if txt[-1:] == "{": + self.indent += 1 # special, private:, public:, and protected: - if txt[-1:] == ":": self.indent += 1 - + if txt[-1:] == ":": + self.indent += 1 + self.write(result.encode('utf-8').decode('utf-8', 'strict')) - + def guard(self, txt): """ Begins an include guard scope for the file @@ -326,8 +336,8 @@ def guard(self, txt): if self.guarding: return self.guarding = True - self.code( incl_guard.format(txt) ) - + self.code( INCL_GUARD.format(txt) ) + def namespace(self, txt): """ Begins a namespace scope for the file @@ -338,7 +348,7 @@ def namespace(self, txt): return self.namespaced = True self.write( 'namespace {0} {{\n'.format(txt) ) - + def include(self, txt): """ Includes a file @@ -347,8 +357,7 @@ def include(self, txt): """ self.write( '#include {0}\n'.format(txt) ) - # - def comment(self, txt, doxygen = True): + def comment(self, txt, doxygen=True): """ Wraps text in C++ comments and outputs it to the file. Handles multilined comments as well. Result always ends with a newline @@ -357,32 +366,32 @@ def comment(self, txt, doxygen = True): """ # skip comments when we are in backslash mode - if self.backslash_mode: return - - lines = txt.split( '\n' ) + if self.backslash_mode: + return + + lines = txt.split('\n') txt = "" - for l in lines: - txt = txt + fill(l, 80) + "\n" + for com in lines: + txt = txt + fill(com, 80) + "\n" txt = txt.strip() if not txt: return - - num_line_ends = txt.count( '\n' ) - + + num_line_ends = txt.count('\n') if doxygen: if num_line_ends > 0: txt = txt.replace("\n", "\n * ") - self.code("/*!\n * " + txt + "\n */") + self.code("/*!\n * " + txt + "\n */") else: - self.code("/*! " + txt + " */" ) + self.code("/*! " + txt + " */") else: lines = txt.split('\n') - for l in lines: - self.code( "// " + l ) - + for com in lines: + self.code("// " + com) + def declare(self, block): """ Formats the member variables for a specific class as described by the XML and outputs the result to the file. @@ -392,22 +401,22 @@ def declare(self, block): if isinstance(block, Block): #self.code('protected:') prot_mode = True - for y in block.members: - if not y.is_duplicate: + for mem in block.members: + if not mem.is_duplicate: if isinstance(block, Block): - if y.is_public and prot_mode: + if mem.is_public and prot_mode: self.code('public:') prot_mode = False - elif not y.is_public and not prot_mode: + elif not mem.is_public and not prot_mode: self.code('protected:') prot_mode = True - self.comment(y.description) - self.code(y.code_declare()) - if y.func: - self.comment(y.description) - self.code("%s %s() const;"%(y.ctype,y.func)) + self.comment(mem.description) + self.code(mem.code_declare()) + if mem.func: + self.comment(mem.description) + self.code("%s %s() const;"%(mem.ctype, mem.func)) - def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", arg_member = None): + def stream(self, block, action, localprefix="", prefix="", arg_prefix="", arg_member=None): """ Generates the function code for various functions in Niflib and outputs it to the file. @param block: The class or struct to generate the function for. @@ -440,9 +449,8 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", stream = "in" else: stream = "out" - - # preperation + # preparation if isinstance(block, Block) or block.name in ["Footer", "Header"]: if action == ACTION_READ: if block.has_links or block.has_crossrefs: @@ -479,31 +487,35 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", block.members.reverse() # calculated data depends on data further down the structure for y in block.members: if not y.is_duplicate and not y.is_manual_update and action in [ACTION_WRITE, ACTION_OUT]: - if y.func: - self.code('%s%s = %s%s();'%(prefix, y.cname, prefix, y.func)) - elif y.is_calculated: - if action in [ACTION_READ, ACTION_WRITE]: - self.code('%s%s = %s%sCalc(info);'%(prefix, y.cname, prefix, y.cname)) - # ACTION_OUT is in asString(), which doesn't take version info - # so let's simply not print the field in this case - elif y.arr1_ref: - if not y.arr1 or not y.arr1.lhs: # Simple Scalar - cref = block.find_member(y.arr1_ref[0], True) - # if not cref.is_duplicate and not cref.next_dup and (not cref.cond.lhs or cref.cond.lhs == y.name): - # self.code('assert(%s%s == (%s)(%s%s.size()));'%(prefix, y.cname, y.ctype, prefix, cref.cname)) - self.code('%s%s = (%s)(%s%s.size());'%(prefix, y.cname, y.ctype, prefix, cref.cname)) - elif y.arr2_ref: # 1-dimensional dynamic array - cref = block.find_member(y.arr2_ref[0], True) - if not y.arr1 or not y.arr1.lhs: # Second dimension - # if not cref.is_duplicate and not cref.next_dup (not cref.cond.lhs or cref.cond.lhs == y.name): - # self.code('assert(%s%s == (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0));'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) - self.code('%s%s = (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0);'%(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) - else: - # index of dynamically sized array - self.code('for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++)'%(self.indent, self.indent, prefix, cref.cname, self.indent)) - self.code('\t%s%s[i%i] = (%s)(%s%s[i%i].size());'%(prefix, y.cname, self.indent, y.ctype, prefix, cref.cname, self.indent)) - # else: #has duplicates needs to be selective based on version - # self.code('assert(!"%s");'%(y.name)) + if y.func: + self.code('%s%s = %s%s();'%(prefix, y.cname, prefix, y.func)) + elif y.is_calculated: + if action in [ACTION_READ, ACTION_WRITE]: + self.code('%s%s = %s%sCalc(info);'%(prefix, y.cname, prefix, y.cname)) + # ACTION_OUT is in asString(), which doesn't take version info + # so let's simply not print the field in this case + elif y.arr1_ref: + if not y.arr1 or not y.arr1.lhs: # Simple Scalar + cref = block.find_member(y.arr1_ref[0], True) + # if not cref.is_duplicate and not cref.next_dup and (not cref.cond.lhs or cref.cond.lhs == y.name): + # self.code('assert(%s%s == (%s)(%s%s.size()));'%(prefix, y.cname, y.ctype, prefix, cref.cname)) + self.code('%s%s = (%s)(%s%s.size());'%(prefix, y.cname, y.ctype, prefix, cref.cname)) + elif y.arr2_ref: # 1-dimensional dynamic array + cref = block.find_member(y.arr2_ref[0], True) + if not y.arr1 or not y.arr1.lhs: # Second dimension + # if not cref.is_duplicate and not cref.next_dup (not cref.cond.lhs or cref.cond.lhs == y.name): + # self.code('assert(%s%s == (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0));'\ + # %(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) + self.code('%s%s = (%s)((%s%s.size() > 0) ? %s%s[0].size() : 0);'\ + %(prefix, y.cname, y.ctype, prefix, cref.cname, prefix, cref.cname)) + else: + # index of dynamically sized array + self.code('for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++)'\ + %(self.indent, self.indent, prefix, cref.cname, self.indent)) + self.code('\t%s%s[i%i] = (%s)(%s%s[i%i].size());'\ + %(prefix, y.cname, self.indent, y.ctype, prefix, cref.cname, self.indent)) + #else: #has duplicates needs to be selective based on version + #self.code('assert(!"%s");'%(y.name)) block.members.reverse() # undo reverse @@ -518,7 +530,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", subblock = enum_types[y.type] elif y.type in flag_types: subblock = flag_types[y.type] - + # check for links if action in [ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: if not subblock.has_links and not subblock.has_crossrefs: @@ -542,12 +554,12 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if not y_arr2_lmember and y.arr2.lhs == z.name: y_arr2_lmember = z if not y_cond_lmember: - if y.cond.lhs == z.name: - y_cond_lmember = z - elif y.cond.op == '&&' and y.cond.lhs == z.name: - y_cond_lmember = z - elif y.cond.op == '||' and y.cond.lhs == z.name: - y_cond_lmember = z + if y.cond.lhs == z.name: + y_cond_lmember = z + elif y.cond.op == '&&' and y.cond.lhs == z.name: + y_cond_lmember = z + elif y.cond.op == '||' and y.cond.lhs == z.name: + y_cond_lmember = z if not y_arg and y.arg == z.name: y_arg = z if y_arr1_lmember: @@ -578,15 +590,15 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", y_vercond = y.vercond.code('info.') if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS]: if lastver1 != y.ver1 or lastver2 != y.ver2 or lastuserver != y.userver or lastuserver2 != y.userver2 or lastvercond != y_vercond: - # we must switch to a new version block + # we must switch to a new version block # close old version block - if lastver1 or lastver2 or lastuserver or lastuserver2 or lastvercond: self.code("};") - # close old condition block as well + if lastver1 or lastver2 or lastuserver or lastuserver2 or lastvercond: + self.code("};") + # close old condition block as well if lastcond: self.code("};") lastcond = None # start new version block - concat = '' verexpr = '' if y.ver1: @@ -604,18 +616,17 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if y_vercond: verexpr = "%s%s( %s )"%(verexpr, concat, y_vercond) if verexpr: - # remove outer redundant parenthesis + # remove outer redundant parenthesis bleft, bright = scanBrackets(verexpr) if bleft == 0 and bright == (len(verexpr) - 1): self.code("if %s {"%verexpr) else: self.code("if ( %s ) {"%verexpr) - # start new condition block if lastcond != y_cond and y_cond: self.code("if ( %s ) {"%y_cond) else: - # we remain in the same version block + # we remain in the same version block # check condition block if lastcond != y_cond: if lastcond: @@ -629,7 +640,6 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", self.code("};") if y_cond: self.code("if ( %s ) {"%y_cond) - # loop over arrays # and resolve variable name if not y.arr1.lhs: @@ -637,31 +647,30 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", else: if action == ACTION_OUT: self.code("array_output_count = 0;") - if y.arr1.lhs.isdigit() == False: + if not y.arr1.lhs.isdigit(): if action == ACTION_READ: - # default to local variable, check if variable is in current scope if not then try to use - # definition from resized child - memcode = "%s%s.resize(%s);"%(y_prefix, y.cname, y.arr1.code(y_arr1_prefix)) - mem = block.find_member(y.arr1.lhs, True) # find member in self or parents - self.code(memcode) - + # default to local variable, check if variable is in current scope if not then try to use + # definition from resized child + memcode = "%s%s.resize(%s);"%(y_prefix, y.cname, y.arr1.code(y_arr1_prefix)) + mem = block.find_member(y.arr1.lhs, True) # find member in self or parents + self.code(memcode) self.code(\ - "for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++) {"%(self.indent, self.indent, y_prefix, y.cname, self.indent)) + "for (unsigned int i%i = 0; i%i < %s%s.size(); i%i++) {"\ + %(self.indent, self.indent, y_prefix, y.cname, self.indent)) else: self.code(\ "for (unsigned int i%i = 0; i%i < %s; i%i++) {"\ %(self.indent, self.indent, y.arr1.code(y_arr1_prefix), self.indent)) if action == ACTION_OUT: - self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') - self.code('%s << "" << endl;'%stream) - self.code('break;') - self.code('};') - + self.code('if ( !verbose && ( array_output_count > MAXARRAYDUMP ) ) {') + self.code('%s << "" << endl;'%stream) + self.code('break;') + self.code('};') if not y.arr2.lhs: z = "%s%s[i%i]"%(y_prefix, y.cname, self.indent-1) else: if not y.arr2_dynamic: - if y.arr2.lhs.isdigit() == False: + if not y.arr2.lhs.isdigit(): if action == ACTION_READ: self.code("%s%s[i%i].resize(%s);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix))) self.code(\ @@ -673,12 +682,13 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent)) else: if action == ACTION_READ: - self.code("%s%s[i%i].resize(%s[i%i]);"%(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix), self.indent-1)) + self.code("%s%s[i%i].resize(%s[i%i]);"\ + %(y_prefix, y.cname, self.indent-1, y.arr2.code(y_arr2_prefix), self.indent-1)) self.code(\ "for (unsigned int i%i = 0; i%i < %s[i%i]; i%i++) {"\ %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent)) z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1) - + if y.type in native_types: # these actions distinguish between refs and non-refs if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: @@ -687,7 +697,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if action in [ACTION_READ, ACTION_WRITE] and y.is_abstract is False: # hack required for vector if y.type == "bool" and y.arr1.lhs: - self.code("{"); + self.code("{") if action == ACTION_READ: self.code("bool tmp;") self.code("NifStream( tmp, %s, info );"%(stream)) @@ -699,7 +709,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", # the usual thing elif not y.arg: cast = "" - if ( y.is_duplicate ): + if y.is_duplicate: cast = "(%s&)" % y.ctype self.code("NifStream( %s%s, %s, info );"%(cast, z, stream)) else: @@ -710,16 +720,15 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", self.code("NifStream( block_num, %s, info );"%stream) self.code("link_stack.push_back( block_num );") elif action == ACTION_WRITE: - self.code("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );" % (z, stream)) + self.code("WriteRef( StaticCast(%s), %s, info, link_map, missing_link_stack );"%(z, stream)) elif action == ACTION_FIXLINKS: - self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z,y.ctemplate)) - + self.code("%s = FixLink<%s>( objects, link_stack, missing_link_stack, info );"%(z, y.ctemplate)) elif action == ACTION_GETREFS and subblock.is_link: if not y.is_duplicate: - self.code('if ( %s != NULL )\n\trefs.push_back(StaticCast(%s));'%(z,z)) + self.code('if ( %s != NULL )\n\trefs.push_back(StaticCast(%s));'%(z, z)) elif action == ACTION_GETPTRS and subblock.is_crossref: if not y.is_duplicate: - self.code('if ( %s != NULL )\n\tptrs.push_back((NiObject *)(%s));'%(z,z)) + self.code('if ( %s != NULL )\n\tptrs.push_back((NiObject *)(%s));'%(z, z)) # the following actions don't distinguish between refs and non-refs elif action == ACTION_OUT: if not y.arr1.lhs: @@ -733,7 +742,7 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", else: subblock = compound_types[y.type] if not y.arr1.lhs: - self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) + self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) elif not y.arr2.lhs: self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) else: @@ -768,131 +777,132 @@ def stream(self, block, action, localprefix = "", prefix = "", arg_prefix = "", if action == ACTION_GETPTRS: self.code("return ptrs;") - # declaration - # print "$t Get$n() const; \nvoid Set$n($t value);\n\n"; - def getset_declare(self, block, prefix = ""): # prefix is used to tag local variables only - for y in block.members: - if not y.func: - if y.cname.lower().find("unk") == -1: - self.code( y.getter_declare("", ";") ) - self.code( y.setter_declare("", ";") ) - self.code() + def getset_declare(self, block, prefix=""): + """ + Declare getter and setter + prefix is used to tag local variables only + """ + for y in block.members: + if not y.func: + if y.cname.lower().find("unk") == -1: + self.code( y.getter_declare("", ";") ) + self.code( y.setter_declare("", ";") ) + self.code() # # Function to extract custom code from existing file # -def ExtractCustomCode( file_name ): - custom_lines = {} - custom_lines['MISC'] = [] - custom_lines['FILE HEAD'] = [] - custom_lines['FILE FOOT'] = [] - custom_lines['PRE-READ'] = [] - custom_lines['POST-READ'] = [] - custom_lines['PRE-WRITE'] = [] - custom_lines['POST-WRITE'] = [] - custom_lines['PRE-STRING'] = [] - custom_lines['POST-STRING'] = [] - custom_lines['PRE-FIXLINKS'] = [] - custom_lines['POST-FIXLINKS'] = [] - custom_lines['CONSTRUCTOR'] = [] - custom_lines['DESTRUCTOR'] = [] - - if os.path.isfile( file_name ) == False: - custom_lines['MISC'].append( "\n" ) - custom_lines['FILE HEAD'].append( "\n" ) - custom_lines['FILE FOOT'].append( "\n" ) - custom_lines['PRE-READ'].append( "\n" ) - custom_lines['POST-READ'].append( "\n" ) - custom_lines['PRE-WRITE'].append( "\n" ) - custom_lines['POST-WRITE'].append( "\n" ) - custom_lines['PRE-STRING'].append( "\n" ) - custom_lines['POST-STRING'].append( "\n" ) - custom_lines['PRE-FIXLINKS'].append( "\n" ) - custom_lines['POST-FIXLINKS'].append( "\n" ) - custom_lines['CONSTRUCTOR'].append( "\n" ) - custom_lines['DESTRUCTOR'].append( "\n" ) - return custom_lines - - f = io.open(file_name, 'rt', 1, 'utf-8') +def extract_custom_code(name): + custom = {} + custom['MISC'] = [] + custom['FILE HEAD'] = [] + custom['FILE FOOT'] = [] + custom['PRE-READ'] = [] + custom['POST-READ'] = [] + custom['PRE-WRITE'] = [] + custom['POST-WRITE'] = [] + custom['PRE-STRING'] = [] + custom['POST-STRING'] = [] + custom['PRE-FIXLINKS'] = [] + custom['POST-FIXLINKS'] = [] + custom['CONSTRUCTOR'] = [] + custom['DESTRUCTOR'] = [] + + if os.path.isfile(name) is False: + custom['MISC'].append('\n') + custom['FILE HEAD'].append('\n') + custom['FILE FOOT'].append('\n') + custom['PRE-READ'].append('\n') + custom['POST-READ'].append('\n') + custom['PRE-WRITE'].append('\n') + custom['POST-WRITE'].append('\n') + custom['PRE-STRING'].append('\n') + custom['POST-STRING'].append('\n') + custom['PRE-FIXLINKS'].append('\n') + custom['POST-FIXLINKS'].append('\n') + custom['CONSTRUCTOR'].append('\n') + custom['DESTRUCTOR'].append('\n') + return custom + + f = io.open(name, 'rt', 1, 'utf-8') lines = f.readlines() f.close() - + custom_flag = False custom_name = "" - - for l in lines: - if custom_flag == True: - if l.find( END_CUSTOM ) != -1: + + for cln in lines: + if custom_flag is True: + if cln.find( END_CUSTOM ) != -1: custom_flag = False else: - if not custom_lines[custom_name]: - custom_lines[custom_name] = [l] + if not custom[custom_name]: + custom[custom_name] = [cln] else: - custom_lines[custom_name].append(l) - if l.find( BEG_MISC ) != -1: + custom[custom_name].append(cln) + if cln.find( BEG_MISC ) != -1: custom_flag = True custom_name = 'MISC' - elif l.find( BEG_HEAD ) != -1: + elif cln.find( BEG_HEAD ) != -1: custom_flag = True custom_name = 'FILE HEAD' - elif l.find( BEG_FOOT ) != -1: + elif cln.find( BEG_FOOT ) != -1: custom_flag = True custom_name = 'FILE FOOT' - elif l.find( BEG_PRE_READ ) != -1: + elif cln.find( BEG_PRE_READ ) != -1: custom_flag = True custom_name = 'PRE-READ' - elif l.find( BEG_POST_READ ) != -1: + elif cln.find( BEG_POST_READ ) != -1: custom_flag = True custom_name = 'POST-READ' - elif l.find( BEG_PRE_WRITE ) != -1: + elif cln.find( BEG_PRE_WRITE ) != -1: custom_flag = True custom_name = 'PRE-WRITE' - elif l.find( BEG_POST_WRITE ) != -1: + elif cln.find( BEG_POST_WRITE ) != -1: custom_flag = True custom_name = 'POST-WRITE' - elif l.find( BEG_PRE_STRING ) != -1: + elif cln.find( BEG_PRE_STRING ) != -1: custom_flag = True custom_name = 'PRE-STRING' - elif l.find( BEG_POST_STRING ) != -1: + elif cln.find( BEG_POST_STRING ) != -1: custom_flag = True custom_name = 'POST-STRING' - elif l.find( BEG_PRE_FIXLINK ) != -1: + elif cln.find( BEG_PRE_FIXLINK ) != -1: custom_flag = True custom_name = 'PRE-FIXLINKS' - elif l.find( BEG_POST_FIXLINK ) != -1: + elif cln.find( BEG_POST_FIXLINK ) != -1: custom_flag = True custom_name = 'POST-FIXLINKS' - elif l.find( BEG_CTOR ) != -1: + elif cln.find( BEG_CTOR ) != -1: custom_flag = True custom_name = 'CONSTRUCTOR' - elif l.find( BEG_DTOR ) != -1: + elif cln.find( BEG_DTOR ) != -1: custom_flag = True custom_name = 'DESTRUCTOR' - elif l.find( BEG_INCL ) != -1: + elif cln.find( BEG_INCL ) != -1: custom_flag = True custom_name = 'INCLUDE' - - return custom_lines + return custom # # Function to compare two files # -def OverwriteIfChanged( original_file, candidate_file ): +def overwrite_if_changed( original_file, candidate_file ): files_differ = False if os.path.isfile( original_file ): - f1 = file( original_file, 'r' ) - f2 = file( candidate_file, 'r' ) + file1 = io.open( original_file, 'r' ) + file2 = io.open( candidate_file, 'r' ) + + str1 = file1.read() + str2 = file2.read() - s1 = f1.read() - s2 = f2.read() + file1.close() + file2.close() - f1.close() - f2.close() - - if s1 != s2: + if str1 != str2: files_differ = True #remove original file os.unlink( original_file ) @@ -902,7 +912,7 @@ def OverwriteIfChanged( original_file, candidate_file ): if files_differ: #Files differ, so overwrite original with candidate os.rename( candidate_file, original_file ) - + # # generate compound code # @@ -915,252 +925,251 @@ def OverwriteIfChanged( original_file, candidate_file ): for n in compound_names: x = compound_types[n] - # skip natively implemented types - if x.name in NATIVETYPES.keys(): continue - + if x.name in NATIVETYPES.keys(): + continue if not GENALLFILES and not x.cname in GENBLOCKS: - continue - + continue + #Get existing custom code file_name = ROOT_DIR + '/include/gen/' + x.cname + '.h' - custom_lines = ExtractCustomCode( file_name ); + custom_lines = extract_custom_code( file_name ) - h = CFile(io.open(file_name, 'wb')) - h.code( fullgen_notice ) - h.guard( x.cname.upper() ) - h.code() - h.include( '"../NIF_IO.h"' ) + HDR = CFile(io.open(file_name, 'wb')) + HDR.code( FULLGEN_NOTICE ) + HDR.guard( x.cname.upper() ) + HDR.code() + HDR.include( '"../NIF_IO.h"' ) if n in ["Header", "Footer"]: - h.include( '"../obj/NiObject.h"' ) - h.code( x.code_include_h() ) - h.namespace( 'Niflib' ) - h.code( x.code_fwd_decl() ) - h.code() + HDR.include( '"../obj/NiObject.h"' ) + HDR.code( x.code_include_h() ) + HDR.namespace( 'Niflib' ) + HDR.code( x.code_fwd_decl() ) + HDR.code() # header - h.comment(x.description) + HDR.comment(x.description) hdr = "struct %s"%x.cname - if x.template: hdr = "template \n%s"%hdr + if x.template: + hdr = "template \n%s"%hdr hdr += " {" - h.code(hdr) - + HDR.code(hdr) + #constructor/destructor/assignment if not x.template: - h.code( compound_decl.format(x.cname) ) + HDR.code( COMPOUND_DECL.format(x.cname) ) # declaration - h.declare(x) + HDR.declare(x) # header and footer functions if n == "Header": - h.code( 'NIFLIB_HIDDEN NifInfo Read( istream& in );' ) - h.code( 'NIFLIB_HIDDEN void Write( ostream& out, const NifInfo & info = NifInfo() ) const;' ) - h.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' ) - + HDR.code( 'NIFLIB_HIDDEN NifInfo Read( istream& in );' ) + HDR.code( 'NIFLIB_HIDDEN void Write( ostream& out, const NifInfo & info = NifInfo() ) const;' ) + HDR.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' ) + if n == "Footer": - h.code( 'NIFLIB_HIDDEN void Read( istream& in, list & link_stack, const NifInfo & info );' ) - h.code( 'NIFLIB_HIDDEN void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' ) - h.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' ) + HDR.code( 'NIFLIB_HIDDEN void Read( istream& in, list & link_stack, const NifInfo & info );' ) + HDR.code( 'NIFLIB_HIDDEN void Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const;' ) + HDR.code( 'NIFLIB_HIDDEN string asString( bool verbose = false ) const;' ) - h.code( BEG_MISC ) + HDR.code( BEG_MISC ) #Preserve Custom code from before - for l in custom_lines['MISC']: - h.write(l); - - h.code( END_CUSTOM ) + for line in custom_lines['MISC']: + HDR.write(line) + + HDR.code( END_CUSTOM ) # done - h.code("};") - h.code() - h.end() + HDR.code("};") + HDR.code() + HDR.end() if not x.template: #Get existing custom code file_name = ROOT_DIR + '/src/gen/' + x.cname + '.cpp' - custom_lines = ExtractCustomCode( file_name ); - - cpp = CFile(io.open(file_name, 'wb')) - cpp.code( partgen_notice ) - cpp.code() - cpp.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) - cpp.code( "using namespace Niflib;" ) - cpp.code() - cpp.code( '//Constructor' ) - + custom_lines = extract_custom_code( file_name ) + + CPP = CFile(io.open(file_name, 'wb')) + CPP.code( PARTGEN_NOTICE ) + CPP.code() + CPP.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) + CPP.code( "using namespace Niflib;" ) + CPP.code() + CPP.code( '//Constructor' ) + # constructor x_code_construct = x.code_construct() #if x_code_construct: - cpp.code("%s::%s()"%(x.cname,x.cname) + x_code_construct + " {};") - cpp.code() + CPP.code("%s::%s()"%(x.cname,x.cname) + x_code_construct + " {};") + CPP.code() - cpp.code('//Copy Constructor') - cpp.code( '%s::%s( const %s & src ) {'%(x.cname,x.cname,x.cname) ) - cpp.code( '*this = src;' ) - cpp.code('};') - cpp.code() + CPP.code('//Copy Constructor') + CPP.code( '%s::%s( const %s & src ) {'%(x.cname,x.cname,x.cname) ) + CPP.code( '*this = src;' ) + CPP.code('};') + CPP.code() - cpp.code('//Copy Operator') - cpp.code( '%s & %s::operator=( const %s & src ) {'%(x.cname,x.cname,x.cname) ) + CPP.code('//Copy Operator') + CPP.code( '%s & %s::operator=( const %s & src ) {'%(x.cname,x.cname,x.cname) ) for m in x.members: if not m.is_duplicate: - cpp.code('this->%s = src.%s;'%(m.cname, m.cname) ) - cpp.code('return *this;') - cpp.code('};') - cpp.code() + CPP.code('this->%s = src.%s;'%(m.cname, m.cname) ) + CPP.code('return *this;') + CPP.code('};') + CPP.code() + + CPP.code( '//Destructor' ) - cpp.code( '//Destructor' ) - # destructor - cpp.code("%s::~%s()"%(x.cname,x.cname) + " {};") + CPP.code("%s::~%s()"%(x.cname,x.cname) + " {};") # header and footer functions if n == "Header": - cpp.code( 'NifInfo ' + x.cname + '::Read( istream& in ) {' ) - cpp.code( '//Declare NifInfo structure' ) - cpp.code( 'NifInfo info;' ) - cpp.code() - cpp.stream(x, ACTION_READ) - cpp.code() - cpp.code( '//Copy info.version to local version var.' ) - cpp.code( 'version = info.version;' ) - cpp.code() - cpp.code( '//Fill out and return NifInfo structure.' ) - cpp.code( 'info.userVersion = userVersion;' ) - cpp.code( 'info.userVersion2 = userVersion2;' ) - cpp.code( 'info.endian = EndianType(endianType);' ) - cpp.code( 'info.creator = exportInfo.creator.str;' ) - cpp.code( 'info.exportInfo1 = exportInfo.exportInfo1.str;' ) - cpp.code( 'info.exportInfo2 = exportInfo.exportInfo2.str;' ) - cpp.code() - cpp.code( 'return info;' ) - cpp.code() - cpp.code( '}' ) - cpp.code() - cpp.code( 'void ' + x.cname + '::Write( ostream& out, const NifInfo & info ) const {' ) - cpp.stream(x, ACTION_WRITE) - cpp.code( '}' ) - cpp.code() - cpp.code( 'string ' + x.cname + '::asString( bool verbose ) const {' ) - cpp.stream(x, ACTION_OUT) - cpp.code( '}' ) - + CPP.code( 'NifInfo ' + x.cname + '::Read( istream& in ) {' ) + CPP.code( '//Declare NifInfo structure' ) + CPP.code( 'NifInfo info;' ) + CPP.code() + CPP.stream(x, ACTION_READ) + CPP.code() + CPP.code( '//Copy info.version to local version var.' ) + CPP.code( 'version = info.version;' ) + CPP.code() + CPP.code( '//Fill out and return NifInfo structure.' ) + CPP.code( 'info.userVersion = userVersion;' ) + CPP.code( 'info.userVersion2 = userVersion2;' ) + CPP.code( 'info.endian = EndianType(endianType);' ) + CPP.code( 'info.creator = exportInfo.creator.str;' ) + CPP.code( 'info.exportInfo1 = exportInfo.exportInfo1.str;' ) + CPP.code( 'info.exportInfo2 = exportInfo.exportInfo2.str;' ) + CPP.code() + CPP.code( 'return info;' ) + CPP.code() + CPP.code( '}' ) + CPP.code() + CPP.code( 'void ' + x.cname + '::Write( ostream& out, const NifInfo & info ) const {' ) + CPP.stream(x, ACTION_WRITE) + CPP.code( '}' ) + CPP.code() + CPP.code( 'string ' + x.cname + '::asString( bool verbose ) const {' ) + CPP.stream(x, ACTION_OUT) + CPP.code( '}' ) + if n == "Footer": - cpp.code() - cpp.code( 'void ' + x.cname + '::Read( istream& in, list & link_stack, const NifInfo & info ) {' ) - cpp.stream(x, ACTION_READ) - cpp.code( '}' ) - cpp.code() - cpp.code( 'void ' + x.cname + '::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {' ) - cpp.stream(x, ACTION_WRITE) - cpp.code( '}' ) - cpp.code() - cpp.code( 'string ' + x.cname + '::asString( bool verbose ) const {' ) - cpp.stream(x, ACTION_OUT) - cpp.code( '}' ) - - cpp.code() - cpp.code( BEG_MISC ) + CPP.code() + CPP.code( 'void ' + x.cname + '::Read( istream& in, list & link_stack, const NifInfo & info ) {' ) + CPP.stream(x, ACTION_READ) + CPP.code( '}' ) + CPP.code() + CPP.code( 'void ' + x.cname + '::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {' ) + CPP.stream(x, ACTION_WRITE) + CPP.code( '}' ) + CPP.code() + CPP.code( 'string ' + x.cname + '::asString( bool verbose ) const {' ) + CPP.stream(x, ACTION_OUT) + CPP.code( '}' ) + + CPP.code() + CPP.code( BEG_MISC ) #Preserve Custom code from before - for l in custom_lines['MISC']: - cpp.write(l); - - cpp.code( END_CUSTOM ) + for line in custom_lines['MISC']: + CPP.write(line) - cpp.end() + CPP.code( END_CUSTOM ) + + CPP.end() # Write out Public Enumeration header Enumerations if GENALLFILES: - out = CFile(io.open(ROOT_DIR + '/include/gen/enums.h', 'wb')) - out.code( fullgen_notice ) - out.guard( 'NIF_ENUMS' ) - out.code() - out.include( '' ) - out.code( 'using namespace std;' ) - out.code() - out.namespace( 'Niflib' ) - out.code() + HDR = CFile(io.open(ROOT_DIR + '/include/gen/enums.h', 'wb')) + HDR.code( FULLGEN_NOTICE ) + HDR.guard( 'NIF_ENUMS' ) + HDR.code() + HDR.include( '' ) + HDR.code( 'using namespace std;' ) + HDR.code() + HDR.namespace( 'Niflib' ) + HDR.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): - if x.options: - if x.description: - out.comment(x.description) - out.code('enum %s {'%(x.cname)) - for o in x.options: - out.code('%s = %s, /*!< %s */'%(o.cname, o.value, o.description)) - out.code('};') - out.code() - out.code('ostream & operator<<( ostream & out, %s const & val );'%x.cname) - out.code() - out.end() + if x.options: + if x.description: + HDR.comment(x.description) + HDR.code('enum %s {'%(x.cname)) + for o in x.options: + HDR.code('%s = %s, /*!< %s */'%(o.cname, o.value, o.description)) + HDR.code('};') + HDR.code() + HDR.code('ostream & operator<<( ostream & out, %s const & val );'%x.cname) + HDR.code() + HDR.end() # Write out Internal Enumeration header (NifStream functions) if GENALLFILES: - out = CFile(io.open(ROOT_DIR + '/include/gen/enums_intl.h', 'wb')) - out.code( fullgen_notice ) - out.guard( 'NIF_ENUMS_INTL' ) - out.code() - out.include( '' ) - out.code( 'using namespace std;' ) - out.code() - out.include('"../nif_basic_types.h"') - out.code() - out.namespace( 'Niflib' ) - out.code() + HDR = CFile(io.open(ROOT_DIR + '/include/gen/enums_intl.h', 'wb')) + HDR.code( FULLGEN_NOTICE ) + HDR.guard( 'NIF_ENUMS_INTL' ) + HDR.code() + HDR.include( '' ) + HDR.code( 'using namespace std;' ) + HDR.code() + HDR.include('"../nif_basic_types.h"') + HDR.code() + HDR.namespace( 'Niflib' ) + HDR.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): - if x.options: - if x.description: - out.code() - out.code( '//---' + x.cname + '---//') - out.code() - out.code('void NifStream( %s & val, istream& in, const NifInfo & info = NifInfo() );'%x.cname) - out.code('void NifStream( %s const & val, ostream& out, const NifInfo & info = NifInfo() );'%x.cname) - out.code() - out.end() + if x.options: + if x.description: + HDR.code() + HDR.code( '//---' + x.cname + '---//') + HDR.code() + HDR.code('void NifStream( %s & val, istream& in, const NifInfo & info = NifInfo() );'%x.cname) + HDR.code('void NifStream( %s const & val, ostream& out, const NifInfo & info = NifInfo() );'%x.cname) + HDR.code() + HDR.end() #Write out Enumeration Implementation if GENALLFILES: - out = CFile(io.open(ROOT_DIR + '/src/gen/enums.cpp', 'wb')) - out.code( fullgen_notice ) - out.code() - out.include('') - out.include('') - out.include('"../../include/NIF_IO.h"') - out.include('"../../include/gen/enums.h"') - out.include('"../../include/gen/enums_intl.h"') - out.code() - out.code('using namespace std;') - out.code() - out.namespace( 'Niflib' ) - out.code() - out.code() + CPP = CFile(io.open(ROOT_DIR + '/src/gen/enums.cpp', 'wb')) + CPP.code( FULLGEN_NOTICE ) + CPP.code() + CPP.include('') + CPP.include('') + CPP.include('"../../include/NIF_IO.h"') + CPP.include('"../../include/gen/enums.h"') + CPP.include('"../../include/gen/enums_intl.h"') + CPP.code() + CPP.code('using namespace std;') + CPP.code() + CPP.namespace( 'Niflib' ) + CPP.code() + CPP.code() for n, x in itertools.chain(enum_types.items(), flag_types.items()): - if x.options: - out.code( enum_impl.format(x.cname, x.storage, r''.join((enum_impl_case.format(o.cname, o.name) for o in x.options))) ) - out.code() - out.end() + if x.options: + CPP.code( ENUM_IMPL.format(x.cname, x.storage, r''.join((ENUM_IMPL_CASE.format(o.cname, o.name) for o in x.options))) ) + CPP.code() + CPP.end() # # NiObject Registration Function # - out = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb')) - out.code( fullgen_notice ) - out.code() - out.include( '"../../include/ObjectRegistry.h"' ) + CPP = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb')) + CPP.code( FULLGEN_NOTICE ) + CPP.code() + CPP.include( '"../../include/ObjectRegistry.h"' ) for n in block_names: x = block_types[n] - out.include( '"../../include/obj/' + x.cname + '.h"' ) - out.code() - out.namespace( 'Niflib' ) - out.code( 'void RegisterObjects() {' ) - out.code() + CPP.include( '"../../include/obj/' + x.cname + '.h"' ) + CPP.code() + CPP.namespace( 'Niflib' ) + CPP.code( 'void RegisterObjects() {' ) + CPP.code() for n in block_names: x = block_types[n] - out.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' ) - out.code() - out.code( '}' ) - out.end() - + CPP.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' ) + CPP.code() + CPP.code( '}' ) + CPP.end() # # NiObject Files @@ -1171,98 +1180,97 @@ def OverwriteIfChanged( original_file, candidate_file ): if not GENALLFILES and not x.cname in GENBLOCKS: continue - + # # NiObject Header File # #Get existing custom code file_name = ROOT_DIR + '/include/obj/' + x.cname + '.h' - custom_lines = ExtractCustomCode( file_name ); + custom_lines = extract_custom_code( file_name ) #output new file - out = CFile(io.open(file_name, 'wb')) - out.code( partgen_notice ) - out.guard( x.cname.upper() ) - out.code() - out.code( BEG_HEAD ) + HDR = CFile(io.open(file_name, 'wb')) + HDR.code( PARTGEN_NOTICE ) + HDR.guard( x.cname.upper() ) + HDR.code() + HDR.code( BEG_HEAD ) #Preserve Custom code from before - for l in custom_lines['FILE HEAD']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.code( x.code_include_h() ) - out.namespace( 'Niflib' ) + for line in custom_lines['FILE HEAD']: + HDR.write(line) + + HDR.code( END_CUSTOM ) + HDR.code() + HDR.code( x.code_include_h() ) + HDR.namespace( 'Niflib' ) if not x.inherit: - out.code( 'using namespace std;' ) - out.code( x.code_fwd_decl() ) - out.code( 'class ' + x.cname + ';' ) - out.code( 'typedef Ref<' + x.cname + '> ' + x.cname + 'Ref;' ) - out.code() - out.comment( x.description ) + HDR.code( 'using namespace std;' ) + HDR.code( x.code_fwd_decl() ) + HDR.code( 'class ' + x.cname + ';' ) + HDR.code( 'typedef Ref<' + x.cname + '> ' + x.cname + 'Ref;' ) + HDR.code() + HDR.comment( x.description ) if x.inherit: - out.code( 'class ' + x.cname + ' : public ' + x.inherit.cname + ' {' ) + HDR.code( 'class ' + x.cname + ' : public ' + x.inherit.cname + ' {' ) else: - out.code( 'class ' + x.cname + ' : public RefObject {' ) - - out.code( 'public:' ) - out.code( classdecl.format(x.cname) ) - out.code() + HDR.code( 'class ' + x.cname + ' : public RefObject {' ) + HDR.code( 'public:' ) + HDR.code( CLASS_DECL.format(x.cname) ) + HDR.code() # # Show example naive implementation if requested # - + # Create a list of members eligable for functions if GENACCESSORS: func_members = [] - for y in x.members: - if not y.arr1_ref and not y.arr2_ref and y.cname.lower().find("unk") == -1: - func_members.append(y) - - if len(func_members) > 0: - out.code( '/***Begin Example Naive Implementation****' ) - out.code() - for y in func_members: - out.comment( y.description + "\n\\return The current value.", False ) - out.code( y.getter_declare("", ";") ) - out.code() - out.comment( y.description + "\n\\param[in] value The new value.", False ) - out.code( y.setter_declare("", ";") ) - out.code() - out.code( '****End Example Naive Implementation***/' ) + for bmem in x.members: + if not bmem.arr1_ref and not bmem.arr2_ref and bmem.cname.lower().find("unk") == -1: + func_members.append(bmem) + + if func_members: + HDR.code( '/***Begin Example Naive Implementation****' ) + HDR.code() + for fmem in func_members: + HDR.comment( fmem.description + "\n\\return The current value.", False ) + HDR.code( fmem.getter_declare("", ";") ) + HDR.code() + HDR.comment( fmem.description + "\n\\param[in] value The new value.", False ) + HDR.code( fmem.setter_declare("", ";") ) + HDR.code() + HDR.code( '****End Example Naive Implementation***/' ) else: - out.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) - out.code() - - out.code( BEG_MISC ) + HDR.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) + HDR.code() + + HDR.code( BEG_MISC ) #Preserve Custom code from before - for l in custom_lines['MISC']: - out.write(l); - - out.code( END_CUSTOM ) + for line in custom_lines['MISC']: + HDR.write(line) + + HDR.code( END_CUSTOM ) if x.members: - out.code( 'protected:' ) - out.declare(x) - out.code( 'public:' ) - out.code( classinternal ) - out.code( '};' ) - out.code() - out.code( BEG_FOOT ) + HDR.code( 'protected:' ) + HDR.declare(x) + HDR.code( 'public:' ) + HDR.code( CLASS_INTL ) + HDR.code( '};' ) + HDR.code() + HDR.code( BEG_FOOT ) #Preserve Custom code from before - for l in custom_lines['FILE FOOT']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.end() + for line in custom_lines['FILE FOOT']: + HDR.write(line) + + HDR.code( END_CUSTOM ) + HDR.code() + HDR.end() ##Check if the temp file is identical to the target file - #OverwriteIfChanged( file_name, 'temp' ) + #overwrite_if_changed( file_name, 'temp' ) # # NiObject Implementation File @@ -1270,193 +1278,192 @@ def OverwriteIfChanged( original_file, candidate_file ): #Get existing custom code file_name = ROOT_DIR + '/src/obj/' + x.cname + '.cpp' - custom_lines = ExtractCustomCode( file_name ); - - out = CFile(io.open(file_name, 'wb')) - out.code( partgen_notice ) - out.code() - out.code( BEG_HEAD ) + custom_lines = extract_custom_code( file_name ) + + CPP = CFile(io.open(file_name, 'wb')) + CPP.code( PARTGEN_NOTICE ) + CPP.code() + CPP.code( BEG_HEAD ) #Preserve Custom code from before - for l in custom_lines['FILE HEAD']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.include( '"../../include/FixLink.h"' ) - out.include( '"../../include/ObjectRegistry.h"' ) - out.include( '"../../include/NIF_IO.h"' ) - out.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) - out.code( "using namespace Niflib;" ); - out.code() - out.code( '//Definition of TYPE constant' ) + for line in custom_lines['FILE HEAD']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code() + CPP.include( '"../../include/FixLink.h"' ) + CPP.include( '"../../include/ObjectRegistry.h"' ) + CPP.include( '"../../include/NIF_IO.h"' ) + CPP.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) + CPP.code( "using namespace Niflib;" ) + CPP.code() + CPP.code( '//Definition of TYPE constant' ) if x.inherit: - out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &' + x.inherit.cname + '::TYPE );' ) + CPP.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &' + x.inherit.cname + '::TYPE );' ) else: - out.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &RefObject::TYPE );' ) - out.code() + CPP.code ( 'const Type ' + x.cname + '::TYPE(\"' + x.name + '\", &RefObject::TYPE );' ) + CPP.code() x_code_construct = x.code_construct() if x_code_construct: - out.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' ) + CPP.code( x.cname + '::' + x.cname + '()' + x_code_construct + ' {' ) else: - out.code( x.cname + '::' + x.cname + '() {' ) - out.code ( BEG_CTOR ) + CPP.code( x.cname + '::' + x.cname + '() {' ) + CPP.code ( BEG_CTOR ) #Preserve Custom code from before - for l in custom_lines['CONSTRUCTOR']: - out.write(l); - - out.code ( END_CUSTOM ) - out.code ( '}' ) - - out.code() - out.code( x.cname + '::' + '~' + x.cname + '() {' ) - out.code ( BEG_DTOR ) + for line in custom_lines['CONSTRUCTOR']: + CPP.write(line) + + CPP.code ( END_CUSTOM ) + CPP.code ( '}' ) + + CPP.code() + CPP.code( x.cname + '::' + '~' + x.cname + '() {' ) + CPP.code ( BEG_DTOR ) #Preserve Custom code from before - for l in custom_lines['DESTRUCTOR']: - out.write(l); - - out.code ( END_CUSTOM ) - out.code ( '}' ) - out.code() - out.code( 'const Type & %s::GetType() const {'%x.cname ) - out.code( 'return TYPE;' ) - out.code( '}' ) - out.code() - out.code( 'NiObject * ' + x.cname + '::Create() {' ) - out.code( 'return new ' + x.cname + ';' ) - out.code( '}' ) - out.code() - - out.code("void %s::Read( istream& in, list & link_stack, const NifInfo & info ) {"%x.cname) - out.code( BEG_PRE_READ ) + for line in custom_lines['DESTRUCTOR']: + CPP.write(line) + + CPP.code ( END_CUSTOM ) + CPP.code ( '}' ) + CPP.code() + CPP.code( 'const Type & %s::GetType() const {'%x.cname ) + CPP.code( 'return TYPE;' ) + CPP.code( '}' ) + CPP.code() + CPP.code( 'NiObject * ' + x.cname + '::Create() {' ) + CPP.code( 'return new ' + x.cname + ';' ) + CPP.code( '}' ) + CPP.code() + + CPP.code("void %s::Read( istream& in, list & link_stack, const NifInfo & info ) {"%x.cname) + CPP.code( BEG_PRE_READ ) #Preserve Custom code from before - for l in custom_lines['PRE-READ']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.stream(x, ACTION_READ) - out.code() - out.code( BEG_POST_READ ) + for line in custom_lines['PRE-READ']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code() + CPP.stream(x, ACTION_READ) + CPP.code() + CPP.code( BEG_POST_READ ) #Preserve Custom code from before - for l in custom_lines['POST-READ']: - out.write(l); - - out.code( END_CUSTOM ) - out.code("}") - out.code() - - out.code("void %s::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {"%x.cname) - out.code( BEG_PRE_WRITE ) + for line in custom_lines['POST-READ']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code("}") + CPP.code() + + CPP.code("void %s::Write( ostream& out, const map & link_map, list & missing_link_stack, const NifInfo & info ) const {"%x.cname) + CPP.code( BEG_PRE_WRITE ) #Preserve Custom code from before - for l in custom_lines['PRE-WRITE']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.stream(x, ACTION_WRITE) - out.code() - out.code( BEG_POST_WRITE ) + for line in custom_lines['PRE-WRITE']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code() + CPP.stream(x, ACTION_WRITE) + CPP.code() + CPP.code( BEG_POST_WRITE ) #Preserve Custom code from before - for l in custom_lines['POST-WRITE']: - out.write(l); - - out.code( END_CUSTOM ) - out.code("}") - out.code() - - out.code("std::string %s::asString( bool verbose ) const {"%x.cname) - out.code( BEG_PRE_STRING ) + for line in custom_lines['POST-WRITE']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code("}") + CPP.code() + + CPP.code("std::string %s::asString( bool verbose ) const {"%x.cname) + CPP.code( BEG_PRE_STRING ) #Preserve Custom code from before - for l in custom_lines['PRE-STRING']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.stream(x, ACTION_OUT) - out.code() - out.code( BEG_POST_STRING ) + for line in custom_lines['PRE-STRING']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code() + CPP.stream(x, ACTION_OUT) + CPP.code() + CPP.code( BEG_POST_STRING ) #Preserve Custom code from before - for l in custom_lines['POST-STRING']: - out.write(l); - - out.code( END_CUSTOM ) - out.code("}") - out.code() + for line in custom_lines['POST-STRING']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code("}") + CPP.code() - out.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname) + CPP.code("void %s::FixLinks( const map & objects, list & link_stack, list & missing_link_stack, const NifInfo & info ) {"%x.cname) + + CPP.code( BEG_PRE_FIXLINK ) - out.code( BEG_PRE_FIXLINK ) - #Preserve Custom code from before - for l in custom_lines['PRE-FIXLINKS']: - out.write(l); - - out.code( END_CUSTOM ) - out.code() - out.stream(x, ACTION_FIXLINKS) - out.code() - out.code( BEG_POST_FIXLINK ) + for line in custom_lines['PRE-FIXLINKS']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code() + CPP.stream(x, ACTION_FIXLINKS) + CPP.code() + CPP.code( BEG_POST_FIXLINK ) #Preserve Custom code from before - for l in custom_lines['POST-FIXLINKS']: - out.write(l); - - out.code( END_CUSTOM ) - out.code("}") - out.code() - - out.code("std::list %s::GetRefs() const {"%x.cname) - out.stream(x, ACTION_GETREFS) - out.code("}") - out.code() - - out.code("std::list %s::GetPtrs() const {"%x.cname) - out.stream(x, ACTION_GETPTRS) - out.code("}") - out.code() + for line in custom_lines['POST-FIXLINKS']: + CPP.write(line) + + CPP.code( END_CUSTOM ) + CPP.code("}") + CPP.code() + + CPP.code("std::list %s::GetRefs() const {"%x.cname) + CPP.stream(x, ACTION_GETREFS) + CPP.code("}") + CPP.code() + + CPP.code("std::list %s::GetPtrs() const {"%x.cname) + CPP.stream(x, ACTION_GETPTRS) + CPP.code("}") + CPP.code() # Output example implementation of public getter/setter Mmthods if requested if GENACCESSORS: func_members = [] - for y in x.members: - if not y.arr1_ref and not y.arr2_ref and y.cname.lower().find("unk") == -1: - func_members.append(y) - - if len(func_members) > 0: - out.code( '/***Begin Example Naive Implementation****' ) - out.code() - for y in func_members: - out.code( y.getter_declare(x.name + "::", " {") ) - out.code( "return %s;"%y.cname ) - out.code( "}" ) - out.code() - - out.code( y.setter_declare(x.name + "::", " {") ) - out.code( "%s = value;"%y.cname ) - out.code( "}" ) - out.code() - out.code( '****End Example Naive Implementation***/' ) + for bmem in x.members: + if not bmem.arr1_ref and not bmem.arr2_ref and bmem.cname.lower().find("unk") == -1: + func_members.append(bmem) + + if func_members: + CPP.code( '/***Begin Example Naive Implementation****' ) + CPP.code() + for fmem in func_members: + CPP.code( fmem.getter_declare(x.name + "::", " {") ) + CPP.code( "return %s;"%fmem.cname ) + CPP.code( "}" ) + CPP.code() + CPP.code( fmem.setter_declare(x.name + "::", " {") ) + CPP.code( "%s = value;"%fmem.cname ) + CPP.code( "}" ) + CPP.code() + CPP.code( '****End Example Naive Implementation***/' ) else: - out.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) - out.code() - - out.code( BEG_MISC ) + CPP.code ( '//--This object has no eligible attributes. No example implementation generated--//' ) + CPP.code() + + CPP.code( BEG_MISC ) #Preserve Custom code from before - for l in custom_lines['MISC']: - out.write(l); - - out.code( END_CUSTOM ) + for line in custom_lines['MISC']: + CPP.write(line) + + CPP.code( END_CUSTOM ) ##Check if the temp file is identical to the target file - #OverwriteIfChanged( file_name, 'temp' ) + #overwrite_if_changed( file_name, 'temp' ) - out.end() + CPP.end() diff --git a/nifxml.py b/nifxml.py index f87e331..12665cd 100644 --- a/nifxml.py +++ b/nifxml.py @@ -72,10 +72,8 @@ from __future__ import unicode_literals -from xml.dom.minidom import * -from textwrap import fill +from xml.dom.minidom import Node, parse -import sys import os import io import re @@ -157,26 +155,27 @@ class Template: is processed. """ def __init__(self): - #Initialize variable dictionary + """Initialize variable dictionary""" self.vars = {} - + def set_var(self, var_name, value): + """Set data in variable dictionary""" self.vars[var_name] = value def parse(self, file_name): - #Open file and read contents to txt variable + """Open file and read contents to txt variable""" f = io.open(file_name, 'rt', 1, 'utf-8') txt = f.read() f.close() #Loop through all variables, replacing them in the template text for i in self.vars: - txt = txt.replace( '{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict') ) + txt = txt.replace('{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict')) #return result return txt -def class_name(n): +def class_name(name_in): """ Formats a valid C++ class name from the name format used in the XML. @param n: The class name to format in C++ style. @@ -184,30 +183,34 @@ def class_name(n): @return The resulting valid C++ class name @rtype: string """ - if n == None: return None + if name_in is None: + return None try: - return native_types[n] + return native_types[name_in] except KeyError: - return n.replace(' ', '_').replace(":", "_") + return name_in.replace(' ', '_').replace(":", "_") - if n == None: return None + if name_in is None: + return None try: - return native_types[n] + return native_types[name_in] except KeyError: pass - if n == 'TEMPLATE': return 'T' - n2 = '' - for i, c in enumerate(n): - if ('A' <= c) and (c <= 'Z'): - if i > 0: n2 += '_' - n2 += c.lower() - elif (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')): - n2 += c + if name_in == 'TEMPLATE': + return 'T' + name_out = '' + for i, char in enumerate(name_in): + if char.isupper(): + if i > 0: + name_out += '_' + name_out += char.lower() + elif char.islower() or char.isdigit(): + name_out += char else: - n2 += '_' - return n2 + name_out += '_' + return name_out -def define_name(n): +def define_name(name_in): """ Formats an all-uppercase version of the name for use in C++ defines. @param n: The class name to format in define style. @@ -215,48 +218,48 @@ def define_name(n): @return The resulting valid C++ define name @rtype: string """ - n2 = '' - for i, c in enumerate(n): - if ('A' <= c) and (c <= 'Z'): + name_out = '' + for i, char in enumerate(name_in): + if char.isupper(): if i > 0: - n2 += '_' - n2 += c + name_out += '_' + name_out += char else: - n2 += c - elif (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')): - n2 += c.upper() + name_out += char + elif char.islower() or char.isdigit(): + name_out += char.upper() else: - n2 += '_' - return n2 + name_out += '_' + return name_out -def member_name(n): +def member_name(name_in): """ Formats a version of the name for use as a C++ member variable. - @param n: The attribute name to format in variable style. - @type n: string + @param name_in: The attribute name to format in variable style. + @type name_in: string @return The resulting valid C++ variable name @rtype: string """ - if n == None: return None - if n == 'ARG': return 'ARG' - n2 = '' + if name_in is None or name_in == 'ARG': + return name_in + name_out = '' lower = True - for i, c in enumerate(n): - if c == ' ': + for char in name_in: + if char == ' ': lower = False - elif (('A' <= c) and (c <= 'Z')) or (('a' <= c) and (c <= 'z')) or (('0' <= c) and (c <= '9')): + elif char.isalnum(): if lower: - n2 += c.lower() + name_out += char.lower() else: - n2 += c.upper() + name_out += char.upper() lower = True - elif c == '\\': # arg member access operator - n2 += '.' + elif char == '\\': # arg member access operator + name_out += '.' else: - n2 += '_' + name_out += '_' lower = True - return n2 - + return name_out + def version2number(s): """ Translates a legible NIF version number to the packed-byte numeric representation. For example, "10.0.1.0" is translated to 0x0A000100. @@ -265,12 +268,12 @@ def version2number(s): @return The resulting numeric version of the given version string. @rtype: int """ - if not s: return None + if not s: + return None l = s.split('.') if len(l) > 4: - assert(False) + assert False return int(s) - if len(l) == 2: version = 0 version += int(l[0]) << (3 * 8) @@ -283,11 +286,10 @@ def version2number(s): return version else: version = 0 - for i in range( 0, len(l) ): + for i in range(0, len(l)): version += int(l[i]) << ((3-i) * 8) #return (int(l[0]) << 24) + (int(l[1]) << 16) + (int(l[2]) << 8) + int(l[3]) return version - def userversion2number(s): """ @@ -299,10 +301,11 @@ def userversion2number(s): @return The resulting numeric version of the given version string. @rtype: int """ - if not s: return None + if not s: + return None return int(s) -def scanBrackets(expr_str, fromIndex = 0): +def scanBrackets(expr_str, fromIndex=0): """Looks for matching brackets. >>> scanBrackets('abcde') @@ -334,7 +337,7 @@ def scanBrackets(expr_str, fromIndex = 0): if startpos != -1 or endpos != -1: raise ValueError("expression syntax error (non-matching brackets?)") return (startpos, endpos) - + class Expression(object): """This class represents an expression. @@ -363,8 +366,8 @@ class Expression(object): >>> bool(Expression('1 != 1').eval()) False """ - operators = [ '==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<', '/', '*' ] - def __init__(self, expr_str, name_filter = None): + operators = ['==', '!=', '>=', '<=', '&&', '||', '&', '|', '-', '+', '>', '<', '/', '*'] + def __init__(self, expr_str, name_filter=None): self._code = expr_str left, self._op, right = self._partition(expr_str) self._left = self._parse(left, name_filter) @@ -373,15 +376,15 @@ def __init__(self, expr_str, name_filter = None): else: self._right = '' - def eval(self, data = None): + def eval(self, data=None): """Evaluate the expression to an integer.""" if isinstance(self._left, Expression): left = self._left.eval(data) - elif isinstance(self._left, basestring): + elif isinstance(self._left, str): left = getattr(data, self._left) if self._left != '""' else "" else: - assert(isinstance(self._left, int)) # debug + assert isinstance(self._left, int) # debug left = self._left if not self._op: @@ -389,10 +392,10 @@ def eval(self, data = None): if isinstance(self._right, Expression): right = self._right.eval(data) - elif isinstance(self._right, basestring): + elif isinstance(self._right, str): right = getattr(data, self._right) if self._right != '""' else "" else: - assert(isinstance(self._right, int)) # debug + assert isinstance(self._right, int) # debug right = self._right if self._op == '==': @@ -422,13 +425,14 @@ def eval(self, data = None): elif self._op == '!': return not left else: - raise NotImplementedError("expression syntax error: operator '" + op + "' not implemented") + raise NotImplementedError("expression syntax error: operator '" + self._op + "' not implemented") def __str__(self): """Reconstruct the expression to a string.""" left = str(self._left) - if not self._op: return left + if not self._op: + return left right = str(self._right) return left + ' ' + self._op + ' ' + right @@ -440,7 +444,7 @@ def encode(self, encoding): return self.__str__().encode(encoding) @classmethod - def _parse(cls, expr_str, name_filter = None): + def _parse(cls, expr_str, name_filter=None): """Returns an Expression, string, or int, depending on the contents of .""" # brackets or operators => expression @@ -449,7 +453,7 @@ def _parse(cls, expr_str, name_filter = None): for op in cls.operators: if expr_str.find(op) != -1: return Expression(expr_str, name_filter) - + mver = re.compile("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") iver = re.compile("[0-9]+") # try to convert it to an integer @@ -482,7 +486,7 @@ def _partition(cls, expr_str): >>> Expression._partition('(a== b) &&(( b!=c)||d )') ('a== b', '&&', '( b!=c)||d') """ - # check for unary operators + # check for unary operators if expr_str.strip().startswith('!'): return expr_str.lstrip(' !'), '!', None lenstr = len(expr_str) @@ -495,7 +499,7 @@ def _partition(cls, expr_str): # so remove brackets and whitespace, # and let that be the left hand side left_str = expr_str[left_startpos+1:left_endpos].strip() - + # the next token should be the operator # find the position where the operator should start op_startpos = left_endpos+1 @@ -516,7 +520,8 @@ def _partition(cls, expr_str): else: # it's not... so we need to scan for the first operator for op_startpos, ch in enumerate(expr_str): - if ch == ' ': continue + if ch == ' ': + continue if ch == '(' or ch == ')': raise ValueError("expression syntax error: expected operator before '%s'"%expr_str[op_startpos:]) # to avoid confusion between && and &, and || and |, @@ -536,11 +541,11 @@ def _partition(cls, expr_str): return left_str, op_str, right_str # operator found! now get the left hand side left_str = expr_str[:op_startpos].strip() - + return left_str, op_str, expr_str[op_endpos+1:].strip() @staticmethod - def _scanBrackets(expr_str, fromIndex = 0): + def _scanBrackets(expr_str, fromIndex=0): """Looks for matching brackets. >>> Expression._scanBrackets('abcde') @@ -572,8 +577,8 @@ def _scanBrackets(expr_str, fromIndex = 0): if startpos != -1 or endpos != -1: raise ValueError("expression syntax error (non-matching brackets?)") return (startpos, endpos) - - def code(self, prefix = '', brackets = True, name_filter = None): + + def code(self, prefix='', brackets=True, name_filter=None): """Format an expression as a string. @param prefix: An optional prefix. @type prefix: string @@ -585,9 +590,10 @@ def code(self, prefix = '', brackets = True, name_filter = None): lbracket = "(" if brackets else "" rbracket = ")" if brackets else "" if not self._op: - if not self.lhs: return '' + if not self.lhs: + return '' if isinstance(self.lhs, int): - return self.lhs + return self.lhs elif self.lhs in block_types: return 'IsDerivedType(%s::TYPE)' % self.lhs else: @@ -630,20 +636,20 @@ def get_terminals(self): yield terminal elif self.rhs: yield self.rhs - + def __getattr__(self, name): - if (name == 'lhs'): + if name == 'lhs': return getattr(self, '_left') - if (name == 'rhs'): + if name == 'rhs': return getattr(self, '_right') - if (name == 'op'): + if name == 'op': return getattr(self, '_op') return object.__getattribute__(self, name) - # ducktyping: pretend we're also a string with isdigit() method def isdigit(self): + """ducktyping: pretend we're also a string with isdigit() method""" return False - + class Expr(Expression): """ Represents a mathmatical expression? @@ -656,19 +662,19 @@ class Expr(Expression): @ivar rhs: The right hand side of the expression? @type rhs: string """ - def __init__(self, n, name_filter = None): + def __init__(self, n, name_filter=None): """ This constructor takes the expression in the form of a string and tokenizes it into left-hand side, operator, right hand side, and something called clhs. @param n: The expression to tokenize. @type n: string """ Expression.__init__(self, n, name_filter) - - def code(self, prefix = '', brackets = True, name_filter = None): + + def code(self, prefix='', brackets=True, name_filter=None): if not name_filter: name_filter = member_name return Expression.code(self, prefix, brackets, name_filter) - + class Option: """ This class represents an option in an option list. @@ -688,10 +694,10 @@ def __init__(self, element): assert element.tagName == 'option' parent = element.parentNode #sisters = parent.getElementsByTagName('option') - + # member attributes - self.value = element.getAttribute('value') - self.name = element.getAttribute('name') + self.value = element.getAttribute('value') + self.name = element.getAttribute('name') if element.firstChild: assert element.firstChild.nodeType == Node.TEXT_NODE self.description = element.firstChild.nodeValue.strip() @@ -701,7 +707,7 @@ def __init__(self, element): class Member: """ - This class represents a member variable? + This class represents the nif.xml tag. @ivar name: The name of this member variable. Comes from the "name" attribute of the tag. @type name: string @ivar type: The type of this member variable. Comes from the "type" attribute of the tag. @@ -730,7 +736,7 @@ class Member: @ivar userver2: The user version 2 where this member exists. Comes from the "userver2" attribute of the tag. @type userver2: string @ivar vercond: The version condition of this member variable. Comes from the "vercond" attribute of the tag. - @type vercond: Eval + @type vercond: Eval @ivar is_public: Whether this member will be declared public. Comes from the "public" attribute of the tag. @type is_public: string @ivar is_abstract: Whether this member is abstract. This means that it does not factor into read/write. @@ -774,7 +780,7 @@ def __init__(self, element): """ This constructor converts an XML element into a Member object. Some sort of processing is applied to the various variables that are copied from the XML tag... - Seems to be trying to set reasonable defaults for certain types, and put things into C++ format generally. + Seems to be trying to set reasonable defaults for certain types, and put things into C++ format generally. @param prefix: An optional prefix used in some situations? @type prefix: string @return The expression formatted into a string? @@ -783,7 +789,7 @@ def __init__(self, element): assert element.tagName == 'add' parent = element.parentNode sisters = parent.getElementsByTagName('add') - + # member attributes self.name = element.getAttribute('name') self.suffix = element.getAttribute('suffix') @@ -816,7 +822,7 @@ def __init__(self, element): self.description = "Unknown." else: self.description = "" - + # Format default value so that it can be used in a C++ initializer list if not self.default and (not self.arr1.lhs and not self.arr2.lhs): if self.type in ["unsigned int", "unsigned short", "byte", "int", "short", "char"]: @@ -832,7 +838,7 @@ def __init__(self, element): elif self.type == "Char8String": pass elif self.type == "StringOffset": - self.default = "-1"; + self.default = "-1" elif self.type in basic_names: self.default = "0" elif self.type in flag_names or self.type in enum_names: @@ -852,10 +858,10 @@ def __init__(self, element): elif self.type in ["Ref", "Ptr", "bool", "Vector3"]: pass elif self.default.find(',') != -1: - pass + pass else: self.default = "(%s)%s"%(class_name(self.type), self.default) - + # calculate other stuff self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') self.type_is_native = self.name in native_types # true if the type is implemented natively @@ -886,11 +892,11 @@ def __init__(self, element): sis_name = sis.getAttribute('name') sis_arr1 = Expr(sis.getAttribute('arr1')) sis_arr2 = Expr(sis.getAttribute('arr2')) - sis_cond = Expr(sis.getAttribute('cond')) + sis_cond = Expr(sis.getAttribute('cond')) if sis_arr1.lhs == self.name and (not sis_arr1.rhs or sis_arr1.rhs.isdigit()): - self.arr1_ref.append(sis_name) + self.arr1_ref.append(sis_name) if sis_arr2.lhs == self.name and (not sis_arr2.rhs or sis_arr2.rhs.isdigit()): - self.arr2_ref.append(sis_name) + self.arr2_ref.append(sis_name) if sis_cond.lhs == self.name: self.cond_ref.append(sis_name) sis = sis.nextSibling @@ -904,27 +910,32 @@ def __init__(self, element): self.carr2_ref = [member_name(n) for n in self.arr2_ref] self.ccond_ref = [member_name(n) for n in self.cond_ref] - # construction - # don't construct anything that hasn't been declared - # don't construct if it has no default def code_construct(self): + """ + Class construction + don't construct anything that hasn't been declared + don't construct if it has no default + """ if self.default and not self.is_duplicate: return "%s(%s)"%(self.cname, self.default) - # declaration - def code_declare(self, prefix = ""): # prefix is used to tag local variables only + def code_declare(self, prefix=""): + """ + Class member declaration + prefix is used to tag local variables only + """ result = self.ctype suffix1 = "" suffix2 = "" keyword = "" if not self.is_duplicate: # is dimension for one or more arrays - if self.arr1_ref: - if not self.arr1 or not self.arr1.lhs: # Simple Scalar - keyword = "mutable " - elif self.arr2_ref: # 1-dimensional dynamic array - keyword = "mutable " - elif self.is_calculated: - keyword = "mutable " + if self.arr1_ref: + if not self.arr1 or not self.arr1.lhs: # Simple Scalar + keyword = "mutable " + elif self.arr2_ref: # 1-dimensional dynamic array + keyword = "mutable " + elif self.is_calculated: + keyword = "mutable " if self.ctemplate: if result != "*": @@ -934,9 +945,9 @@ def code_declare(self, prefix = ""): # prefix is used to tag local variables onl if self.arr1.lhs: if self.arr1.lhs.isdigit(): if self.arr2.lhs and self.arr2.lhs.isdigit(): - result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) + result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) else: - result = "array<%s,%s >"%(self.arr1.lhs, result) + result = "array<%s,%s >"%(self.arr1.lhs, result) else: if self.arr2.lhs and self.arr2.lhs.isdigit(): result = "vector< array<%s,%s > >"%(self.arr2.lhs, result) @@ -948,70 +959,73 @@ def code_declare(self, prefix = ""): # prefix is used to tag local variables onl result = keyword + result + " " + prefix + self.cname + suffix1 + suffix2 + ";" return result - def getter_declare(self, scope = "", suffix = ""): - ltype = self.ctype - if self.ctemplate: - if ltype != "*": - ltype += "<%s >"%self.ctemplate - else: - ltype = "%s *"%self.ctemplate - if self.arr1.lhs: - if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s > "%(self.arr1.lhs, ltype) - # ltype = ltype - else: - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype) - else: - ltype = "vector<%s >"%ltype - if self.arr2.lhs: - if self.arr2.lhs.isdigit(): - if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s >"%(self.arr2.lhs,ltype) - # ltype = ltype - else: - ltype = "vector<%s >"%ltype - result = ltype + " " + scope + "Get" + self.cname[0:1].upper() + self.cname[1:] + "() const" + suffix - return result - - def setter_declare(self, scope = "", suffix = ""): - ltype = self.ctype - if self.ctemplate: - if ltype != "*": - ltype += "<%s >"%self.ctemplate - else: - ltype = "%s *"%self.ctemplate - if self.arr1.lhs: - if self.arr1.lhs.isdigit(): - # ltype = "const %s&"%ltype - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs,self.arr2.lhs, ltype) + def getter_declare(self, scope="", suffix=""): + """Getter member function declaration.""" + ltype = self.ctype + if self.ctemplate: + if ltype != "*": + ltype += "<%s >"%self.ctemplate + else: + ltype = "%s *"%self.ctemplate + if self.arr1.lhs: + if self.arr1.lhs.isdigit(): + ltype = "array<%s,%s > "%(self.arr1.lhs, ltype) + # ltype = ltype + else: + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype) + else: + ltype = "vector<%s >"%ltype + if self.arr2.lhs: + if self.arr2.lhs.isdigit(): + if self.arr1.lhs.isdigit(): + ltype = "array<%s,%s >"%(self.arr2.lhs, ltype) + # ltype = ltype + else: + ltype = "vector<%s >"%ltype + result = ltype + " " + scope + "Get" + self.cname[0:1].upper() + self.cname[1:] + "() const" + suffix + return result + + def setter_declare(self, scope="", suffix=""): + """Setter member function declaration.""" + ltype = self.ctype + if self.ctemplate: + if ltype != "*": + ltype += "<%s >"%self.ctemplate else: - ltype = "const array<%s,%s >& "%(self.arr1.lhs,ltype) - - else: - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype) - else: - ltype = "const vector<%s >&"%ltype - else: - if not self.type in basic_names: - ltype = "const %s &"%ltype - - result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix - return result + ltype = "%s *"%self.ctemplate + if self.arr1.lhs: + if self.arr1.lhs.isdigit(): + # ltype = "const %s&"%ltype + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype) + else: + ltype = "const array<%s,%s >& "%(self.arr1.lhs, ltype) + else: + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype) + else: + ltype = "const vector<%s >&"%ltype + else: + if not self.type in basic_names: + ltype = "const %s &"%ltype + + result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix + return result class Version: + """This class represents the nif.xml tag.""" def __init__(self, element): self.num = element.getAttribute('num') self.description = element.firstChild.nodeValue.strip() - + class Basic: + """This class represents the nif.xml tag.""" def __init__(self, element): global native_types self.name = element.getAttribute('name') - assert(self.name) # debug + assert self.name # debug self.cname = class_name(self.name) if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE: self.description = element.firstChild.nodeValue.strip() @@ -1041,34 +1055,35 @@ def __init__(self, element): self.options = [] class Enum(Basic): - def __init__(self, element): - Basic.__init__(self, element) - - self.storage = element.getAttribute('storage') - self.prefix = element.getAttribute('prefix') - # Find the native storage type - self.storage = basic_types[self.storage].nativetype - self.description = element.firstChild.nodeValue.strip() - - self.nativetype = self.cname - native_types[self.name] = self.nativetype - - # Locate all special enumeration options - for option in element.getElementsByTagName('option'): - if self.prefix and option.hasAttribute('name'): - option.setAttribute('name', self.prefix + "_" + option.getAttribute('name')) - x = Option(option) - self.options.append(x) + """This class represents the nif.xml tag.""" + def __init__(self, element): + Basic.__init__(self, element) + + self.storage = element.getAttribute('storage') + self.prefix = element.getAttribute('prefix') + # Find the native storage type + self.storage = basic_types[self.storage].nativetype + self.description = element.firstChild.nodeValue.strip() + + self.nativetype = self.cname + native_types[self.name] = self.nativetype + + # Locate all special enumeration options + for option in element.getElementsByTagName('option'): + if self.prefix and option.hasAttribute('name'): + option.setAttribute('name', self.prefix + "_" + option.getAttribute('name')) + self.options.append(Option(option)) class Flag(Enum): - def __init__(self, element): - Enum.__init__(self, element) - for option in self.options: - option.bit = option.value - option.value = 1 << int(option.value) - + """This class represents the nif.xml tag.""" + def __init__(self, element): + Enum.__init__(self, element) + for option in self.options: + option.bit = option.value + option.value = 1 << int(option.value) + class Compound(Basic): - # create a compound type from the XML attributes + """This class represents the nif.xml tag.""" def __init__(self, element): Basic.__init__(self, element) @@ -1093,12 +1108,12 @@ def __init__(self, element): # recursively defined structures... so we remove # this one to avoid the problem # as a result a minority of nifs won't load - continue + continue #********************* #** NIFLIB HACK END ** #********************* self.members.append(x) - + # detect templates #if x.type == 'TEMPLATE': # self.template = True @@ -1106,44 +1121,41 @@ def __init__(self, element): # self.template = True # detect argument - if x.uses_argument: - self.argument = True - else: - self.argument = False + self.argument = bool(x.uses_argument) # detect links & crossrefs - y = None + mem = None try: - y = basic_types[x.type] + mem = basic_types[x.type] except KeyError: try: - y = compound_types[x.type] + mem = compound_types[x.type] except KeyError: pass - if y: - if y.has_links: + if mem: + if mem.has_links: self.has_links = True - if y.has_crossrefs: + if mem.has_crossrefs: self.has_crossrefs = True - + # create duplicate chains for items that need it (only valid in current object scope) # prefer to use iterators to avoid O(n^2) but I dont know how to reset iterators - for x in self.members: - atx = False - for y in self.members: - if atx: - if x.name == y.name: # duplicate - x.next_dup = y - break - elif x == y: - atx = True + for outer in self.members: + atx = False + for inner in self.members: + if atx: + if outer.name == inner.name: # duplicate + outer.next_dup = inner + break + elif outer == inner: + atx = True def code_construct(self): # constructor result = '' first = True - for y in self.members: - y_code_construct = y.code_construct() + for mem in self.members: + y_code_construct = mem.code_construct() if y_code_construct: if not first: result += ', ' + y_code_construct @@ -1153,20 +1165,21 @@ def code_construct(self): return result def code_include_h(self): - if self.nativetype: return "" + if self.nativetype: + return "" result = "" # include all required structures used_structs = [] - for y in self.members: + for mem in self.members: file_name = None - if y.type != self.name: - if y.type in compound_names: - if not compound_types[y.type].nativetype: - file_name = "%s%s.h"%(self.gen_file_prefix, y.ctype) - elif y.type in basic_names: - if basic_types[y.type].nativetype == "Ref": + if mem.type != self.name: + if mem.type in compound_names: + if not compound_types[mem.type].nativetype: + file_name = "%s%s.h"%(self.gen_file_prefix, mem.ctype) + elif mem.type in basic_names: + if basic_types[mem.type].nativetype == "Ref": file_name = "%sRef.h"%(self.root_file_prefix) if file_name and file_name not in used_structs: used_structs.append( file_name ) @@ -1175,31 +1188,31 @@ def code_include_h(self): for file_name in used_structs: result += '#include "%s"\n'%file_name return result - + def code_fwd_decl(self): - if self.nativetype: return "" - + if self.nativetype: + return "" result = "" # forward declaration of blocks used_blocks = [] - for y in self.members: - if y.template in block_names and y.template != self.name: - if not y.ctemplate in used_blocks: - used_blocks.append( y.ctemplate ) + for mem in self.members: + if mem.template in block_names and mem.template != self.name: + if not mem.ctemplate in used_blocks: + used_blocks.append( mem.ctemplate ) if used_blocks: result += '\n// Forward define of referenced NIF objects\n' for fwd_class in used_blocks: result += 'class %s;\n'%fwd_class - return result def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): - if self.nativetype: return "" - + if self.nativetype: + return "" + if not usedirs: - gen_dir = self.gen_file_prefix - obj_dir = self.obj_file_prefix + gen_dir = self.gen_file_prefix + obj_dir = self.obj_file_prefix result = [] @@ -1207,19 +1220,19 @@ def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): result.append('#include "%s%s.h"\n'%(gen_dir, self.cname)) elif self.name in block_names: result.append('#include "%s%s.h"\n'%(obj_dir, self.cname)) - else: assert(False) # bug + else: assert False # bug # include referenced blocks used_blocks = [] - for y in self.members: - if y.template in block_names and y.template != self.name: - file_name = '#include "%s%s.h"\n'%(obj_dir, y.ctemplate) + for mem in self.members: + if mem.template in block_names and mem.template != self.name: + file_name = '#include "%s%s.h"\n'%(obj_dir, mem.ctemplate) if file_name not in used_blocks: used_blocks.append( file_name ) - if y.type in compound_names: - subblock = compound_types[y.type] + if mem.type in compound_names: + subblock = compound_types[mem.type] used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir)) - for terminal in y.cond.get_terminals(): + for terminal in mem.cond.get_terminals(): if terminal in block_types: used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal)) for file_name in sorted(set(used_blocks)): @@ -1230,37 +1243,38 @@ def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): def code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None): return ''.join(self.code_include_cpp_set(True, gen_dir, obj_dir)) - # find member by name def find_member(self, name, inherit=False): - for y in self.members: - if y.name == name: - return y - return None - - # find first reference of name in class + """Find member by name""" + for mem in self.members: + if mem.name == name: + return mem + return None + def find_first_ref(self, name): - for y in self.members: - if y.arr1 and y.arr1.lhs == name: - return y - elif y.arr2 and y.arr2.lhs == name: - return y - return None - - # Tests recursively for members with an array size. + """Find first reference of name in class.""" + for mem in self.members: + if mem.arr1 and mem.arr1.lhs == name: + return mem + elif mem.arr2 and mem.arr2.lhs == name: + return mem + return None + def has_arr(self): - for y in self.members: - if y.arr1.lhs or (y.type in compound_types and compound_types[y.type].has_arr()): + """Tests recursively for members with an array size.""" + for mem in self.members: + if mem.arr1.lhs or (mem.type in compound_types and compound_types[mem.type].has_arr()): return True return False class Block(Compound): + """This class represents the nif.xml tag.""" def __init__(self, element): Compound.__init__(self, element) #the relative path to files in the gen folder self.gen_file_prefix = "../gen/" #the relative path to files in the obj folder self.obj_file_prefix = "" - + self.is_ancestor = (element.getAttribute('abstract') == "1") inherit = element.getAttribute('inherit') if inherit: @@ -1288,70 +1302,70 @@ def code_include_h(self): #include """ result += Compound.code_include_h(self) return result - + # find member by name def find_member(self, name, inherit=False): - ret = Compound.find_member(self, name) - if not ret and inherit and self.inherit: - ret = self.inherit.find_member(name, inherit) - return ret + ret = Compound.find_member(self, name) + if not ret and inherit and self.inherit: + ret = self.inherit.find_member(name, inherit) + return ret # find first reference of name in class def find_first_ref(self, name): - ret = None - if self.inherit: - ret = self.inherit.find_first_ref(name) - if not ret: - ret = Compound.find_first_ref(self, name) - return ret - + ret = None + if self.inherit: + ret = self.inherit.find_first_ref(name) + if not ret: + ret = Compound.find_first_ref(self, name) + return ret + # # import elements into our code generating classes # # import via "import nifxml" from . if os.path.exists("nif.xml"): - doc = parse("nif.xml") + XML = parse("nif.xml") # import via "import docsys" from .. elif os.path.exists("docsys/nif.xml"): - doc = parse("docsys/nif.xml") + XML = parse("docsys/nif.xml") # new submodule system elif os.path.exists("nifxml/nif.xml"): - doc = parse("nifxml/nif.xml") + XML = parse("nifxml/nif.xml") else: raise ImportError("nif.xml not found") -for element in doc.getElementsByTagName('version'): - x = Version(element) +for el in XML.getElementsByTagName('version'): + x = Version(el) version_types[x.num] = x version_names.append(x.num) -for element in doc.getElementsByTagName('basic'): - x = Basic(element) +for el in XML.getElementsByTagName('basic'): + x = Basic(el) assert not x.name in basic_types basic_types[x.name] = x basic_names.append(x.name) -for element in doc.getElementsByTagName('enum'): - x = Enum(element) +for el in XML.getElementsByTagName('enum'): + x = Enum(el) assert not x.name in enum_types enum_types[x.name] = x enum_names.append(x.name) -for element in doc.getElementsByTagName('bitflags'): - x = Flag(element) +for el in XML.getElementsByTagName('bitflags'): + x = Flag(el) assert not x.name in flag_types flag_types[x.name] = x flag_names.append(x.name) - -for element in doc.getElementsByTagName("compound"): - x = Compound(element) + +for el in XML.getElementsByTagName("compound"): + x = Compound(el) assert not x.name in compound_types compound_types[x.name] = x compound_names.append(x.name) -for element in doc.getElementsByTagName("niobject"): - x = Block(element) +for el in XML.getElementsByTagName("niobject"): + x = Block(el) assert not x.name in block_types block_types[x.name] = x block_names.append(x.name) From af99c64138c9ef07641f1ea954a07634bff28044 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Thu, 21 Dec 2017 00:42:53 -0500 Subject: [PATCH 20/27] Move niflib code out of nifxml A function, parse_XML now must be called to actually fill the Blocks/Compounds. This gives another script a chance to monkeypatch the classes before calling the function. Moved all the niflib specific methods to gen_niflib and then monkeypatch the classes with them. Pass native types dict as a parameter instead of referencing it globally. Fixed nifxml_doc generation also. --- gen_niflib.py | 332 +++++++++++++++++++++++++++++++++++++++++- nifxml.py | 392 ++++++++------------------------------------------ nifxml_doc.py | 27 +++- 3 files changed, 415 insertions(+), 336 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index d8ff066..e594a50 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -84,10 +84,334 @@ import io import itertools -from nifxml import Block, native_types, NATIVETYPES -from nifxml import block_types, basic_types, compound_types, enum_types, flag_types -from nifxml import block_names, compound_names -from nifxml import scanBrackets, define_name +from nifxml import Member, Compound, Block, native_types, NATIVETYPES +from nifxml import block_types, basic_types, compound_types, enum_types, flag_types, version_types +from nifxml import block_names, basic_names, compound_names, enum_names, flag_names, version_names +from nifxml import scanBrackets, define_name, parse_XML + + +# The relative path to the project root for compounds and NiObjects (Compound and Block) +ROOT_FILE_PREFIX = "../" + +# The relative path to NiObject for compounds (Block and Compound) +CMP_OBJ_FILE_PREFIX = "../obj/" +CMP_GEN_FILE_PREFIX = "" + +# The relative path to compounds for NiObject (Compound and Block) +BLK_GEN_FILE_PREFIX = "../gen/" +BLK_OBJ_FILE_PREFIX = "" + +# The XML to niflib type mapping +NATIVETYPES = { + 'bool' : 'bool', + 'byte' : 'byte', + 'uint' : 'unsigned int', + 'ulittle32' : 'unsigned int', + 'ushort' : 'unsigned short', + 'int' : 'int', + 'short' : 'short', + 'BlockTypeIndex' : 'unsigned short', + 'char' : 'byte', + 'FileVersion' : 'unsigned int', + 'Flags' : 'unsigned short', + 'float' : 'float', + 'hfloat' : 'hfloat', + 'HeaderString' : 'HeaderString', + 'LineString' : 'LineString', + 'Ptr' : '*', + 'Ref' : 'Ref', + 'StringOffset' : 'unsigned int', + 'StringIndex' : 'IndexString', + 'SizedString' : 'string', + 'string' : 'IndexString', + 'Color3' : 'Color3', + 'Color4' : 'Color4', + #'ByteColor3' : 'ByteColor3', # TODO: Niflib type + 'ByteColor4' : 'ByteColor4', + 'FilePath' : 'IndexString', + 'Vector3' : 'Vector3', + 'Vector4' : 'Vector4', + 'Quaternion' : 'Quaternion', + 'Matrix22' : 'Matrix22', + 'Matrix33' : 'Matrix33', + 'Matrix34' : 'Matrix34', + 'Matrix44' : 'Matrix44', + 'hkMatrix3' : 'InertiaMatrix', + 'ShortString' : 'ShortString', + 'Key' : 'Key', + 'QuatKey' : 'Key', + 'TexCoord' : 'TexCoord', + 'Triangle' : 'Triangle', + 'BSVertexData' : 'BSVertexData', + 'BSVertexDataSSE' : 'BSVertexData', + #'BSVertexDesc' : 'BSVertexDesc' +} + +# +# Member Patching +# + +def member_code_construct(self): + """ + Class construction + don't construct anything that hasn't been declared + don't construct if it has no default + """ + if self.default and not self.is_duplicate: + return "%s(%s)"%(self.cname, self.default) + +def member_code_declare(self, prefix=""): + """ + Class member declaration + prefix is used to tag local variables only + """ + result = self.ctype + suffix1 = "" + suffix2 = "" + keyword = "" + if not self.is_duplicate: # is dimension for one or more arrays + if self.arr1_ref: + if not self.arr1 or not self.arr1.lhs: # Simple Scalar + keyword = "mutable " + elif self.arr2_ref: # 1-dimensional dynamic array + keyword = "mutable " + elif self.is_calculated: + keyword = "mutable " + + if self.ctemplate: + if result != "*": + result += "<%s >"%self.ctemplate + else: + result = "%s *"%self.ctemplate + if self.arr1.lhs: + if self.arr1.lhs.isdigit(): + if self.arr2.lhs and self.arr2.lhs.isdigit(): + result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) + else: + result = "array<%s,%s >"%(self.arr1.lhs, result) + else: + if self.arr2.lhs and self.arr2.lhs.isdigit(): + result = "vector< array<%s,%s > >"%(self.arr2.lhs, result) + else: + if self.arr2.lhs: + result = "vector< vector<%s > >"%result + else: + result = "vector<%s >"%result + result = keyword + result + " " + prefix + self.cname + suffix1 + suffix2 + ";" + return result + +def member_getter_declare(self, scope="", suffix=""): + """Getter member function declaration.""" + ltype = self.ctype + if self.ctemplate: + if ltype != "*": + ltype += "<%s >"%self.ctemplate + else: + ltype = "%s *"%self.ctemplate + if self.arr1.lhs: + if self.arr1.lhs.isdigit(): + ltype = "array<%s,%s > "%(self.arr1.lhs, ltype) + # ltype = ltype + else: + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype) + else: + ltype = "vector<%s >"%ltype + if self.arr2.lhs: + if self.arr2.lhs.isdigit(): + if self.arr1.lhs.isdigit(): + ltype = "array<%s,%s >"%(self.arr2.lhs, ltype) + # ltype = ltype + else: + ltype = "vector<%s >"%ltype + result = ltype + " " + scope + "Get" + self.cname[0:1].upper() + self.cname[1:] + "() const" + suffix + return result + +def member_setter_declare(self, scope="", suffix=""): + """Setter member function declaration.""" + ltype = self.ctype + if self.ctemplate: + if ltype != "*": + ltype += "<%s >"%self.ctemplate + else: + ltype = "%s *"%self.ctemplate + if self.arr1.lhs: + if self.arr1.lhs.isdigit(): + # ltype = "const %s&"%ltype + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype) + else: + ltype = "const array<%s,%s >& "%(self.arr1.lhs, ltype) + else: + if self.arr2.lhs and self.arr2.lhs.isdigit(): + ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype) + else: + ltype = "const vector<%s >&"%ltype + else: + if not self.type in basic_names: + ltype = "const %s &"%ltype + + result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix + return result + +Member.code_construct = member_code_construct +Member.code_declare = member_code_declare +Member.getter_declare = member_getter_declare +Member.setter_declare = member_setter_declare + +# +# Compound Patching +# + +def compound_code_construct(self): + # constructor + result = '' + first = True + for mem in self.members: + y_code_construct = mem.code_construct() + if y_code_construct: + if not first: + result += ', ' + y_code_construct + else: + result += ' : ' + y_code_construct + first = False + return result + +def compound_code_include_h(self): + if self.nativetype: + return "" + + result = "" + + # include all required structures + used_structs = [] + for mem in self.members: + file_name = None + if mem.type != self.name: + if mem.type in compound_names: + if not compound_types[mem.type].nativetype: + file_name = "%s%s.h"%(self.gen_file_prefix, mem.ctype) + elif mem.type in basic_names: + if basic_types[mem.type].nativetype == "Ref": + file_name = "%sRef.h"%(self.root_file_prefix) + if file_name and file_name not in used_structs: + used_structs.append( file_name ) + if used_structs: + result += "\n// Include structures\n" + for file_name in used_structs: + result += '#include "%s"\n'%file_name + return result + +def compound_code_fwd_decl(self): + if self.nativetype: + return "" + result = "" + + # forward declaration of blocks + used_blocks = [] + for mem in self.members: + if mem.template in block_names and mem.template != self.name: + if not mem.ctemplate in used_blocks: + used_blocks.append( mem.ctemplate ) + if used_blocks: + result += '\n// Forward define of referenced NIF objects\n' + for fwd_class in used_blocks: + result += 'class %s;\n'%fwd_class + return result + +def compound_code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): + if self.nativetype: + return "" + + if not usedirs: + gen_dir = self.gen_file_prefix + obj_dir = self.obj_file_prefix + + result = [] + + if self.name in compound_names: + result.append('#include "%s%s.h"\n'%(gen_dir, self.cname)) + elif self.name in block_names: + result.append('#include "%s%s.h"\n'%(obj_dir, self.cname)) + else: assert False # bug + + # include referenced blocks + used_blocks = [] + for mem in self.members: + if mem.template in block_names and mem.template != self.name: + file_name = '#include "%s%s.h"\n'%(obj_dir, mem.ctemplate) + if file_name not in used_blocks: + used_blocks.append( file_name ) + if mem.type in compound_names: + subblock = compound_types[mem.type] + used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir)) + for terminal in mem.cond.get_terminals(): + if terminal in block_types: + used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal)) + for file_name in sorted(set(used_blocks)): + result.append(file_name) + + return result + +def compound_code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None): + return ''.join(self.code_include_cpp_set(True, gen_dir, obj_dir)) + + +Compound.root_file_prefix = ROOT_FILE_PREFIX +Compound.gen_file_prefix = CMP_GEN_FILE_PREFIX +Compound.obj_file_prefix = CMP_OBJ_FILE_PREFIX +Compound.code_construct = compound_code_construct +Compound.code_include_h = compound_code_include_h +Compound.code_fwd_decl = compound_code_fwd_decl +Compound.code_include_cpp_set = compound_code_include_cpp_set +Compound.code_include_cpp = compound_code_include_cpp + +# +# Block Patching +# + +def block_code_include_h(self): + result = "" + if self.inherit: + result += '#include "%s.h"\n'%self.inherit.cname + else: + result += """#include "../RefObject.h" +#include "../Type.h" +#include "../Ref.h" +#include "../nif_basic_types.h" +#include +#include +#include +#include +#include +#include +#include +#include """ + result += Compound.code_include_h(self) + return result + +Block.gen_file_prefix = BLK_GEN_FILE_PREFIX +Block.obj_file_prefix = BLK_OBJ_FILE_PREFIX +Block.code_include_h = block_code_include_h + +# +# Parse XML after patching classes +# + +parse_XML(NATIVETYPES) + +assert version_types +assert version_names +assert basic_types +assert basic_names +assert compound_types +assert compound_names +assert block_types +assert block_names +assert enum_types +assert enum_names +assert flag_types +assert flag_names # # global data diff --git a/nifxml.py b/nifxml.py index 12665cd..318d94e 100644 --- a/nifxml.py +++ b/nifxml.py @@ -99,50 +99,7 @@ block_names = [] version_names = [] -NATIVETYPES = { - 'bool' : 'bool', - 'byte' : 'byte', - 'uint' : 'unsigned int', - 'ulittle32' : 'unsigned int', - 'ushort' : 'unsigned short', - 'int' : 'int', - 'short' : 'short', - 'BlockTypeIndex' : 'unsigned short', - 'char' : 'byte', - 'FileVersion' : 'unsigned int', - 'Flags' : 'unsigned short', - 'float' : 'float', - 'hfloat' : 'hfloat', - 'HeaderString' : 'HeaderString', - 'LineString' : 'LineString', - 'Ptr' : '*', - 'Ref' : 'Ref', - 'StringOffset' : 'unsigned int', - 'StringIndex' : 'IndexString', - 'SizedString' : 'string', - 'string' : 'IndexString', - 'Color3' : 'Color3', - 'Color4' : 'Color4', - #'ByteColor3' : 'ByteColor3', # TODO: Niflib type - 'ByteColor4' : 'ByteColor4', - 'FilePath' : 'IndexString', - 'Vector3' : 'Vector3', - 'Vector4' : 'Vector4', - 'Quaternion' : 'Quaternion', - 'Matrix22' : 'Matrix22', - 'Matrix33' : 'Matrix33', - 'Matrix34' : 'Matrix34', - 'Matrix44' : 'Matrix44', - 'hkMatrix3' : 'InertiaMatrix', - 'ShortString' : 'ShortString', - 'Key' : 'Key', - 'QuatKey' : 'Key', - 'TexCoord' : 'TexCoord', - 'Triangle' : 'Triangle', - 'BSVertexData' : 'BSVertexData', - 'BSVertexDataSSE' : 'BSVertexData', - #'BSVertexDesc' : 'BSVertexDesc' -} +NATIVETYPES = {} # # HTML Template class @@ -170,7 +127,8 @@ def parse(self, file_name): #Loop through all variables, replacing them in the template text for i in self.vars: - txt = txt.replace('{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict')) + if self.vars[i] is not None: + txt = txt.replace('{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict')) #return result return txt @@ -910,109 +868,6 @@ def __init__(self, element): self.carr2_ref = [member_name(n) for n in self.arr2_ref] self.ccond_ref = [member_name(n) for n in self.cond_ref] - def code_construct(self): - """ - Class construction - don't construct anything that hasn't been declared - don't construct if it has no default - """ - if self.default and not self.is_duplicate: - return "%s(%s)"%(self.cname, self.default) - - def code_declare(self, prefix=""): - """ - Class member declaration - prefix is used to tag local variables only - """ - result = self.ctype - suffix1 = "" - suffix2 = "" - keyword = "" - if not self.is_duplicate: # is dimension for one or more arrays - if self.arr1_ref: - if not self.arr1 or not self.arr1.lhs: # Simple Scalar - keyword = "mutable " - elif self.arr2_ref: # 1-dimensional dynamic array - keyword = "mutable " - elif self.is_calculated: - keyword = "mutable " - - if self.ctemplate: - if result != "*": - result += "<%s >"%self.ctemplate - else: - result = "%s *"%self.ctemplate - if self.arr1.lhs: - if self.arr1.lhs.isdigit(): - if self.arr2.lhs and self.arr2.lhs.isdigit(): - result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) - else: - result = "array<%s,%s >"%(self.arr1.lhs, result) - else: - if self.arr2.lhs and self.arr2.lhs.isdigit(): - result = "vector< array<%s,%s > >"%(self.arr2.lhs, result) - else: - if self.arr2.lhs: - result = "vector< vector<%s > >"%result - else: - result = "vector<%s >"%result - result = keyword + result + " " + prefix + self.cname + suffix1 + suffix2 + ";" - return result - - def getter_declare(self, scope="", suffix=""): - """Getter member function declaration.""" - ltype = self.ctype - if self.ctemplate: - if ltype != "*": - ltype += "<%s >"%self.ctemplate - else: - ltype = "%s *"%self.ctemplate - if self.arr1.lhs: - if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s > "%(self.arr1.lhs, ltype) - # ltype = ltype - else: - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype) - else: - ltype = "vector<%s >"%ltype - if self.arr2.lhs: - if self.arr2.lhs.isdigit(): - if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s >"%(self.arr2.lhs, ltype) - # ltype = ltype - else: - ltype = "vector<%s >"%ltype - result = ltype + " " + scope + "Get" + self.cname[0:1].upper() + self.cname[1:] + "() const" + suffix - return result - - def setter_declare(self, scope="", suffix=""): - """Setter member function declaration.""" - ltype = self.ctype - if self.ctemplate: - if ltype != "*": - ltype += "<%s >"%self.ctemplate - else: - ltype = "%s *"%self.ctemplate - if self.arr1.lhs: - if self.arr1.lhs.isdigit(): - # ltype = "const %s&"%ltype - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype) - else: - ltype = "const array<%s,%s >& "%(self.arr1.lhs, ltype) - else: - if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype) - else: - ltype = "const vector<%s >&"%ltype - else: - if not self.type in basic_names: - ltype = "const %s &"%ltype - - result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix - return result - class Version: """This class represents the nif.xml tag.""" def __init__(self, element): @@ -1021,9 +876,7 @@ def __init__(self, element): class Basic: """This class represents the nif.xml tag.""" - def __init__(self, element): - global native_types - + def __init__(self, element, ntypes): self.name = element.getAttribute('name') assert self.name # debug self.cname = class_name(self.name) @@ -1035,34 +888,35 @@ def __init__(self, element): self.description = "" self.count = element.getAttribute('count') + self.template = (element.getAttribute('istemplate') == "1") + self.options = [] self.is_link = False self.is_crossref = False self.has_links = False self.has_crossrefs = False - self.nativetype = NATIVETYPES.get(self.name) - if self.nativetype: - native_types[self.name] = self.nativetype - if self.nativetype == "Ref": - self.is_link = True - self.has_links = True - if self.nativetype == "*": - self.is_crossref = True - self.has_crossrefs = True - - self.template = (element.getAttribute('istemplate') == "1") - self.options = [] + self.nativetype = None + if ntypes: + self.nativetype = ntypes.get(self.name) + if self.nativetype: + native_types[self.name] = self.nativetype + if self.nativetype == "Ref": + self.is_link = True + self.has_links = True + if self.nativetype == "*": + self.is_crossref = True + self.has_crossrefs = True class Enum(Basic): """This class represents the nif.xml tag.""" - def __init__(self, element): - Basic.__init__(self, element) + def __init__(self, element, ntypes): + Basic.__init__(self, element, ntypes) self.storage = element.getAttribute('storage') self.prefix = element.getAttribute('prefix') # Find the native storage type - self.storage = basic_types[self.storage].nativetype + self.storage = basic_types[self.storage].nativetype if basic_types[self.storage].nativetype else basic_types[self.storage].name self.description = element.firstChild.nodeValue.strip() self.nativetype = self.cname @@ -1076,23 +930,16 @@ def __init__(self, element): class Flag(Enum): """This class represents the nif.xml tag.""" - def __init__(self, element): - Enum.__init__(self, element) + def __init__(self, element, ntypes): + Enum.__init__(self, element, ntypes) for option in self.options: option.bit = option.value option.value = 1 << int(option.value) class Compound(Basic): """This class represents the nif.xml tag.""" - def __init__(self, element): - Basic.__init__(self, element) - - #the relative path to files in the gen folder - self.gen_file_prefix = "" - #the relative path to files in the obj folder - self.obj_file_prefix = "../obj/" - #the relative path to files in the root folder - self.root_file_prefix = "../" + def __init__(self, element, ntypes): + Basic.__init__(self, element, ntypes) self.members = [] # list of all members (list of Member) self.argument = False # does it use an argument? @@ -1150,99 +997,6 @@ def __init__(self, element): elif outer == inner: atx = True - def code_construct(self): - # constructor - result = '' - first = True - for mem in self.members: - y_code_construct = mem.code_construct() - if y_code_construct: - if not first: - result += ', ' + y_code_construct - else: - result += ' : ' + y_code_construct - first = False - return result - - def code_include_h(self): - if self.nativetype: - return "" - - result = "" - - # include all required structures - used_structs = [] - for mem in self.members: - file_name = None - if mem.type != self.name: - if mem.type in compound_names: - if not compound_types[mem.type].nativetype: - file_name = "%s%s.h"%(self.gen_file_prefix, mem.ctype) - elif mem.type in basic_names: - if basic_types[mem.type].nativetype == "Ref": - file_name = "%sRef.h"%(self.root_file_prefix) - if file_name and file_name not in used_structs: - used_structs.append( file_name ) - if used_structs: - result += "\n// Include structures\n" - for file_name in used_structs: - result += '#include "%s"\n'%file_name - return result - - def code_fwd_decl(self): - if self.nativetype: - return "" - result = "" - - # forward declaration of blocks - used_blocks = [] - for mem in self.members: - if mem.template in block_names and mem.template != self.name: - if not mem.ctemplate in used_blocks: - used_blocks.append( mem.ctemplate ) - if used_blocks: - result += '\n// Forward define of referenced NIF objects\n' - for fwd_class in used_blocks: - result += 'class %s;\n'%fwd_class - return result - - def code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=None): - if self.nativetype: - return "" - - if not usedirs: - gen_dir = self.gen_file_prefix - obj_dir = self.obj_file_prefix - - result = [] - - if self.name in compound_names: - result.append('#include "%s%s.h"\n'%(gen_dir, self.cname)) - elif self.name in block_names: - result.append('#include "%s%s.h"\n'%(obj_dir, self.cname)) - else: assert False # bug - - # include referenced blocks - used_blocks = [] - for mem in self.members: - if mem.template in block_names and mem.template != self.name: - file_name = '#include "%s%s.h"\n'%(obj_dir, mem.ctemplate) - if file_name not in used_blocks: - used_blocks.append( file_name ) - if mem.type in compound_names: - subblock = compound_types[mem.type] - used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir)) - for terminal in mem.cond.get_terminals(): - if terminal in block_types: - used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal)) - for file_name in sorted(set(used_blocks)): - result.append(file_name) - - return result - - def code_include_cpp(self, usedirs=False, gen_dir=None, obj_dir=None): - return ''.join(self.code_include_cpp_set(True, gen_dir, obj_dir)) - def find_member(self, name, inherit=False): """Find member by name""" for mem in self.members: @@ -1268,13 +1022,8 @@ def has_arr(self): class Block(Compound): """This class represents the nif.xml tag.""" - def __init__(self, element): - Compound.__init__(self, element) - #the relative path to files in the gen folder - self.gen_file_prefix = "../gen/" - #the relative path to files in the obj folder - self.obj_file_prefix = "" - + def __init__(self, element, ntypes): + Compound.__init__(self, element, ntypes) self.is_ancestor = (element.getAttribute('abstract') == "1") inherit = element.getAttribute('inherit') if inherit: @@ -1283,26 +1032,6 @@ def __init__(self, element): self.inherit = None self.has_interface = (element.getElementsByTagName('interface') != []) - def code_include_h(self): - result = "" - if self.inherit: - result += '#include "%s.h"\n'%self.inherit.cname - else: - result += """#include "../RefObject.h" -#include "../Type.h" -#include "../Ref.h" -#include "../nif_basic_types.h" -#include -#include -#include -#include -#include -#include -#include -#include """ - result += Compound.code_include_h(self) - return result - # find member by name def find_member(self, name, inherit=False): ret = Compound.find_member(self, name) @@ -1335,37 +1064,38 @@ def find_first_ref(self, name): else: raise ImportError("nif.xml not found") -for el in XML.getElementsByTagName('version'): - x = Version(el) - version_types[x.num] = x - version_names.append(x.num) - -for el in XML.getElementsByTagName('basic'): - x = Basic(el) - assert not x.name in basic_types - basic_types[x.name] = x - basic_names.append(x.name) - -for el in XML.getElementsByTagName('enum'): - x = Enum(el) - assert not x.name in enum_types - enum_types[x.name] = x - enum_names.append(x.name) - -for el in XML.getElementsByTagName('bitflags'): - x = Flag(el) - assert not x.name in flag_types - flag_types[x.name] = x - flag_names.append(x.name) - -for el in XML.getElementsByTagName("compound"): - x = Compound(el) - assert not x.name in compound_types - compound_types[x.name] = x - compound_names.append(x.name) - -for el in XML.getElementsByTagName("niobject"): - x = Block(el) - assert not x.name in block_types - block_types[x.name] = x - block_names.append(x.name) +def parse_XML(ntypes=None): + for el in XML.getElementsByTagName('version'): + x = Version(el) + version_types[x.num] = x + version_names.append(x.num) + + for el in XML.getElementsByTagName('basic'): + x = Basic(el, ntypes) + assert not x.name in basic_types + basic_types[x.name] = x + basic_names.append(x.name) + + for el in XML.getElementsByTagName('enum'): + x = Enum(el, ntypes) + assert not x.name in enum_types + enum_types[x.name] = x + enum_names.append(x.name) + + for el in XML.getElementsByTagName('bitflags'): + x = Flag(el, ntypes) + assert not x.name in flag_types + flag_types[x.name] = x + flag_names.append(x.name) + + for el in XML.getElementsByTagName("compound"): + x = Compound(el, ntypes) + assert not x.name in compound_types + compound_types[x.name] = x + compound_names.append(x.name) + + for el in XML.getElementsByTagName("niobject"): + x = Block(el, ntypes) + assert not x.name in block_types + block_types[x.name] = x + block_names.append(x.name) diff --git a/nifxml_doc.py b/nifxml_doc.py index dec3443..9fa970f 100644 --- a/nifxml_doc.py +++ b/nifxml_doc.py @@ -49,12 +49,37 @@ # -------------------------------------------------------------------------- from __future__ import unicode_literals -from nifxml import * + from distutils.dir_util import mkpath +import sys import os import io import itertools +from nifxml import Template +from nifxml import block_types, basic_types, compound_types, enum_types, flag_types, version_types +from nifxml import block_names, basic_names, compound_names, enum_names, flag_names, version_names +from nifxml import parse_XML + +# +# Parse the XML +# + +parse_XML() + +assert version_types +assert version_names +assert basic_types +assert basic_names +assert compound_types +assert compound_names +assert block_types +assert block_names +assert enum_types +assert enum_names +assert flag_types +assert flag_names + # # global data # From 897068f547977d9a7d1b7c7459a76ddf57c9d10c Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Thu, 21 Dec 2017 00:58:05 -0500 Subject: [PATCH 21/27] Fix bug with "Can be used as array size" Wasn't actually looking at the member count attribute. Thanks, Python. --- nifxml_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nifxml_doc.py b/nifxml_doc.py index 9fa970f..8a18c73 100644 --- a/nifxml_doc.py +++ b/nifxml_doc.py @@ -229,7 +229,7 @@ def ListAttributes( compound ): temp.set_var( "title", x.name ) temp.set_var( "name", x.name ) temp.set_var( "description", x.description.replace("\n", "
") ) - if count == "1": + if x.count == "1": temp.set_var( "count", "

Yes

" ) else: temp.set_var( "count", "

No

" ) From cf3aca1850ddb83062036256d9184a0241914d0c Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Thu, 21 Dec 2017 02:28:38 -0500 Subject: [PATCH 22/27] Fix links for BSSkin::* niobjects --- nifxml_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nifxml_doc.py b/nifxml_doc.py index 8a18c73..0d84f74 100644 --- a/nifxml_doc.py +++ b/nifxml_doc.py @@ -536,7 +536,7 @@ def ListObjectTree( root ): desc = "" #add a new list for this ancestor - object_tree += "
  • " + root.name + " | " + desc + "
  • \n" + object_tree += "
  • " + root.name + " | " + desc + "
  • \n" """
    • From 8703ec19aa50c0aa8eb075c6790f535567e77d78 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sat, 6 Jan 2018 18:39:55 -0500 Subject: [PATCH 23/27] Complete nifxml_doc rewrite, new license --- LICENSE | 674 ++++++++++++++++++++++++++++ doc/docsys.css | 25 +- nifxml.py | 210 ++++----- nifxml_doc.py | 855 ++++++++++++------------------------ nifxml_tmpl.py | 207 +++++++++ templates/attr_row.html | 11 - templates/basic.html | 10 - templates/class.html | 58 --- templates/compound.html | 22 - templates/enum.html | 20 - templates/enum_row.html | 5 - templates/hierarchy.html | 2 - templates/inherit_row.html | 3 - templates/list.html | 8 - templates/list_row.html | 4 - templates/main.html | 13 - templates/niobject.html | 22 - templates/niobject_nav.html | 2 - templates/version_list.html | 8 - templates/version_row.html | 4 - 20 files changed, 1286 insertions(+), 877 deletions(-) create mode 100644 LICENSE create mode 100644 nifxml_tmpl.py delete mode 100644 templates/attr_row.html delete mode 100644 templates/basic.html delete mode 100644 templates/class.html delete mode 100644 templates/compound.html delete mode 100644 templates/enum.html delete mode 100644 templates/enum_row.html delete mode 100644 templates/hierarchy.html delete mode 100644 templates/inherit_row.html delete mode 100644 templates/list.html delete mode 100644 templates/list_row.html delete mode 100644 templates/main.html delete mode 100644 templates/niobject.html delete mode 100644 templates/niobject_nav.html delete mode 100644 templates/version_list.html delete mode 100644 templates/version_row.html diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/doc/docsys.css b/doc/docsys.css index 018254b..9d3a235 100644 --- a/doc/docsys.css +++ b/doc/docsys.css @@ -2,26 +2,31 @@ hr { border: 1px; color: black; background: black; height: 1px; } body { font-family: Verdana, Arial, Helvetica, sans-serif; } p, td, th, div, pre, li { color: black; font-size: 10pt; } td { vertical-align: text-top; } -a { color: #006699; text-decoration: none;} -a:link { color: #006699; text-decoration: none;} -a:visited { color: #006699; text-decoration: none;} +a { color: #2087bb; text-decoration: none;} +a:link { color: #2087bb; text-decoration: none;} +a:visited { color: #2087bb; text-decoration: none;} a:active { text-decoration: none;} a:hover { color: #DD6900; text-decoration: underline;} +table { width: 100%; } th { font-weight: bold; color: black; background: #D3DCE3} h1 { text-align: center; color: black; background: #FF6600; } h2 { text-align: center; color: black; background: #FFCC33; } h3 { text-align: left; color: black; background: #FFCC66; } -a:link { text-decoration: none; color: #006699; background: transparent; } -a:visited { text-decoration: none; color: #006699; background: transparent; } +a:link { text-decoration: none; color: #2087bb; background: transparent; } +a:visited { text-decoration: none; color: #2087bb; background: transparent; } a:hover { text-decoration: underline; color: #DD6900; background: transparent; } a:link.nav { color: #000000; background: transparent; } a:visited.nav { color: #000000; background: transparent; } a:hover.nav { color: #FF0000; background: transparent; } .nav { color: #000000; background: transparent; } -tr.reg0 { background: #CCCCCC; color: black; } -tr.reg1 { background: #DDDDDD; color: black; } -tr.inact0 { background: #CCAAAA; color: black; } -tr.inact1 { background: #DDBBBB; color: black; } -tr.extrnl { background: #AADDAA; color: black; } +tr { background: #DDDDDD; color: black; } +tr.odd { background: #DDDDDD; color: black; } +tr.even { background: #EEEEEE; color: black; } +td.aname { white-space: nowrap; } +td.atype { white-space: nowrap; } +td.aarg { font-size: small; font-family: 'Courier New', Courier, monospace; } +td.aarr1 { font-size: small; font-family: 'Courier New', Courier, monospace; } +td.aarr2 { font-size: small; font-family: 'Courier New', Courier, monospace; } +td.acond { font-size: small; font-family: 'Courier New', Courier, monospace; } diff --git a/nifxml.py b/nifxml.py index 318d94e..f593022 100644 --- a/nifxml.py +++ b/nifxml.py @@ -75,7 +75,6 @@ from xml.dom.minidom import Node, parse import os -import io import re import types @@ -83,55 +82,24 @@ # global data # -native_types = {} -native_types['TEMPLATE'] = 'T' -basic_types = {} -enum_types = {} -flag_types = {} -compound_types = {} -block_types = {} -version_types = {} - -basic_names = [] -compound_names = [] -enum_names = [] -flag_names = [] -block_names = [] -version_names = [] +TYPES_NATIVE = {} +TYPES_NATIVE['TEMPLATE'] = 'T' +TYPES_BASIC = {} +TYPES_ENUM = {} +TYPES_FLAG = {} +TYPES_COMPOUND = {} +TYPES_BLOCK = {} +TYPES_VERSION = {} + +NAMES_BASIC = [] +NAMES_COMPOUND = [] +NAMES_ENUM = [] +NAMES_FLAG = [] +NAMES_BLOCK = [] +NAMES_VERSION = [] NATIVETYPES = {} -# -# HTML Template class -# - -class Template: - """ - This class processes template files. These files have tags enclosed - in curly brackets like this: {tag}, which are replaced when a template - is processed. - """ - def __init__(self): - """Initialize variable dictionary""" - self.vars = {} - - def set_var(self, var_name, value): - """Set data in variable dictionary""" - self.vars[var_name] = value - - def parse(self, file_name): - """Open file and read contents to txt variable""" - f = io.open(file_name, 'rt', 1, 'utf-8') - txt = f.read() - f.close() - - #Loop through all variables, replacing them in the template text - for i in self.vars: - if self.vars[i] is not None: - txt = txt.replace('{' + i + '}', self.vars[i].encode('utf-8').decode('utf-8', 'strict')) - - #return result - return txt def class_name(name_in): """ @@ -144,14 +112,14 @@ def class_name(name_in): if name_in is None: return None try: - return native_types[name_in] + return TYPES_NATIVE[name_in] except KeyError: return name_in.replace(' ', '_').replace(":", "_") if name_in is None: return None try: - return native_types[name_in] + return TYPES_NATIVE[name_in] except KeyError: pass if name_in == 'TEMPLATE': @@ -552,7 +520,7 @@ def code(self, prefix='', brackets=True, name_filter=None): return '' if isinstance(self.lhs, int): return self.lhs - elif self.lhs in block_types: + elif self.lhs in TYPES_BLOCK: return 'IsDerivedType(%s::TYPE)' % self.lhs else: return prefix + (name_filter(self.lhs) if name_filter else self.lhs) @@ -560,7 +528,7 @@ def code(self, prefix='', brackets=True, name_filter=None): lhs = self.lhs if isinstance(lhs, Expression): lhs = lhs.code(prefix, True, name_filter) - elif lhs in block_types: + elif lhs in TYPES_BLOCK: lhs = 'IsDerivedType(%s::TYPE)' % lhs elif lhs and not lhs.isdigit() and not lhs.startswith('0x'): lhs = prefix + (name_filter(lhs) if name_filter else lhs) @@ -570,13 +538,13 @@ def code(self, prefix='', brackets=True, name_filter=None): rhs = self.rhs if isinstance(lhs, Expression): lhs = lhs.code(prefix, True, name_filter) - elif lhs in block_types: + elif lhs in TYPES_BLOCK: lhs = 'IsDerivedType(%s::TYPE)' % lhs elif lhs and not lhs.isdigit() and not lhs.startswith('0x'): lhs = prefix + (name_filter(lhs) if name_filter else lhs) if isinstance(rhs, Expression): rhs = rhs.code(prefix, True, name_filter) - elif rhs in block_types: + elif rhs in TYPES_BLOCK: rhs = 'IsDerivedType(%s::TYPE)' % rhs elif rhs and not rhs.isdigit() and not rhs.startswith('0x'): rhs = prefix + (name_filter(rhs) if name_filter else rhs) @@ -797,9 +765,9 @@ def __init__(self, element): pass elif self.type == "StringOffset": self.default = "-1" - elif self.type in basic_names: + elif self.type in NAMES_BASIC: self.default = "0" - elif self.type in flag_names or self.type in enum_names: + elif self.type in NAMES_FLAG or self.type in NAMES_ENUM: self.default = "0" if self.default: if self.default[0] == '(' and self.default[-1] == ')': @@ -822,7 +790,7 @@ def __init__(self, element): # calculate other stuff self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') - self.type_is_native = self.name in native_types # true if the type is implemented natively + self.type_is_native = self.name in TYPES_NATIVE # true if the type is implemented natively # calculate stuff from reference to previous members # true if this is a duplicate of a previously declared member @@ -872,6 +840,7 @@ class Version: """This class represents the nif.xml tag.""" def __init__(self, element): self.num = element.getAttribute('num') + self.name = self.num # Treat the version as a name to match other tags self.description = element.firstChild.nodeValue.strip() class Basic: @@ -900,7 +869,7 @@ def __init__(self, element, ntypes): if ntypes: self.nativetype = ntypes.get(self.name) if self.nativetype: - native_types[self.name] = self.nativetype + TYPES_NATIVE[self.name] = self.nativetype if self.nativetype == "Ref": self.is_link = True self.has_links = True @@ -916,11 +885,11 @@ def __init__(self, element, ntypes): self.storage = element.getAttribute('storage') self.prefix = element.getAttribute('prefix') # Find the native storage type - self.storage = basic_types[self.storage].nativetype if basic_types[self.storage].nativetype else basic_types[self.storage].name + self.storage = TYPES_BASIC[self.storage].nativetype if TYPES_BASIC[self.storage].nativetype else TYPES_BASIC[self.storage].name self.description = element.firstChild.nodeValue.strip() self.nativetype = self.cname - native_types[self.name] = self.nativetype + TYPES_NATIVE[self.name] = self.nativetype # Locate all special enumeration options for option in element.getElementsByTagName('option'): @@ -973,10 +942,10 @@ def __init__(self, element, ntypes): # detect links & crossrefs mem = None try: - mem = basic_types[x.type] + mem = TYPES_BASIC[x.type] except KeyError: try: - mem = compound_types[x.type] + mem = TYPES_COMPOUND[x.type] except KeyError: pass if mem: @@ -1016,7 +985,7 @@ def find_first_ref(self, name): def has_arr(self): """Tests recursively for members with an array size.""" for mem in self.members: - if mem.arr1.lhs or (mem.type in compound_types and compound_types[mem.type].has_arr()): + if mem.arr1.lhs or (mem.type in TYPES_COMPOUND and TYPES_COMPOUND[mem.type].has_arr()): return True return False @@ -1026,21 +995,18 @@ def __init__(self, element, ntypes): Compound.__init__(self, element, ntypes) self.is_ancestor = (element.getAttribute('abstract') == "1") inherit = element.getAttribute('inherit') - if inherit: - self.inherit = block_types[inherit] - else: - self.inherit = None + self.inherit = TYPES_BLOCK[inherit] if inherit else None self.has_interface = (element.getElementsByTagName('interface') != []) - # find member by name def find_member(self, name, inherit=False): + """Find member by name""" ret = Compound.find_member(self, name) if not ret and inherit and self.inherit: ret = self.inherit.find_member(name, inherit) return ret - # find first reference of name in class def find_first_ref(self, name): + """Find first reference of name in class""" ret = None if self.inherit: ret = self.inherit.find_first_ref(name) @@ -1048,6 +1014,15 @@ def find_first_ref(self, name): ret = Compound.find_first_ref(self, name) return ret + def ancestors(self): + """List all ancestors of this block""" + ancestors = [] + parent = self + while parent: + ancestors.append(parent) + parent = parent.inherit + return ancestors + # # import elements into our code generating classes # @@ -1065,37 +1040,68 @@ def find_first_ref(self, name): raise ImportError("nif.xml not found") def parse_XML(ntypes=None): - for el in XML.getElementsByTagName('version'): - x = Version(el) - version_types[x.num] = x - version_names.append(x.num) - - for el in XML.getElementsByTagName('basic'): - x = Basic(el, ntypes) - assert not x.name in basic_types - basic_types[x.name] = x - basic_names.append(x.name) - - for el in XML.getElementsByTagName('enum'): - x = Enum(el, ntypes) - assert not x.name in enum_types - enum_types[x.name] = x - enum_names.append(x.name) - - for el in XML.getElementsByTagName('bitflags'): - x = Flag(el, ntypes) - assert not x.name in flag_types - flag_types[x.name] = x - flag_names.append(x.name) - - for el in XML.getElementsByTagName("compound"): - x = Compound(el, ntypes) - assert not x.name in compound_types - compound_types[x.name] = x - compound_names.append(x.name) - - for el in XML.getElementsByTagName("niobject"): - x = Block(el, ntypes) - assert not x.name in block_types - block_types[x.name] = x - block_names.append(x.name) + for element in XML.getElementsByTagName('version'): + instance = Version(element) + TYPES_VERSION[instance.num] = instance + NAMES_VERSION.append(instance.num) + + for element in XML.getElementsByTagName('basic'): + instance = Basic(element, ntypes) + assert not instance.name in TYPES_BASIC + TYPES_BASIC[instance.name] = instance + NAMES_BASIC.append(instance.name) + + for element in XML.getElementsByTagName('enum'): + instance = Enum(element, ntypes) + assert not instance.name in TYPES_ENUM + TYPES_ENUM[instance.name] = instance + NAMES_ENUM.append(instance.name) + + for element in XML.getElementsByTagName('bitflags'): + instance = Flag(element, ntypes) + assert not instance.name in TYPES_FLAG + TYPES_FLAG[instance.name] = instance + NAMES_FLAG.append(instance.name) + + for element in XML.getElementsByTagName('compound'): + instance = Compound(element, ntypes) + assert not instance.name in TYPES_COMPOUND + TYPES_COMPOUND[instance.name] = instance + NAMES_COMPOUND.append(instance.name) + + for element in XML.getElementsByTagName('niobject'): + instance = Block(element, ntypes) + assert not instance.name in TYPES_BLOCK + TYPES_BLOCK[instance.name] = instance + NAMES_BLOCK.append(instance.name) + + validate_XML() + +def validate_XML(): + """Perform some basic validation on the data retrieved from the XML""" + assert TYPES_VERSION + assert NAMES_VERSION + assert TYPES_BASIC + assert NAMES_BASIC + assert TYPES_COMPOUND + assert NAMES_COMPOUND + assert TYPES_BLOCK + assert NAMES_BLOCK + assert TYPES_ENUM + assert NAMES_ENUM + assert TYPES_FLAG + assert NAMES_FLAG + + assert len(TYPES_VERSION) == len(NAMES_VERSION) + assert len(TYPES_BASIC) == len(NAMES_BASIC) + assert len(TYPES_COMPOUND) == len(NAMES_COMPOUND) + assert len(TYPES_BLOCK) == len(NAMES_BLOCK) + assert len(TYPES_ENUM) == len(NAMES_ENUM) + assert len(TYPES_FLAG) == len(NAMES_FLAG) + + assert all(name for name in NAMES_VERSION) + assert all(name for name in NAMES_BASIC) + assert all(name for name in NAMES_COMPOUND) + assert all(name for name in NAMES_BLOCK) + assert all(name for name in NAMES_ENUM) + assert all(name for name in NAMES_FLAG) diff --git a/nifxml_doc.py b/nifxml_doc.py index 0d84f74..ce10375 100644 --- a/nifxml_doc.py +++ b/nifxml_doc.py @@ -1,583 +1,292 @@ #!/usr/bin/python +""" +nifxml_doc.py -# nifxml_doc.py -# -# This script generates HTML documentation from the XML file. -# -# -------------------------------------------------------------------------- -# Command line options -# -# -p /path/to/doc : specifies the path where HTML documentation must be created -# -# -------------------------------------------------------------------------- -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (c) 2005, 2006, 2007 NIF File Format Library and Tools -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the NIF File Format Library and Tools -# project nor the names of its contributors may be used to endorse -# or promote products derived from this software without specific -# prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# ***** END LICENCE BLOCK ***** -# -------------------------------------------------------------------------- - -from __future__ import unicode_literals - -from distutils.dir_util import mkpath -import sys -import os -import io -import itertools - -from nifxml import Template -from nifxml import block_types, basic_types, compound_types, enum_types, flag_types, version_types -from nifxml import block_names, basic_names, compound_names, enum_names, flag_names, version_names -from nifxml import parse_XML - -# -# Parse the XML -# - -parse_XML() - -assert version_types -assert version_names -assert basic_types -assert basic_names -assert compound_types -assert compound_names -assert block_types -assert block_names -assert enum_types -assert enum_names -assert flag_types -assert flag_names - -# -# global data -# - -ROOT_DIR = "." - - -prev = "" -for i in sys.argv: - if prev == "-p": - ROOT_DIR = i - prev = i - -# -# Sort Name Lists -# - -block_names.sort() -compound_names.sort() -basic_names.sort() -enum_names.sort() -flag_names.sort() - -def tohex(value, nbytes=4): - """Improved version of hex.""" - return ("0x%%0%dX" % (2*nbytes)) % (int(str(value)) & (2**(nbytes*8)-1)) - -def ListAttributes( compound ): - attr_list = "" - count = 0 - - #Create Attribute List - for a in compound.members: - temp.set_var( "attr-name", a.name ) - attr_type = '%s'%(a.type, a.type) - if a.template: - attr_type += '<%s>'%(a.template.replace("\\", "_"), a.template) - - temp.set_var( "attr-type", attr_type ) - temp.set_var( "attr-arg", a.arg ) - temp.set_var( "attr-arr1", a.arr1.lhs ) - temp.set_var( "attr-arr2", a.arr2.lhs ) - cond_string = a.cond.code("", brackets = False) - if cond_string: - temp.set_var( "attr-cond", cond_string ) - else: - temp.set_var( "attr-cond", "" ) - - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "attr-desc", a.description.replace("\n", "
      ") ) - - temp.set_var( "attr-from", a.orig_ver1 ) - temp.set_var( "attr-to", a.orig_ver2 ) - - attr_list += temp.parse( "templates/attr_row.html" ) - - count += 1 - - return attr_list - -# -# Generate Version List Page -# - -temp = Template() -temp.set_var( "title", "NIF File Format Versions" ) - -#List each Version with Description - -count = 0 -version_list = "" -for n in version_names: - x = version_types[n] - - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "list-name", x.num ) - temp.set_var( "list-desc", x.description.replace("\n", "
      ") ) - - version_list += temp.parse( "templates/version_row.html" ) - - count += 1 - - -temp.set_var( "list", version_list ) - -temp.set_var( "contents", temp.parse( "templates/version_list.html") ) - -f = io.open(ROOT_DIR + '/doc/version_list.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - - -# -# Generate Basic List Page -# - -temp = Template() -temp.set_var( "title", "Basic Data Types" ) - -#List each Basic Type with Description - -count = 0 -basic_list = "" -for n in basic_names: - x = basic_types[n] - - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "list-name", x.name ) - temp.set_var( "list-desc", x.description.replace("\n", "
      ") ) - - basic_list += temp.parse( "templates/list_row.html" ) - - count += 1 - - -temp.set_var( "list", basic_list ) - -temp.set_var( "contents", temp.parse( "templates/list.html") ) - -f = io.open(ROOT_DIR + '/doc/basic_list.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - - - -# -# Generate Basic Pages -# - -count = 0 -for n in basic_names: - x = basic_types[n] - - temp = Template() - temp.set_var( "title", x.name ) - temp.set_var( "name", x.name ) - temp.set_var( "description", x.description.replace("\n", "
      ") ) - if x.count == "1": - temp.set_var( "count", "

      Yes

      " ) - else: - temp.set_var( "count", "

      No

      " ) - - #Create Found In list - found_in = "" - - for b in block_names: - for m in block_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - for b in compound_names: - for m in compound_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - temp.set_var( "found-in", found_in ); - - temp.set_var( "contents", temp.parse( "templates/basic.html") ) - - f = io.open(ROOT_DIR + '/doc/' + x.cname.replace('\\', '_') + '.html', 'wt', 1, 'utf-8') - f.write( temp.parse( "templates/main.html" ) ) - f.close() - -# -# Generate Enum List Page -# - -temp = Template() -temp.set_var( "title", "Enum Data Types" ) - -#List each Enum Type with Description +Generates HTML documentation for the XML file. -count = 0 -enum_list = "" -for n, x in itertools.chain(enum_types.items(), flag_types.items()): - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "list-name", x.name ) - temp.set_var( "list-desc", x.description.replace("\n", "
      ") ) +To list command line options run: + nifxml_doc.py -h - enum_list += temp.parse( "templates/list_row.html" ) + This file is part of nifxml + Copyright (c) 2017 NifTools - count += 1 + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3. + 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 + General Public License for more details. -temp.set_var( "list", enum_list ) + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" -temp.set_var( "contents", temp.parse( "templates/list.html") ) - -f = io.open(ROOT_DIR + '/doc/enum_list.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - - - -# -# Generate Enum Pages -# - -count = 0 -for n, x in itertools.chain(enum_types.items(), flag_types.items()): - - temp = Template() - temp.set_var( "title", x.name ) - temp.set_var( "name", x.name ) - temp.set_var( "storage", x.storage ) - temp.set_var( "description", x.description.replace("\n", "
      ") ) - - #Create Found In list - found_in = "" - - for b in block_names: - for m in block_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - for b in compound_names: - for m in compound_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - temp.set_var( "found-in", found_in ); - - #Create Choice List - - count = 0 - choice_list = "" - for o in x.options: - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) +from __future__ import unicode_literals - # represent bit flags with hex - if (hasattr(o, "bit")): - temp.set_var( "enum-number", tohex(o.value) ) +import re +import os +import io +import argparse +from shutil import copy2 + +from nifxml import Compound, Block, Enum, parse_XML, version2number +from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_VERSION +from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG, NAMES_VERSION + +import nifxml_tmpl as tmpl + +# +# Globals +# + +SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) +DOC_PATH = '/doc/' +DOC_FILE = '{}.html' +CSS_PATH = os.path.join(SCRIPT_PATH, 'doc/docsys.css') +ICO_PATH = os.path.join(SCRIPT_PATH, 'doc/favicon.ico') + +def clean(string): + """Removes everything but letters from a string""" + pattern = re.compile(r'[\W_]+') + return pattern.sub('', string) + +def main(): + """Parses the XML and generates all doc pages""" + # Parse the XML and sort names + parse_XML() + NAMES_BASIC.sort() + NAMES_COMPOUND.sort() + NAMES_BLOCK.sort() + NAMES_ENUM.sort() + NAMES_FLAG.sort() + + # Default to calling directory + root_dir = '.' + heading = True + metadata = True + minver = '' + + # Command line args + parser = argparse.ArgumentParser(description="NIF Format XML Docs Generation") + parser.add_argument('-p', '--path', help="The path where the doc folder will be generated.") + parser.add_argument('-no-h1', '--no-heading', action='store_true', + help="Whether to not generate the main

      heading. Used by NifSkope for built-in help.") + parser.add_argument('-no-meta', '--no-metadata-columns', action='store_true', + help="Whether to not generate the metadata attribute columns (arg, arr1, arr2, etc.)") + parser.add_argument('-min-ver', '--minimum-version', + help="Hides attributes below this version. Format 'XX.X.X.XX'") + args = parser.parse_args() + if args.path: + root_dir = args.path + if args.no_heading: + heading = False + if args.no_metadata_columns: + metadata = False + if args.minimum_version: + minver = args.minimum_version + + # Create the document generator + doc = DocGenerator(root_dir + DOC_PATH, heading, metadata, version2number(minver)) + # Generate NiObject Pages + doc.gen_pages(NAMES_BLOCK, TYPES_BLOCK, tmpl.NIOBJECT if metadata else tmpl.NIOBJECT_NO_META) + # Generate Compound Pages + doc.gen_pages(NAMES_COMPOUND, TYPES_COMPOUND, tmpl.COMPOUND if metadata else tmpl.COMPOUND_NO_META) + # Generate Basic Pages + doc.gen_pages(NAMES_BASIC, TYPES_BASIC, tmpl.BASIC) + # Generate Enum Pages + enums = dict(TYPES_ENUM, **TYPES_FLAG) + doc.gen_pages(sorted(enums), enums, tmpl.ENUM) + + # Generate Basic List Page + doc.gen_list_page('Basic Data Types', NAMES_BASIC, TYPES_BASIC, 'basic_list') + # Generate NiObject List Page + doc.gen_list_page('NIF Object List', NAMES_BLOCK, TYPES_BLOCK, 'niobject_list') + # Generate Compound List Page + doc.gen_list_page('Compound Data Types', NAMES_COMPOUND, TYPES_COMPOUND, 'compound_list') + # Generate Enum List Page + doc.gen_list_page('Enum Data Types', sorted(enums), enums, 'enum_list') + # Generate Version List Page + doc.gen_list_page('NIF File Format Versions', NAMES_VERSION, TYPES_VERSION, 'version_list', tmpl.VERSION_ROW, 'Versions') + # Generate Index Page + doc.gen_index() + + +class DocGenerator(): + """Methods for formatting and outputting the template strings with data from the XML""" + + def __init__(self, path, heading=True, metadata=True, minver=0): + """Initialize generator""" + self.doc_file = path + DOC_FILE + self.main = tmpl.MAIN_H1 if heading else tmpl.MAIN_NO_H1 + self.attr_row = tmpl.ATTR if metadata else tmpl.ATTR_NO_META + self.inherit = tmpl.INHERIT_ROW if metadata else tmpl.INHERIT_NO_META + self.minver = minver + self.blocks = dict(TYPES_BLOCK, **TYPES_COMPOUND) + install_dir = os.path.abspath(path) + if not os.path.exists(install_dir): + os.makedirs(install_dir) + # Install CSS and ICO + if os.path.dirname(CSS_PATH) != install_dir: + copy2(CSS_PATH, install_dir) + copy2(ICO_PATH, install_dir) + + # + # Template Helper functions + # + + def list_attributes(self, compound): + """Create Attribute List""" + attrs = '' + count = 0 + for mem in compound.members: + if self.minver and mem.ver2 and mem.ver2 < self.minver: + continue + attr_type = tmpl.TYPE_LINK.format(clean(mem.type), mem.type) + if mem.template: + attr_type += tmpl.TMPL_LINK.format(clean(mem.template), mem.template) + content = { + 'attr_name': mem.name, + 'attr_type': attr_type, + 'attr_arg': mem.arg, + 'attr_arr1': mem.arr1.lhs, + 'attr_arr2': mem.arr2.lhs, + 'attr_cond': mem.cond, + 'attr_desc': mem.description.replace('\n', '
      '), + 'attr_from': mem.orig_ver1, + 'attr_to': mem.orig_ver2, + 'row': 'even' if count % 2 == 0 else 'odd' + } + count += 1 # Manually increment because of 'continue' on skipped versioned rows + attrs += self.attr_row.format(**content) + return attrs + + @staticmethod + def list_tags(names, types, template): + """List each tag with a description""" + tag_list = '' + for count, tname in enumerate(names): + tag = types[tname] + content = { + 'list_name': tag.name, + 'list_cname': clean(tag.name), + 'list_desc': tag.description.replace('\n', '
      '), + 'row': 'even' if count % 2 == 0 else 'odd' + } + tag_list += template.format(**content) + return tag_list + + @staticmethod + def list_choices(tag): + """Create Choice List""" + choice_list = '' + for count, opt in enumerate(tag.options): + content = { + # Display bitflags as hex + 'enum_number': opt.value if not hasattr(opt, 'bit') else '{0:#0{1}x}'.format(int(opt.value), 10), + 'enum_name': opt.name, + 'enum_desc': opt.description.replace('\n', '
      '), + 'row': 'even' if count % 2 == 0 else 'odd' + } + choice_list += tmpl.ENUM_ROW.format(**content) + return choice_list + + @staticmethod + def list_child_blocks(block): + """Create Child Block list""" + return ''.join(tmpl.LI_LINK.format(clean(n), n) for n in NAMES_BLOCK if TYPES_BLOCK[n].inherit == block) + + def member_of(self, name): + """Create Member Of list""" + found = '' + for b_name in NAMES_BLOCK + NAMES_COMPOUND: + for bmem in self.blocks[b_name].members: + if bmem.type == name: + found += tmpl.LI_LINK.format(clean(b_name), b_name) + break + return found + + def list_ancestor_attributes(self, block): + """Create list of attributes for all ancestors""" + attr_list = '' + for ancestor in reversed(block.ancestors()): + content = {'inherit': ancestor.name, 'cinherit': clean(ancestor.name)} + attr_list += self.inherit.format(**content) + self.list_attributes(ancestor) + return attr_list + + def list_object_tree(self, root): + """Builds a hierarchical unordered list for a specified ancestor root.""" + tree = '' + # Get truncated description + lines = root.description.splitlines(False) + # Add a new list for this ancestor + tree += tmpl.LI_LINK_DESC.format(clean(root.name), root.name, '' if not lines else lines[0]) + # Create Child List + children = [TYPES_BLOCK[n] for n in NAMES_BLOCK if TYPES_BLOCK[n].inherit == root] + if children: + tree += tmpl.UL_ITEM.format(''.join(self.list_object_tree(c) for c in children)) + return tree + + # + # Generation Functions + # + + def gen_pages(self, names, types, template): + """Generate Pages for XML Tag""" + for count, name in enumerate(names): + tag = types[name] + contents = { + 'name': tag.name, + 'description': tag.description.replace('\n', '
      '), + 'storage': '' if not isinstance(tag, Enum) else tag.storage, + 'count': bool(tag.count == '1'), + 'member_of': self.member_of(name) if not isinstance(tag, Block) else '', + 'row': 'even' if count % 2 == 0 else 'odd' + } + if isinstance(tag, Block): + contents['attributes'] = self.list_ancestor_attributes(tag) + contents['parent_of'] = self.list_child_blocks(tag) + elif isinstance(tag, Compound): + contents['attributes'] = self.list_attributes(tag) + elif isinstance(tag, Enum): + contents['choices'] = self.list_choices(tag) + + page = {'title': tag.name, 'contents': template.format(**contents)} + + html = io.open(self.doc_file.format(clean(tag.name)), 'wt', 1, 'utf-8') + html.write( self.main.format(**page) ) + html.close() + + def gen_list_page(self, title, names, types, pagename, rowtmpl=tmpl.LIST_ROW, header='Name'): + """Generate List Page for XML Tag""" + page = {'title': title} + contents = { + 'title': title, + 'list_header': header, + 'list': self.list_tags(names, types, rowtmpl) + } + if isinstance(types[names[0]], Block): + page['contents'] = tmpl.NAV_LIST.format(**contents) else: - temp.set_var( "enum-number", o.value ) - temp.set_var( "enum-name", o.name ) - temp.set_var( "enum-desc", o.description.replace("\n", "
      ") ) - - choice_list += temp.parse( "templates/enum_row.html" ) - - count += 1 - - temp.set_var( "choices", choice_list ) - - temp.set_var( "contents", temp.parse( "templates/enum.html") ) - - f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') - f.write( temp.parse( "templates/main.html" ) ) - f.close() - - -# -# Generate Compound List Page -# - -temp = Template() -temp.set_var( "title", "Compound Data Types" ) - -#List each Compound with Description - -count = 0 -compound_list = "" -for n in compound_names: - x = compound_types[n] - - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "list-name", x.name ) - temp.set_var( "list-desc", x.description.replace("\n", "
      ") ) - - compound_list += temp.parse( "templates/list_row.html" ) - - count += 1 - - -temp.set_var( "list", compound_list ) - -temp.set_var( "contents", temp.parse( "templates/list.html") ) - -f = io.open(ROOT_DIR + '/doc/compound_list.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - - - -# -# Generate Compound Pages -# - -count = 0 -for n in compound_names: - x = compound_types[n] - - temp = Template() - temp.set_var( "title", x.name ) - temp.set_var( "name", x.name ) - temp.set_var( "description", x.description.replace("\n", "
      ") ) - - #Create Found In list - found_in = "" - - for b in block_names: - for m in block_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - for b in compound_names: - for m in compound_types[b].members: - if m.type == n: - found_in += "
    • " + b + "
    • \n" - break - - temp.set_var( "found-in", found_in ); - - #Create Attribute List - attr_list = ListAttributes( x) - - temp.set_var( "attributes", attr_list ) - - temp.set_var( "contents", temp.parse( "templates/compound.html") ) - - f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') - f.write( temp.parse( "templates/main.html" ) ) - f.close() - -# -# Generate NiObject List Page -# - -temp = Template() -temp.set_var( "title", "NIF Object List" ) - -#List each NiObject with Description - -count = 0 -niobject_list = "" -for n in block_names: - x = block_types[n] - - if count % 2 == 0: - temp.set_var( "row-class", "reg0" ) - else: - temp.set_var( "row-class", "reg1" ) - - temp.set_var( "list-name", x.name ) - temp.set_var( "list-desc", x.description.replace("\n", "
      ") ) - - niobject_list += temp.parse( "templates/list_row.html" ) - - count += 1 - - -temp.set_var( "list", niobject_list ) - -temp.set_var( "niobject-contents", temp.parse( "templates/list.html") ) -temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") ) - -f = io.open(ROOT_DIR + '/doc/niobject_list.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - - - -# -# Generate NiObject Pages -# - -count = 0 -for n in block_names: - x = block_types[n] - - temp = Template() - temp.set_var( "title", x.name ) - temp.set_var( "name", x.name ) - temp.set_var( "description", x.description.replace("\n", "
      ") ) - - #Create Ancestor List - - ancestors = [] - b = x - while b: - ancestors.append(b) - b = b.inherit - - ancestors.reverse() - - #Create Attribute List - attr_list = "" - count = 0 - - for a in ancestors: - temp.set_var( "inherit", a.name ) - attr_list += temp.parse( "templates/inherit_row.html" ) - - inherit_list = "" - inherit_list = ListAttributes( a ) - - attr_list += inherit_list - - temp.set_var( "attributes", attr_list ) - - #Create Parent Of list - parent_of = "" - for b in block_names: - if block_types[b].inherit == x: - parent_of += "
    • " + b + "
    • \n" - - temp.set_var( "parent-of", parent_of ); - - temp.set_var( "contents", temp.parse( "templates/niobject.html") ) - - f = io.open(ROOT_DIR + '/doc/' + x.cname.replace("\\", "_") + '.html', 'wt', 1, 'utf-8') - f.write( temp.parse( "templates/main.html" ) ) - f.close() - -#global value -object_tree = "" - -def ListObjectTree( root ): - - global object_tree - - #get first line of description - lines = root.description.splitlines(False) - if len(lines) > 0: - desc = lines[0] - else: - desc = "" - - #add a new list for this ancestor - object_tree += "
    • " + root.name + " | " + desc + "
    • \n" - """ -
        -
      • - NiObject - | Abstract block type.
          -
        • - """ - - #Create Child List - - children = [] - for b in block_names: - if block_types[b].inherit == root: - children.append(block_types[b]) - - if len(children) > 0: - object_tree += "
            \n" - - for c in children: - ListObjectTree(c) - - object_tree += "
          \n" - -# -# Generate NiObject Hierarchy Page -# - -temp = Template() -temp.set_var( "title", "NIF Object Hierarchy" ) - -# Build Tree - -object_tree = "" -ListObjectTree( block_types["NiObject"] ) -temp.set_var( "object-tree", object_tree ) - - -temp.set_var( "niobject-contents", temp.parse( "templates/hierarchy.html") ) -temp.set_var( "contents", temp.parse( "templates/niobject_nav.html") ) - -f = io.open(ROOT_DIR + '/doc/index.html', 'wt', 1, 'utf-8') -f.write( temp.parse( "templates/main.html" ) ) -f.close() - + page['contents'] = tmpl.LIST.format(**contents) + + html = io.open(self.doc_file.format(pagename), 'wt', 1, 'utf-8') + html.write( self.main.format(**page) ) + html.close() + + def gen_index(self): + """Generate index.html""" + page = {'title': 'NIF Object Hierarchy'} + contents = { + 'title': page['title'], + 'object_tree': self.list_object_tree( TYPES_BLOCK['NiObject'] ) + } + page['contents'] = tmpl.NAV_HIER.format(**contents) + + html = io.open(self.doc_file.format('index'), 'wt', 1, 'utf-8') + html.write( self.main.format(**page) ) + html.close() + +if __name__ == "__main__": + main() diff --git a/nifxml_tmpl.py b/nifxml_tmpl.py new file mode 100644 index 0000000..03c5559 --- /dev/null +++ b/nifxml_tmpl.py @@ -0,0 +1,207 @@ +#!/usr/bin/python +""" + This file is part of nifxml + Copyright (c) 2017 NifTools + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 3. + + 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" +from __future__ import unicode_literals + +UL_ITEM = '
            \n{0}
          \n' +LI_LINK = '\t
        • {1}
        • \n' +LI_LINK_DESC = '
        • {1} | {2}
        • \n' +TYPE_LINK = '{1}' +TMPL_LINK = '<{1}>' + +MAIN_BEG = """ + + +\tNIF File Format Documentation - {title} +\t +\t + +""" +MAIN_END = """ +\t

          NIF Objects | Compound Types +\t| Enum Types | Basic Types | File Versions

          +\t{contents} + +""" + +# Main HTML template with and without H1 heading +MAIN_H1 = MAIN_BEG + '

          NIF File Format Documentation

          ' + MAIN_END +MAIN_NO_H1 = MAIN_BEG + MAIN_END + +# Attribute row with metadata +ATTR = """ + +\t{attr_name} +\t{attr_type} +\t{attr_arg} +\t{attr_arr1} +\t{attr_arr2} +\t{attr_cond} +\t{attr_desc} +\t{attr_from} +\t{attr_to} + +""" + +# Attribute row without metadata +ATTR_NO_META = """ + +\t{attr_name} +\t{attr_type} +\t{attr_desc} + +""" + +# List for Found In +FOUND_IN = """ +

          Found In

          +
            +{member_of} +
          +""" + +# List for Parent Of +PARENT_OF = """ +

          Parent Of

          +
            +{parent_of} +
          """ + +# Basic layout +BASIC = """ +

          {name}

          +{description} + +

          Can Be Used As Array Size

          +

          {count}

          +""" + FOUND_IN + +# Block with metadata columns +BLOCK = """ +

          {name}

          +{description} +

          Attributes

          + +\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t\t +\t +\t{attributes} +
          NameTypeArgArr1Arr2CondDescriptionFromTo
          +""" + +# Block without metadata columns +BLOCK_NM = """ +

          {name}

          +{description} +

          Attributes

          + +\t +\t\t +\t\t +\t\t +\t +\t{attributes} +
          NameTypeDescription
          +""" + +# Compound and NiObject with and without metadata columns +COMPOUND = BLOCK + FOUND_IN +NIOBJECT = BLOCK + PARENT_OF +COMPOUND_NO_META = BLOCK_NM + FOUND_IN +NIOBJECT_NO_META = BLOCK_NM + PARENT_OF + +# Object tree for index.html +HIERARCHY = """ +
            {object_tree}
          """ + +# Object list for *_list.html +LIST = """ + +\t +\t\t +\t\t +\t +{list} +
          {list_header}Description
          """ + +# Navbar +NAV = """

          Hierarchy | List

          +

          {title}

          +""" + +# Nav plus contents +NAV_LIST = NAV + LIST +NAV_HIER = NAV + HIERARCHY + +# Enum layout +ENUM = """ +

          {name}

          +{description} + +

          Choices

          + +\t +\t\t +\t\t +\t\t +\t +\t{choices} +
          NumberNameDescription
          + +

          Storage Type

          +{storage} +""" + FOUND_IN + +ENUM_ROW = """ + +\t{enum_number} +\t{enum_name} +\t{enum_desc} + +""" + +INHERIT_ROW = """ + +\tFrom {inherit} + +""" + +INHERIT_NO_META = """ + +\tFrom {inherit} + +""" + +LIST_ROW = """ + +\t{list_name} +\t{list_desc} +""" + +VERSION_ROW = """ + +\t{list_name} +\t{list_desc} + +""" diff --git a/templates/attr_row.html b/templates/attr_row.html deleted file mode 100644 index e72a4da..0000000 --- a/templates/attr_row.html +++ /dev/null @@ -1,11 +0,0 @@ - - {attr-name} - {attr-type} - {attr-arg} - {attr-arr1} - {attr-arr2} - {attr-cond} - {attr-desc} - {attr-from} - {attr-to} - diff --git a/templates/basic.html b/templates/basic.html deleted file mode 100644 index d0aa9be..0000000 --- a/templates/basic.html +++ /dev/null @@ -1,10 +0,0 @@ -

          {name}

          -{description} - -

          Can Be Used As Array Size

          -{count} - -

          Found In

          -
            -{found-in} -
          \ No newline at end of file diff --git a/templates/class.html b/templates/class.html deleted file mode 100644 index 4714324..0000000 --- a/templates/class.html +++ /dev/null @@ -1,58 +0,0 @@ -

          {name}

          -{description} -

          Attribute of...

          - -

          Attributes

          - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          NameTypeArgArr1Arr2CondDescriptionFromTo
          Num KeysuintNumber of keys in the array.AnyAny
          InterpolationKeyTypeNum Keys != 0The key type.AnyAny
          KeysKeyInterpolationNum KeysThe keys.AnyAny
          - \ No newline at end of file diff --git a/templates/compound.html b/templates/compound.html deleted file mode 100644 index b0a4be3..0000000 --- a/templates/compound.html +++ /dev/null @@ -1,22 +0,0 @@ -

          {name}

          -{description} -

          Attributes

          - - - - - - - - - - - - -{attributes} -
          NameTypeArgArr1Arr2CondDescriptionFromTo
          - -

          Found In

          -
            -{found-in} -
          \ No newline at end of file diff --git a/templates/enum.html b/templates/enum.html deleted file mode 100644 index 47575ad..0000000 --- a/templates/enum.html +++ /dev/null @@ -1,20 +0,0 @@ -

          {name}

          -{description} - -

          Choices

          - - - - - - -{choices} -
          NumberNameDescription
          - -

          Storage Type

          -{storage} - -

          Found In

          -
            -{found-in} -
          \ No newline at end of file diff --git a/templates/enum_row.html b/templates/enum_row.html deleted file mode 100644 index ff6ec55..0000000 --- a/templates/enum_row.html +++ /dev/null @@ -1,5 +0,0 @@ - - {enum-number} - {enum-name} - {enum-desc} - diff --git a/templates/hierarchy.html b/templates/hierarchy.html deleted file mode 100644 index 5948453..0000000 --- a/templates/hierarchy.html +++ /dev/null @@ -1,2 +0,0 @@ -

          {title}

          -
            {object-tree}
          \ No newline at end of file diff --git a/templates/inherit_row.html b/templates/inherit_row.html deleted file mode 100644 index f19d216..0000000 --- a/templates/inherit_row.html +++ /dev/null @@ -1,3 +0,0 @@ - - From {inherit} - diff --git a/templates/list.html b/templates/list.html deleted file mode 100644 index 74ec048..0000000 --- a/templates/list.html +++ /dev/null @@ -1,8 +0,0 @@ -

          {title}

          - - - - - -{list} -
          NameDescription
          \ No newline at end of file diff --git a/templates/list_row.html b/templates/list_row.html deleted file mode 100644 index 88fb91a..0000000 --- a/templates/list_row.html +++ /dev/null @@ -1,4 +0,0 @@ - - {list-name} - {list-desc} - diff --git a/templates/main.html b/templates/main.html deleted file mode 100644 index 93bfa21..0000000 --- a/templates/main.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - NIF File Format Documentation - {title} - - - - -

          NIF File Format Documentation

          -

          NIF Objects | Compound Types | Enum Types | Basic Types | File Versions

          - {contents} - - diff --git a/templates/niobject.html b/templates/niobject.html deleted file mode 100644 index 9ff1aff..0000000 --- a/templates/niobject.html +++ /dev/null @@ -1,22 +0,0 @@ -

          {name}

          -{description} -

          Attributes

          - - - - - - - - - - - - -{attributes} -
          NameTypeArgArr1Arr2CondDescriptionFromTo
          - -

          Parent Of

          -
            -{parent-of} -
          \ No newline at end of file diff --git a/templates/niobject_nav.html b/templates/niobject_nav.html deleted file mode 100644 index 531afa5..0000000 --- a/templates/niobject_nav.html +++ /dev/null @@ -1,2 +0,0 @@ -

          Hierarchy | List

          -{niobject-contents} diff --git a/templates/version_list.html b/templates/version_list.html deleted file mode 100644 index e264263..0000000 --- a/templates/version_list.html +++ /dev/null @@ -1,8 +0,0 @@ -

          {title}

          - - - - - -{list} -
          NumberDescription
          \ No newline at end of file diff --git a/templates/version_row.html b/templates/version_row.html deleted file mode 100644 index e407dfa..0000000 --- a/templates/version_row.html +++ /dev/null @@ -1,4 +0,0 @@ - - {list-name} - {list-desc} - From dc96f6816710a4edc6532be7a73e7607086b1082 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sat, 6 Jan 2018 19:08:13 -0500 Subject: [PATCH 24/27] Update gen_niflib for nifxml.py Also improve the include() function for CFile so that quotes are not required. --- gen_niflib.py | 116 +++++++++++++++++++++++--------------------------- nifxml.py | 2 - 2 files changed, 53 insertions(+), 65 deletions(-) diff --git a/gen_niflib.py b/gen_niflib.py index e594a50..76ace6f 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -84,9 +84,9 @@ import io import itertools -from nifxml import Member, Compound, Block, native_types, NATIVETYPES -from nifxml import block_types, basic_types, compound_types, enum_types, flag_types, version_types -from nifxml import block_names, basic_names, compound_names, enum_names, flag_names, version_names +from nifxml import Member, Compound, Block +from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_NATIVE +from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND from nifxml import scanBrackets, define_name, parse_XML @@ -248,7 +248,7 @@ def member_setter_declare(self, scope="", suffix=""): else: ltype = "const vector<%s >&"%ltype else: - if not self.type in basic_names: + if not self.type in NAMES_BASIC: ltype = "const %s &"%ltype result = "void " + scope + "Set" + self.cname[0:1].upper() + self.cname[1:] + "( " + ltype + " value )" + suffix @@ -288,11 +288,11 @@ def compound_code_include_h(self): for mem in self.members: file_name = None if mem.type != self.name: - if mem.type in compound_names: - if not compound_types[mem.type].nativetype: + if mem.type in NAMES_COMPOUND: + if not TYPES_COMPOUND[mem.type].nativetype: file_name = "%s%s.h"%(self.gen_file_prefix, mem.ctype) - elif mem.type in basic_names: - if basic_types[mem.type].nativetype == "Ref": + elif mem.type in NAMES_BASIC: + if TYPES_BASIC[mem.type].nativetype == "Ref": file_name = "%sRef.h"%(self.root_file_prefix) if file_name and file_name not in used_structs: used_structs.append( file_name ) @@ -310,7 +310,7 @@ def compound_code_fwd_decl(self): # forward declaration of blocks used_blocks = [] for mem in self.members: - if mem.template in block_names and mem.template != self.name: + if mem.template in NAMES_BLOCK and mem.template != self.name: if not mem.ctemplate in used_blocks: used_blocks.append( mem.ctemplate ) if used_blocks: @@ -329,24 +329,24 @@ def compound_code_include_cpp_set(self, usedirs=False, gen_dir=None, obj_dir=Non result = [] - if self.name in compound_names: + if self.name in NAMES_COMPOUND: result.append('#include "%s%s.h"\n'%(gen_dir, self.cname)) - elif self.name in block_names: + elif self.name in NAMES_BLOCK: result.append('#include "%s%s.h"\n'%(obj_dir, self.cname)) else: assert False # bug # include referenced blocks used_blocks = [] for mem in self.members: - if mem.template in block_names and mem.template != self.name: + if mem.template in NAMES_BLOCK and mem.template != self.name: file_name = '#include "%s%s.h"\n'%(obj_dir, mem.ctemplate) if file_name not in used_blocks: used_blocks.append( file_name ) - if mem.type in compound_names: - subblock = compound_types[mem.type] + if mem.type in NAMES_COMPOUND: + subblock = TYPES_COMPOUND[mem.type] used_blocks.extend(subblock.code_include_cpp_set(True, gen_dir, obj_dir)) for terminal in mem.cond.get_terminals(): - if terminal in block_types: + if terminal in TYPES_BLOCK: used_blocks.append('#include "%s%s.h"\n'%(obj_dir, terminal)) for file_name in sorted(set(used_blocks)): result.append(file_name) @@ -400,19 +400,6 @@ def block_code_include_h(self): parse_XML(NATIVETYPES) -assert version_types -assert version_names -assert basic_types -assert basic_names -assert compound_types -assert compound_names -assert block_types -assert block_names -assert enum_types -assert enum_names -assert flag_types -assert flag_names - # # global data # @@ -563,8 +550,8 @@ def block_code_include_h(self): # Fix known manual update attributes. For now hard code here. -block_types["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True -#block_types["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True +TYPES_BLOCK["NiKeyframeData"].find_member("Num Rotation Keys").is_manual_update = True +#TYPES_BLOCK["NiTriStripsData"].find_member("Num Triangles").is_manual_update = True ACTION_READ = 0 @@ -679,7 +666,10 @@ def include(self, txt): @param txt: The include filepath. @type txt: str """ - self.write( '#include {0}\n'.format(txt) ) + if (txt.startswith('<') and txt.endswith('>')) or (txt.startswith('"') and txt.endswith('"')): + self.write( '#include {0}\n'.format(txt) ) + else: + self.write( '#include "{0}"\n'.format(txt) ) def comment(self, txt, doxygen=True): """ @@ -846,14 +836,14 @@ def stream(self, block, action, localprefix="", prefix="", arg_prefix="", arg_me # now comes the difficult part: processing all members recursively for y in block.members: # get block - if y.type in basic_types: - subblock = basic_types[y.type] - elif y.type in compound_types: - subblock = compound_types[y.type] - elif y.type in enum_types: - subblock = enum_types[y.type] - elif y.type in flag_types: - subblock = flag_types[y.type] + if y.type in TYPES_BASIC: + subblock = TYPES_BASIC[y.type] + elif y.type in TYPES_COMPOUND: + subblock = TYPES_COMPOUND[y.type] + elif y.type in TYPES_ENUM: + subblock = TYPES_ENUM[y.type] + elif y.type in TYPES_FLAG: + subblock = TYPES_FLAG[y.type] # check for links if action in [ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: @@ -1013,7 +1003,7 @@ def stream(self, block, action, localprefix="", prefix="", arg_prefix="", arg_me %(self.indent, self.indent, y.arr2.code(y_arr2_prefix), self.indent-1, self.indent)) z = "%s%s[i%i][i%i]"%(y_prefix, y.cname, self.indent-2, self.indent-1) - if y.type in native_types: + if y.type in TYPES_NATIVE: # these actions distinguish between refs and non-refs if action in [ACTION_READ, ACTION_WRITE, ACTION_FIXLINKS, ACTION_GETREFS, ACTION_GETPTRS]: if (not subblock.is_link) and (not subblock.is_crossref): @@ -1064,7 +1054,7 @@ def stream(self, block, action, localprefix="", prefix="", arg_prefix="", arg_me self.code('%s << "%*s%s[" << i%i << "]: " << %s << endl;'%(stream, 2*self.indent, "", y.name, self.indent-1, z)) self.code('array_output_count++;') else: - subblock = compound_types[y.type] + subblock = TYPES_COMPOUND[y.type] if not y.arr1.lhs: self.stream(subblock, action, "%s%s_"%(localprefix, y.cname), "%s."%z, y_arg_prefix, y_arg) elif not y.arr2.lhs: @@ -1247,8 +1237,8 @@ def overwrite_if_changed( original_file, candidate_file ): mkpath(os.path.join(ROOT_DIR, "src/obj")) mkpath(os.path.join(ROOT_DIR, "src/gen")) -for n in compound_names: - x = compound_types[n] +for n in NAMES_COMPOUND: + x = TYPES_COMPOUND[n] # skip natively implemented types if x.name in NATIVETYPES.keys(): continue @@ -1263,9 +1253,9 @@ def overwrite_if_changed( original_file, candidate_file ): HDR.code( FULLGEN_NOTICE ) HDR.guard( x.cname.upper() ) HDR.code() - HDR.include( '"../NIF_IO.h"' ) + HDR.include( '../NIF_IO.h' ) if n in ["Header", "Footer"]: - HDR.include( '"../obj/NiObject.h"' ) + HDR.include( '../obj/NiObject.h' ) HDR.code( x.code_include_h() ) HDR.namespace( 'Niflib' ) HDR.code( x.code_fwd_decl() ) @@ -1415,7 +1405,7 @@ def overwrite_if_changed( original_file, candidate_file ): HDR.code() HDR.namespace( 'Niflib' ) HDR.code() - for n, x in itertools.chain(enum_types.items(), flag_types.items()): + for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()): if x.options: if x.description: HDR.comment(x.description) @@ -1437,11 +1427,11 @@ def overwrite_if_changed( original_file, candidate_file ): HDR.include( '' ) HDR.code( 'using namespace std;' ) HDR.code() - HDR.include('"../nif_basic_types.h"') + HDR.include('../nif_basic_types.h') HDR.code() HDR.namespace( 'Niflib' ) HDR.code() - for n, x in itertools.chain(enum_types.items(), flag_types.items()): + for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()): if x.options: if x.description: HDR.code() @@ -1459,16 +1449,16 @@ def overwrite_if_changed( original_file, candidate_file ): CPP.code() CPP.include('') CPP.include('') - CPP.include('"../../include/NIF_IO.h"') - CPP.include('"../../include/gen/enums.h"') - CPP.include('"../../include/gen/enums_intl.h"') + CPP.include('../../include/NIF_IO.h') + CPP.include('../../include/gen/enums.h') + CPP.include('../../include/gen/enums_intl.h') CPP.code() CPP.code('using namespace std;') CPP.code() CPP.namespace( 'Niflib' ) CPP.code() CPP.code() - for n, x in itertools.chain(enum_types.items(), flag_types.items()): + for n, x in itertools.chain(TYPES_ENUM.items(), TYPES_FLAG.items()): if x.options: CPP.code( ENUM_IMPL.format(x.cname, x.storage, r''.join((ENUM_IMPL_CASE.format(o.cname, o.name) for o in x.options))) ) CPP.code() @@ -1480,16 +1470,16 @@ def overwrite_if_changed( original_file, candidate_file ): CPP = CFile(io.open(ROOT_DIR + '/src/gen/register.cpp', 'wb')) CPP.code( FULLGEN_NOTICE ) CPP.code() - CPP.include( '"../../include/ObjectRegistry.h"' ) - for n in block_names: - x = block_types[n] - CPP.include( '"../../include/obj/' + x.cname + '.h"' ) + CPP.include( '../../include/ObjectRegistry.h' ) + for n in NAMES_BLOCK: + x = TYPES_BLOCK[n] + CPP.include( '../../include/obj/' + x.cname + '.h' ) CPP.code() CPP.namespace( 'Niflib' ) CPP.code( 'void RegisterObjects() {' ) CPP.code() - for n in block_names: - x = block_types[n] + for n in NAMES_BLOCK: + x = TYPES_BLOCK[n] CPP.code( 'ObjectRegistry::RegisterObject( "' + x.name + '", ' + x.cname + '::Create );' ) CPP.code() CPP.code( '}' ) @@ -1498,8 +1488,8 @@ def overwrite_if_changed( original_file, candidate_file ): # # NiObject Files # -for n in block_names: - x = block_types[n] +for n in NAMES_BLOCK: + x = TYPES_BLOCK[n] x_define_name = define_name(x.cname) if not GENALLFILES and not x.cname in GENBLOCKS: @@ -1615,9 +1605,9 @@ def overwrite_if_changed( original_file, candidate_file ): CPP.code( END_CUSTOM ) CPP.code() - CPP.include( '"../../include/FixLink.h"' ) - CPP.include( '"../../include/ObjectRegistry.h"' ) - CPP.include( '"../../include/NIF_IO.h"' ) + CPP.include( '../../include/FixLink.h' ) + CPP.include( '../../include/ObjectRegistry.h' ) + CPP.include( '../../include/NIF_IO.h' ) CPP.code( x.code_include_cpp( True, "../../include/gen/", "../../include/obj/" ) ) CPP.code( "using namespace Niflib;" ) CPP.code() diff --git a/nifxml.py b/nifxml.py index f593022..b8dd3b2 100644 --- a/nifxml.py +++ b/nifxml.py @@ -98,8 +98,6 @@ NAMES_BLOCK = [] NAMES_VERSION = [] -NATIVETYPES = {} - def class_name(name_in): """ From b8ef1e12f572936885ef31a1c9ee4d19145ba13f Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sat, 6 Jan 2018 19:32:24 -0500 Subject: [PATCH 25/27] Rename nifxml_doc --- nifxml_doc.py => nifdoc.py | 6 +++--- nifxml_tmpl.py => nifdoc_tmpl.py | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename nifxml_doc.py => nifdoc.py (99%) rename nifxml_tmpl.py => nifdoc_tmpl.py (100%) diff --git a/nifxml_doc.py b/nifdoc.py similarity index 99% rename from nifxml_doc.py rename to nifdoc.py index ce10375..4ce3153 100644 --- a/nifxml_doc.py +++ b/nifdoc.py @@ -1,11 +1,11 @@ #!/usr/bin/python """ -nifxml_doc.py +nifdoc.py Generates HTML documentation for the XML file. To list command line options run: - nifxml_doc.py -h + nifdoc.py -h This file is part of nifxml Copyright (c) 2017 NifTools @@ -35,7 +35,7 @@ from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_VERSION from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG, NAMES_VERSION -import nifxml_tmpl as tmpl +import nifdoc_tmpl as tmpl # # Globals diff --git a/nifxml_tmpl.py b/nifdoc_tmpl.py similarity index 100% rename from nifxml_tmpl.py rename to nifdoc_tmpl.py From 1d9b197301286151da9c8614d49d390adff4fbf4 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sat, 6 Jan 2018 21:15:25 -0500 Subject: [PATCH 26/27] Remove docsys references --- doc/{docsys.css => nifdoc.css} | 0 nifdoc.py | 2 +- nifdoc_tmpl.py | 2 +- nifxml.py | 5 ----- 4 files changed, 2 insertions(+), 7 deletions(-) rename doc/{docsys.css => nifdoc.css} (100%) diff --git a/doc/docsys.css b/doc/nifdoc.css similarity index 100% rename from doc/docsys.css rename to doc/nifdoc.css diff --git a/nifdoc.py b/nifdoc.py index 4ce3153..2b0ad29 100644 --- a/nifdoc.py +++ b/nifdoc.py @@ -44,7 +44,7 @@ SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) DOC_PATH = '/doc/' DOC_FILE = '{}.html' -CSS_PATH = os.path.join(SCRIPT_PATH, 'doc/docsys.css') +CSS_PATH = os.path.join(SCRIPT_PATH, 'doc/nifdoc.css') ICO_PATH = os.path.join(SCRIPT_PATH, 'doc/favicon.ico') def clean(string): diff --git a/nifdoc_tmpl.py b/nifdoc_tmpl.py index 03c5559..57187e8 100644 --- a/nifdoc_tmpl.py +++ b/nifdoc_tmpl.py @@ -27,7 +27,7 @@ \tNIF File Format Documentation - {title} -\t +\t \t """ diff --git a/nifxml.py b/nifxml.py index b8dd3b2..93eae58 100644 --- a/nifxml.py +++ b/nifxml.py @@ -1025,13 +1025,8 @@ def ancestors(self): # import elements into our code generating classes # -# import via "import nifxml" from . if os.path.exists("nif.xml"): XML = parse("nif.xml") -# import via "import docsys" from .. -elif os.path.exists("docsys/nif.xml"): - XML = parse("docsys/nif.xml") -# new submodule system elif os.path.exists("nifxml/nif.xml"): XML = parse("nifxml/nif.xml") else: From 6bb4cc76a19b104d79f878364747d98347393df5 Mon Sep 17 00:00:00 2001 From: jonwd7 Date: Sun, 7 Jan 2018 01:36:17 -0500 Subject: [PATCH 27/27] Reorganization, cleanup --- .gitignore | 14 + .project | 17 -- __init__.py | 6 + doc/__init__.py | 0 nifdoc_tmpl.py => doc/nifdoc_tmpl.py | 0 gen_niflib.py | 22 +- nifdoc.py | 28 +- nifxml.py | 415 ++++++++++----------------- 8 files changed, 199 insertions(+), 303 deletions(-) create mode 100644 .gitignore delete mode 100644 .project create mode 100644 doc/__init__.py rename nifdoc_tmpl.py => doc/nifdoc_tmpl.py (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19e45c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Generated html +*.html + +# VS Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/.project b/.project deleted file mode 100644 index d8d55d7..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - nifdocsys - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - - diff --git a/__init__.py b/__init__.py index 596cb5c..e81ca65 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1,7 @@ __all__ = ["nifxml"] + +from .nifxml import parse_xml +from .nifxml import Member, Version, Basic, Compound, Block, Enum, Flag +from .nifxml import TYPES_VERSION, TYPES_BASIC, TYPES_BLOCK, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_NATIVE +from .nifxml import NAMES_VERSION, NAMES_BASIC, NAMES_BLOCK, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG +from .nifxml import class_name, define_name, member_name, version2number, scanBrackets diff --git a/doc/__init__.py b/doc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nifdoc_tmpl.py b/doc/nifdoc_tmpl.py similarity index 100% rename from nifdoc_tmpl.py rename to doc/nifdoc_tmpl.py diff --git a/gen_niflib.py b/gen_niflib.py index 76ace6f..2a4bfa3 100755 --- a/gen_niflib.py +++ b/gen_niflib.py @@ -87,7 +87,7 @@ from nifxml import Member, Compound, Block from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_NATIVE from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND -from nifxml import scanBrackets, define_name, parse_XML +from nifxml import scanBrackets, define_name, parse_xml # The relative path to the project root for compounds and NiObjects (Compound and Block) @@ -186,12 +186,12 @@ def member_code_declare(self, prefix=""): if self.arr1.lhs: if self.arr1.lhs.isdigit(): if self.arr2.lhs and self.arr2.lhs.isdigit(): - result = "array< %s, array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) + result = "Niflib::array< %s, Niflib::array<%s,%s > >"%(self.arr1.lhs, self.arr2.lhs, result) else: - result = "array<%s,%s >"%(self.arr1.lhs, result) + result = "Niflib::array<%s,%s >"%(self.arr1.lhs, result) else: if self.arr2.lhs and self.arr2.lhs.isdigit(): - result = "vector< array<%s,%s > >"%(self.arr2.lhs, result) + result = "vector< Niflib::array<%s,%s > >"%(self.arr2.lhs, result) else: if self.arr2.lhs: result = "vector< vector<%s > >"%result @@ -210,17 +210,17 @@ def member_getter_declare(self, scope="", suffix=""): ltype = "%s *"%self.ctemplate if self.arr1.lhs: if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s > "%(self.arr1.lhs, ltype) + ltype = "Niflib::array<%s,%s > "%(self.arr1.lhs, ltype) # ltype = ltype else: if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "vector< array<%s,%s > >"%(self.arr2.lhs, ltype) + ltype = "vector< Niflib::array<%s,%s > >"%(self.arr2.lhs, ltype) else: ltype = "vector<%s >"%ltype if self.arr2.lhs: if self.arr2.lhs.isdigit(): if self.arr1.lhs.isdigit(): - ltype = "array<%s,%s >"%(self.arr2.lhs, ltype) + ltype = "Niflib::array<%s,%s >"%(self.arr2.lhs, ltype) # ltype = ltype else: ltype = "vector<%s >"%ltype @@ -239,12 +239,12 @@ def member_setter_declare(self, scope="", suffix=""): if self.arr1.lhs.isdigit(): # ltype = "const %s&"%ltype if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const array< %s, array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype) + ltype = "const Niflib::array< %s, Niflib::array<%s,%s > >&"%(self.arr1.lhs, self.arr2.lhs, ltype) else: - ltype = "const array<%s,%s >& "%(self.arr1.lhs, ltype) + ltype = "const Niflib::array<%s,%s >& "%(self.arr1.lhs, ltype) else: if self.arr2.lhs and self.arr2.lhs.isdigit(): - ltype = "const vector< array<%s,%s > >&"%(self.arr2.lhs, ltype) + ltype = "const vector< Niflib::array<%s,%s > >&"%(self.arr2.lhs, ltype) else: ltype = "const vector<%s >&"%ltype else: @@ -398,7 +398,7 @@ def block_code_include_h(self): # Parse XML after patching classes # -parse_XML(NATIVETYPES) +parse_xml(NATIVETYPES) # # global data diff --git a/nifdoc.py b/nifdoc.py index 2b0ad29..4018355 100644 --- a/nifdoc.py +++ b/nifdoc.py @@ -7,20 +7,20 @@ To list command line options run: nifdoc.py -h - This file is part of nifxml - Copyright (c) 2017 NifTools +This file is part of nifxml +Copyright (c) 2017 NifTools - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, version 3. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3. - 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 - General Public License for more details. +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 +General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . +You should have received a copy of the GNU General Public License +along with this program. If not, see . """ from __future__ import unicode_literals @@ -31,11 +31,11 @@ import argparse from shutil import copy2 -from nifxml import Compound, Block, Enum, parse_XML, version2number +from nifxml import Compound, Block, Enum, parse_xml, version2number from nifxml import TYPES_BLOCK, TYPES_BASIC, TYPES_COMPOUND, TYPES_ENUM, TYPES_FLAG, TYPES_VERSION from nifxml import NAMES_BLOCK, NAMES_BASIC, NAMES_COMPOUND, NAMES_ENUM, NAMES_FLAG, NAMES_VERSION -import nifdoc_tmpl as tmpl +from doc import nifdoc_tmpl as tmpl # # Globals @@ -55,7 +55,7 @@ def clean(string): def main(): """Parses the XML and generates all doc pages""" # Parse the XML and sort names - parse_XML() + parse_xml() NAMES_BASIC.sort() NAMES_COMPOUND.sort() NAMES_BLOCK.sort() diff --git a/nifxml.py b/nifxml.py index 93eae58..800a12a 100644 --- a/nifxml.py +++ b/nifxml.py @@ -1,73 +1,53 @@ #!/usr/bin/python - -# TODO: split in multiple files - """ -This module generates C++ code for Niflib from the NIF file format specification XML. - -@author: Amorilia -@author: Shon - -@contact: http://niftools.sourceforge.net - -@copyright: -Copyright (c) 2005, NIF File Format Library and Tools. -All rights reserved. - -@license: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - - Neither the name of the NIF File Format Library and Tools - project nor the names of its contributors may be used to endorse - or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -@var native_types: Maps name of basic or compound type to name of type implemented manually in Niflib. - These are the types tagged by the niflibtype tag in the XML. For example, - if a (basic or compound) type with C{name="ferrari"} has C{niflibtype="car"} - then C{native_types["ferrari"]} equals the string C{"car"}. -@type native_types: C{dictionary} - -@var basic_types: Maps name of basic type to L{Basic} instance. -@type basic_types: C{dictionary} - -@var compound_types: Maps name of compound type to a L{Compound} instance. -@type compound_types: C{dictionary} - -@var block_types: Maps name of the block name to a L{Block} instance. -@type block_types: C{list} - -@var basic_names: Sorted keys of L{basic_types}. -@type basic_names: C{list} - -@var compound_names: Sorted keys of L{compound_types}. -@type compound_names: C{list} - -@var block_names: Sorted keys of L{block_types}. -@type block_names: C{list} +nifxml.py + +Parses nif.xml into dictionaries of classes grouped by XML tag type. + +This file is part of nifxml +Copyright (c) 2017 NifTools + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3. + +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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +This file incorporates work covered by the following copyright and permission notice: + Copyright (c) 2005, NIF File Format Library and Tools. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + - Neither the name of the NIF File Format Library and Tools + project nor the names of its contributors may be used to endorse + or promote products derived from this software without specific + prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. """ from __future__ import unicode_literals @@ -76,10 +56,9 @@ import os import re -import types # -# global data +# Globals # TYPES_NATIVE = {} @@ -99,13 +78,9 @@ NAMES_VERSION = [] -def class_name(name_in): +def class_name(name_in): # type: (str) -> str """ Formats a valid C++ class name from the name format used in the XML. - @param n: The class name to format in C++ style. - @type n: string - @return The resulting valid C++ class name - @rtype: string """ if name_in is None: return None @@ -134,13 +109,9 @@ def class_name(name_in): name_out += '_' return name_out -def define_name(name_in): +def define_name(name_in): # type: (str) -> str """ Formats an all-uppercase version of the name for use in C++ defines. - @param n: The class name to format in define style. - @type n: string - @return The resulting valid C++ define name - @rtype: string """ name_out = '' for i, char in enumerate(name_in): @@ -156,13 +127,9 @@ def define_name(name_in): name_out += '_' return name_out -def member_name(name_in): +def member_name(name_in): # type: (str) -> str """ Formats a version of the name for use as a C++ member variable. - @param name_in: The attribute name to format in variable style. - @type name_in: string - @return The resulting valid C++ variable name - @rtype: string """ if name_in is None or name_in == 'ARG': return name_in @@ -184,13 +151,10 @@ def member_name(name_in): lower = True return name_out -def version2number(s): +def version2number(s): # type: (str) -> int """ - Translates a legible NIF version number to the packed-byte numeric representation. For example, "10.0.1.0" is translated to 0x0A000100. - @param s: The version string to translate into numeric form. - @type s: string - @return The resulting numeric version of the given version string. - @rtype: int + Translates a legible NIF version number to the packed-byte numeric representation. + For example, "10.0.1.0" is translated to 0x0A000100. """ if not s: return None @@ -210,26 +174,11 @@ def version2number(s): return version else: version = 0 - for i in range(0, len(l)): - version += int(l[i]) << ((3-i) * 8) - #return (int(l[0]) << 24) + (int(l[1]) << 16) + (int(l[2]) << 8) + int(l[3]) + for i, ver in enumerate(l): + version += int(ver) << ((3-i) * 8) return version -def userversion2number(s): - """ - Translates a legible NIF user version number to the packed-byte numeric representation. - Currently just converts the string to an int as this may be a raw number. - Probably to be used just in case this understanding changes. - @param s: The version string to translate into numeric form. - @type s: string - @return The resulting numeric version of the given version string. - @rtype: int - """ - if not s: - return None - return int(s) - -def scanBrackets(expr_str, fromIndex=0): +def scanBrackets(expr_str, fromIndex=0): # type: (str, int) -> Tuple[int, int] """Looks for matching brackets. >>> scanBrackets('abcde') @@ -351,7 +300,7 @@ def eval(self, data=None): else: raise NotImplementedError("expression syntax error: operator '" + self._op + "' not implemented") - def __str__(self): + def __str__(self): # type: () -> str """Reconstruct the expression to a string.""" left = str(self._left) @@ -360,7 +309,7 @@ def __str__(self): right = str(self._right) return left + ' ' + self._op + ' ' + right - def encode(self, encoding): + def encode(self, encoding): # type: (str) -> str """ To allow encode() to be called on an Expression directly as if it were a string (For Python 2/3 cross-compatibility.) @@ -368,7 +317,7 @@ def encode(self, encoding): return self.__str__().encode(encoding) @classmethod - def _parse(cls, expr_str, name_filter=None): + def _parse(cls, expr_str, name_filter=None): # type: (str, Callable[[str], str]) -> str """Returns an Expression, string, or int, depending on the contents of .""" # brackets or operators => expression @@ -502,15 +451,8 @@ def _scanBrackets(expr_str, fromIndex=0): raise ValueError("expression syntax error (non-matching brackets?)") return (startpos, endpos) - def code(self, prefix='', brackets=True, name_filter=None): - """Format an expression as a string. - @param prefix: An optional prefix. - @type prefix: string - @param brackets: If C{True}, then put expression between brackets. - @type prefix: string - @return The expression formatted into a string. - @rtype: string - """ + def code(self, prefix='', brackets=True, name_filter=None): # type: (str, bool, Callable[[str], str]) -> str + """Format an expression as a string.""" lbracket = "(" if brackets else "" rbracket = ")" if brackets else "" if not self._op: @@ -588,7 +530,8 @@ class Expr(Expression): """ def __init__(self, n, name_filter=None): """ - This constructor takes the expression in the form of a string and tokenizes it into left-hand side, operator, right hand side, and something called clhs. + This constructor takes the expression in the form of a string and tokenizes it into left-hand side, + operator, right hand side, and something called clhs. @param n: The expression to tokenize. @type n: string """ @@ -603,13 +546,9 @@ class Option: """ This class represents an option in an option list. @ivar value: The C++ value of option variable. Comes from the "value" attribute of the . - @type description: string @ivar cname: The name of this member for use in C++. - @type cname: string """ def __init__(self, element): """ @@ -620,85 +559,50 @@ def __init__(self, element): #sisters = parent.getElementsByTagName('option') # member attributes - self.value = element.getAttribute('value') - self.name = element.getAttribute('name') + self.value = element.getAttribute('value') # type: str + self.name = element.getAttribute('name') # type: str + self.description = self.name # type: str if element.firstChild: assert element.firstChild.nodeType == Node.TEXT_NODE self.description = element.firstChild.nodeValue.strip() - else: - self.description = self.name - self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_").replace(":", "_") + self.cname = self.name.upper().replace(" ", "_").replace("-", "_").replace("/", "_").replace("=", "_").replace(":", "_") # type: str class Member: """ This class represents the nif.xml tag. @ivar name: The name of this member variable. Comes from the "name" attribute of the tag. - @type name: string - @ivar type: The type of this member variable. Comes from the "type" attribute of the tag. - @type type: string @ivar arg: The argument of this member variable. Comes from the "arg" attribute of the tag. - @type arg: string @ivar template: The template type of this member variable. Comes from the "template" attribute of the tag. - @type template: string @ivar arr1: The first array size of this member variable. Comes from the "arr1" attribute of the tag. - @type arr1: Eval @ivar arr2: The first array size of this member variable. Comes from the "arr2" attribute of the tag. - @type arr2: Eval @ivar cond: The condition of this member variable. Comes from the "cond" attribute of the tag. - @type cond: Eval @ivar func: The function of this member variable. Comes from the "func" attribute of the tag. - @type func: string @ivar default: The default value of this member variable. Comes from the "default" attribute of the tag. Formatted to be ready to use in a C++ constructor initializer list. - @type default: string @ivar ver1: The first version this member exists. Comes from the "ver1" attribute of the tag. - @type ver1: string @ivar ver2: The last version this member exists. Comes from the "ver2" attribute of the tag. - @type ver2: string @ivar userver: The user version where this member exists. Comes from the "userver" attribute of the tag. - @type userver: string @ivar userver2: The user version 2 where this member exists. Comes from the "userver2" attribute of the tag. - @type userver2: string @ivar vercond: The version condition of this member variable. Comes from the "vercond" attribute of the tag. - @type vercond: Eval @ivar is_public: Whether this member will be declared public. Comes from the "public" attribute of the tag. - @type is_public: string @ivar is_abstract: Whether this member is abstract. This means that it does not factor into read/write. - @type is_abstract: bool @ivar description: The description of this member variable. Comes from the text between and . - @type description: string @ivar uses_argument: Specifies whether this attribute uses an argument. - @type uses_argument: bool @ivar type_is_native: Specifies whether the type is implemented natively - @type type_is_native: bool @ivar is_duplicate: Specifies whether this is a duplicate of a previously declared member - @type is_duplicate: bool @ivar arr2_dynamic: Specifies whether arr2 refers to an array (?) - @type arr2_dynamic: bool @ivar arr1_ref: Names of the attributes it is a (unmasked) size of (?) - @type arr1_ref: string array? @ivar arr2_ref: Names of the attributes it is a (unmasked) size of (?) - @type arr2_ref: string array? @ivar cond_ref: Names of the attributes it is a condition of (?) - @type cond_ref: string array? @ivar cname: Unlike default, name isn't formatted for C++ so use this instead? - @type cname: string @ivar ctype: Unlike default, type isn't formatted for C++ so use this instead? - @type ctype: string @ivar carg: Unlike default, arg isn't formatted for C++ so use this instead? - @type carg: string @ivar ctemplate: Unlike default, template isn't formatted for C++ so use this instead? - @type ctemplate: string @ivar carr1_ref: Unlike default, arr1_ref isn't formatted for C++ so use this instead? - @type carr1_ref: string @ivar carr2_ref: Unlike default, arr2_ref isn't formatted for C++ so use this instead? - @type carr2_ref: string @ivar ccond_ref: Unlike default, cond_ref isn't formatted for C++ so use this instead? - @type ccond_ref: string @ivar next_dup: Next duplicate member - @type next_dup: Member @ivar is_manual_update: True if the member value is manually updated by the code - @type is_manual_update: bool """ def __init__(self, element): """ @@ -715,37 +619,37 @@ def __init__(self, element): sisters = parent.getElementsByTagName('add') # member attributes - self.name = element.getAttribute('name') - self.suffix = element.getAttribute('suffix') - self.type = element.getAttribute('type') - self.arg = element.getAttribute('arg') - self.template = element.getAttribute('template') - self.arr1 = Expr(element.getAttribute('arr1')) - self.arr2 = Expr(element.getAttribute('arr2')) - self.cond = Expr(element.getAttribute('cond')) - self.func = element.getAttribute('function') - self.default = element.getAttribute('default') - self.orig_ver1 = element.getAttribute('ver1') - self.orig_ver2 = element.getAttribute('ver2') - self.ver1 = version2number(element.getAttribute('ver1')) - self.ver2 = version2number(element.getAttribute('ver2')) - self.userver = userversion2number(element.getAttribute('userver')) - self.userver2 = userversion2number(element.getAttribute('userver2')) - self.vercond = Expr(element.getAttribute('vercond')) - self.is_public = (element.getAttribute('public') == "1") - self.is_abstract = (element.getAttribute('abstract') == "1") - self.next_dup = None - self.is_manual_update = False - self.is_calculated = (element.getAttribute('calculated') == "1") - - #Get description from text between start and end tags + self.name = element.getAttribute('name') # type: str + self.suffix = element.getAttribute('suffix') # type: str + self.type = element.getAttribute('type') # type: str + self.arg = element.getAttribute('arg') # type: str + self.template = element.getAttribute('template') # type: str + self.arr1 = Expr(element.getAttribute('arr1')) # type: Expr + self.arr2 = Expr(element.getAttribute('arr2')) # type: Expr + self.cond = Expr(element.getAttribute('cond')) # type: Expr + self.func = element.getAttribute('function') # type: str + self.default = element.getAttribute('default') # type: str + self.orig_ver1 = element.getAttribute('ver1') # type: str + self.orig_ver2 = element.getAttribute('ver2') # type: str + self.ver1 = version2number(element.getAttribute('ver1')) # type: int + self.ver2 = version2number(element.getAttribute('ver2')) # type: int + xint = lambda s: int(s) if s else None + self.userver = xint(element.getAttribute('userver')) # type: Optional[int] + self.userver2 = xint(element.getAttribute('userver2')) # type: Optional[int] + self.vercond = Expr(element.getAttribute('vercond')) # type: Expr + self.is_public = (element.getAttribute('public') == "1") # type: bool + self.is_abstract = (element.getAttribute('abstract') == "1") # type: bool + self.next_dup = None # type: Optional[Member] + self.is_manual_update = False # type: bool + self.is_calculated = (element.getAttribute('calculated') == "1") # type: bool + + # Get description from text between start and end tags + self.description = "" # type: str if element.firstChild: assert element.firstChild.nodeType == Node.TEXT_NODE self.description = element.firstChild.nodeValue.strip() elif self.name.lower().find("unk") == 0: self.description = "Unknown." - else: - self.description = "" # Format default value so that it can be used in a C++ initializer list if not self.default and (not self.arr1.lhs and not self.arr2.lhs): @@ -787,13 +691,15 @@ def __init__(self, element): self.default = "(%s)%s"%(class_name(self.type), self.default) # calculate other stuff - self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') - self.type_is_native = self.name in TYPES_NATIVE # true if the type is implemented natively + self.uses_argument = (self.cond.lhs == '(ARG)' or self.arr1.lhs == '(ARG)' or self.arr2.lhs == '(ARG)') # type: bool + # true if the type is implemented natively + self.type_is_native = self.name in TYPES_NATIVE # type: bool # calculate stuff from reference to previous members # true if this is a duplicate of a previously declared member - self.is_duplicate = False - self.arr2_dynamic = False # true if arr2 refers to an array + self.is_duplicate = False # type: bool + # true if arr2 refers to an array + self.arr2_dynamic = False # type: bool sis = element.previousSibling while sis: if sis.nodeType == Node.ELEMENT_NODE: @@ -806,10 +712,13 @@ def __init__(self, element): self.arr2_dynamic = True sis = sis.previousSibling - # calculate stuff from reference to next members - self.arr1_ref = [] # names of the attributes it is a (unmasked) size of - self.arr2_ref = [] # names of the attributes it is a (unmasked) size of - self.cond_ref = [] # names of the attributes it is a condition of + # Calculate stuff from reference to next members + # Names of the attributes it is a (unmasked) size of + self.arr1_ref = [] # type: List[str] + # Names of the attributes it is a (unmasked) size of + self.arr2_ref = [] # type: List[str] + # Names of the attributes it is a condition of + self.cond_ref = [] # type: List[str] sis = element.nextSibling while sis != None: if sis.nodeType == Node.ELEMENT_NODE: @@ -826,44 +735,44 @@ def __init__(self, element): sis = sis.nextSibling # C++ names - self.cname = member_name(self.name if not self.suffix else self.name + "_" + self.suffix) - self.ctype = class_name(self.type) - self.carg = member_name(self.arg) - self.ctemplate = class_name(self.template) - self.carr1_ref = [member_name(n) for n in self.arr1_ref] - self.carr2_ref = [member_name(n) for n in self.arr2_ref] - self.ccond_ref = [member_name(n) for n in self.cond_ref] + self.cname = member_name(self.name if not self.suffix else self.name + "_" + self.suffix) # type: str + self.ctype = class_name(self.type) # type: str + self.carg = member_name(self.arg) # type: str + self.ctemplate = class_name(self.template) # type: str + self.carr1_ref = [member_name(n) for n in self.arr1_ref] # type: List[str] + self.carr2_ref = [member_name(n) for n in self.arr2_ref] # type: List[str] + self.ccond_ref = [member_name(n) for n in self.cond_ref] # type: List[str] class Version: """This class represents the nif.xml tag.""" def __init__(self, element): - self.num = element.getAttribute('num') - self.name = self.num # Treat the version as a name to match other tags - self.description = element.firstChild.nodeValue.strip() + self.num = element.getAttribute('num') # type: str + # Treat the version as a name to match other tags + self.name = self.num # type: str + self.description = element.firstChild.nodeValue.strip() # type: str class Basic: """This class represents the nif.xml tag.""" def __init__(self, element, ntypes): - self.name = element.getAttribute('name') + self.name = element.getAttribute('name') # type: str assert self.name # debug - self.cname = class_name(self.name) + self.cname = class_name(self.name) # type: str + self.description = "" # type: str if element.firstChild and element.firstChild.nodeType == Node.TEXT_NODE: self.description = element.firstChild.nodeValue.strip() elif self.name.lower().find("unk") == 0: self.description = "Unknown." - else: - self.description = "" - self.count = element.getAttribute('count') - self.template = (element.getAttribute('istemplate') == "1") - self.options = [] + self.count = element.getAttribute('count') # type: str + self.template = (element.getAttribute('istemplate') == "1") # type: bool + self.options = [] # type: List[Option] - self.is_link = False - self.is_crossref = False - self.has_links = False - self.has_crossrefs = False + self.is_link = False # type: bool + self.is_crossref = False # type: bool + self.has_links = False # type: bool + self.has_crossrefs = False # type: bool - self.nativetype = None + self.nativetype = None # type: Optional[str] if ntypes: self.nativetype = ntypes.get(self.name) if self.nativetype: @@ -908,8 +817,8 @@ class Compound(Basic): def __init__(self, element, ntypes): Basic.__init__(self, element, ntypes) - self.members = [] # list of all members (list of Member) - self.argument = False # does it use an argument? + self.members = [] # type: List[Member] + self.argument = False # type: bool # store all attribute data & calculate stuff for member in element.getElementsByTagName('add'): @@ -1021,76 +930,60 @@ def ancestors(self): parent = parent.inherit return ancestors -# -# import elements into our code generating classes -# - -if os.path.exists("nif.xml"): - XML = parse("nif.xml") -elif os.path.exists("nifxml/nif.xml"): - XML = parse("nifxml/nif.xml") -else: - raise ImportError("nif.xml not found") +def parse_xml(ntypes=None): + """Import elements into our classes""" + if os.path.exists("nif.xml"): + xml = parse("nif.xml") + elif os.path.exists("nifxml/nif.xml"): + xml = parse("nifxml/nif.xml") + else: + raise ImportError("nif.xml not found") -def parse_XML(ntypes=None): - for element in XML.getElementsByTagName('version'): + for element in xml.getElementsByTagName('version'): instance = Version(element) TYPES_VERSION[instance.num] = instance NAMES_VERSION.append(instance.num) - for element in XML.getElementsByTagName('basic'): + for element in xml.getElementsByTagName('basic'): instance = Basic(element, ntypes) assert not instance.name in TYPES_BASIC TYPES_BASIC[instance.name] = instance NAMES_BASIC.append(instance.name) - for element in XML.getElementsByTagName('enum'): + for element in xml.getElementsByTagName('enum'): instance = Enum(element, ntypes) assert not instance.name in TYPES_ENUM TYPES_ENUM[instance.name] = instance NAMES_ENUM.append(instance.name) - for element in XML.getElementsByTagName('bitflags'): + for element in xml.getElementsByTagName('bitflags'): instance = Flag(element, ntypes) assert not instance.name in TYPES_FLAG TYPES_FLAG[instance.name] = instance NAMES_FLAG.append(instance.name) - for element in XML.getElementsByTagName('compound'): + for element in xml.getElementsByTagName('compound'): instance = Compound(element, ntypes) assert not instance.name in TYPES_COMPOUND TYPES_COMPOUND[instance.name] = instance NAMES_COMPOUND.append(instance.name) - for element in XML.getElementsByTagName('niobject'): + for element in xml.getElementsByTagName('niobject'): instance = Block(element, ntypes) assert not instance.name in TYPES_BLOCK TYPES_BLOCK[instance.name] = instance NAMES_BLOCK.append(instance.name) - validate_XML() + validate_xml() -def validate_XML(): +def validate_xml(): """Perform some basic validation on the data retrieved from the XML""" - assert TYPES_VERSION - assert NAMES_VERSION - assert TYPES_BASIC - assert NAMES_BASIC - assert TYPES_COMPOUND - assert NAMES_COMPOUND - assert TYPES_BLOCK - assert NAMES_BLOCK - assert TYPES_ENUM - assert NAMES_ENUM - assert TYPES_FLAG - assert NAMES_FLAG - - assert len(TYPES_VERSION) == len(NAMES_VERSION) - assert len(TYPES_BASIC) == len(NAMES_BASIC) - assert len(TYPES_COMPOUND) == len(NAMES_COMPOUND) - assert len(TYPES_BLOCK) == len(NAMES_BLOCK) - assert len(TYPES_ENUM) == len(NAMES_ENUM) - assert len(TYPES_FLAG) == len(NAMES_FLAG) + assert TYPES_VERSION and NAMES_VERSION and len(TYPES_VERSION) == len(NAMES_VERSION) + assert TYPES_BASIC and NAMES_BASIC and len(TYPES_BASIC) == len(NAMES_BASIC) + assert TYPES_COMPOUND and NAMES_COMPOUND and len(TYPES_COMPOUND) == len(NAMES_COMPOUND) + assert TYPES_BLOCK and NAMES_BLOCK and len(TYPES_BLOCK) == len(NAMES_BLOCK) + assert TYPES_ENUM and NAMES_ENUM and len(TYPES_ENUM) == len(NAMES_ENUM) + assert TYPES_FLAG and NAMES_FLAG and len(TYPES_FLAG) == len(NAMES_FLAG) assert all(name for name in NAMES_VERSION) assert all(name for name in NAMES_BASIC)