#!/usr/bin/env python import sys import getopt import os import string import io array_names = set() enum_names = set() boolean_names = set() byte_names = set() little_endian_names = set() little_endian_names.add("TTableTag") implementation_code = io.StringIO() def get_field_type_and_validation(field_etc): validation = None i = field_etc.find("=") if i == -1: i = field_etc.find(" in [") if i != -1: validation = field_etc[i + len(" in [") : ].strip() assert(validation.endswith("]")) validation = validation[ : -1] validation = [item.strip() for item in validation.strip().split(",")] else: validation = field_etc[i + len("=") : ].strip() validation = [validation] if i != -1: field_type = field_etc[: i] else: field_type = field_etc i = field_type.find("{") if i > -1: field_type = field_type[ : i] return field_type.strip(), validation def is_array_P(field_type): if field_type in array_names: return True if field_type.startswith("array"): return True return False def extract_array_of(field_type): i = field_type.find(" of ") assert(i != -1) return field_type[i + len(" of ") : ] def generate_load_unpacker(field_accessor, field_type, implementation_code): if field_type not in little_endian_names: if is_array_P(field_type): element_type = extract_array_of(field_type) implementation_code.write("for fArrayIndex := Low(%(field_accessor)s) to High(%(field_accessor)s) do {****}\n" % locals()) generate_load_unpacker("%s[fArrayIndex]" % field_accessor, element_type, implementation_code) elif field_type in boolean_names or field_type.endswith("Boolean") or field_type in byte_names or field_type.endswith("Byte") or field_type.endswith("TUINT8"): # do not need to shuffle data types with size < 8 bits. implementation_code.write(";\n") elif field_type in enum_names: implementation_code.write(""" fEnumValue := TInteger(%(field_accessor)s); ToHostByteOrderFromBigEndianM(fEnumValue); %(field_accessor)s := %(field_type)s(fEnumValue); """ % locals()) else: implementation_code.write(""" ToHostByteOrderFromBigEndianM(%s); """ % field_accessor) def generate_save_packer(field_accessor, field_type, field_validation, implementation_code): if field_validation is not None and len(field_validation) == 1: # and isinstance(field_validation, list): constant_value = field_validation[0] #if field_type.find("INT") == -1: # constant_value = constant_value.replace("'", "") implementation_code.write("\t%s := %s;\n" % (field_accessor, constant_value)) if field_type not in little_endian_names: if is_array_P(field_type): element_type = extract_array_of(field_type) implementation_code.write("for fArrayIndex := Low(%(field_accessor)s) to High(%(field_accessor)s) do {****}\n" % locals()) generate_save_packer("%s[fArrayIndex]" % field_accessor, element_type, None, implementation_code) elif field_type in boolean_names or field_type.endswith("Boolean") or field_type in byte_names or field_type.endswith("Byte") or field_type.endswith("TUINT8"): # do not need to shuffle data types with size < 8 bits. implementation_code.write(";\n") elif field_type in enum_names: implementation_code.write(""" fEnumValue := TInteger(%(field_accessor)s); ToBigEndianFromHostByteOrderM(fEnumValue); %(field_accessor)s := %(field_type)s(fEnumValue); // questionable. """ % locals()) else: implementation_code.write("\tToBigEndianFromHostByteOrderM(%s);\n" % field_accessor) def finish_record(name, definition): global implementation_code if name is None: return clean_definition = [[item.strip() for item in entry_string.split(";", 1)[0].split(":", 1)] for entry_string in definition if entry_string.find(":") != -1 and entry_string.find(":") < (entry_string.find("//") if entry_string.find("//") != -1 else len(entry_string))] #output.write("\ntype\n") output.write("\t%s = record\n" % name) for definition_line in definition: c = definition_line.find("//") if c == -1: c = len(definition_line) i = definition_line.find("=") if i == -1 or i >= c: i = definition_line.find(" in [") if i != -1: j = definition_line.find("]") definition_line = definition_line[ : i] + definition_line[j + 1 : ] elif i < c: j = definition_line.find(" ", i + 2) k = definition_line.find(";", i + 2) if k != -1 and (k < j or j == -1): j = k assert(j != -1) definition_line = definition_line[ : i] + definition_line[j : ] output.write("\t\t%s\n" % definition_line) output.write("\tend;\n") load_function_declaration = "procedure Load(aInputStream : TStream; out aOutputRecord : %s); overload;" % name save_function_declaration = "procedure Save(const aInputRecord : %s; aOutputStream : TStream); overload;" % name output.write("%s\n" % load_function_declaration) output.write("%s\n" % save_function_declaration) #print name, definition, clean_definition implementation_code.write("%s" % load_function_declaration) implementation_code.write(""" var \tfOnDisk : %(name)s; \tfEnumValue : TCardinal; \tfArrayIndex : TInteger; begin \taInputStream.ReadBuffer(fOnDisk, Sizeof(fOnDisk)); \taOutputRecord := fOnDisk; """ % locals()) for field_name, field_etc in clean_definition: field_type, field_validation = get_field_type_and_validation(field_etc) generate_load_unpacker("aOutputRecord.%s" % field_name, field_type, implementation_code) for field_name, field_etc in clean_definition: field_type, field_validation = get_field_type_and_validation(field_etc) if field_validation is not None: implementation_code.write("\tEnsure(%s);\n" % " or ".join(["(aOutputRecord.%s = %s)" % (field_name, validation_item) for validation_item in field_validation])); implementation_code.write(""" end; """) implementation_code.write("%s" % save_function_declaration) implementation_code.write(""" var \tfOnDisk : %(name)s; \tfEnumValue : TInteger; \tfArrayIndex : TInteger; begin \tfOnDisk := aInputRecord; """ % locals()) for field_name, field_etc in clean_definition: field_type, field_validation = get_field_type_and_validation(field_etc) generate_save_packer("fOnDisk.%s" % field_name, field_type, field_validation, implementation_code) implementation_code.write(""" \taOutputStream.WriteBuffer(fOnDisk, Sizeof(fOnDisk)); end; """) options, args = getopt.getopt(sys.argv[1 : ], "o:") options = dict(options) name = args[0] basename = os.path.basename(name) record_definition = [] record_name = None record_flags = "" #unit_name = "OpenType_R_%s" % basename.split(".", 1)[0] #output.write("unit %s;\n" % unit_name) input_file = open(name, "r") if "-o" in options: output_name = options["-o"] output_temp_name = output_name + ".tmp" output = open(output_temp_name, "w") else: output = sys.stdout output.write("{$IFDEF in_interface}\n") in_record = False for line in input_file.readlines(): if not in_record: i = line.find("//") if i > -1: non_comment = line[ : i].strip() else: non_comment = line.strip() if non_comment.endswith("= record") or non_comment.endswith("= packed record"): # new record. if non_comment.endswith("= packed record"): record_flags = "packed " else: record_flags = "" #finish_record(record_name, record_definition) record_definition = [] record_name = non_comment.split("=", 1)[0].strip() in_record = True else: if non_comment.find("= set ") > -1 or non_comment.find("= (") > -1: # set or enum type. enum_name = non_comment.split("=", 1)[0].strip() enum_names.add(enum_name) output.write(line) else: # in record: line = line.strip() if line.startswith("end;"): # end record. finish_record(record_name, record_definition) in_record = False elif line != "": # record contents. i = line.find("//") if i > -1: non_comment = line[ : i].rstrip() else: non_comment = line assert(non_comment == "" or non_comment.endswith(";")) record_definition.append(line.strip()) input_file.close() output.write("{$ENDIF in_interface}\n") output.write("{$IFDEF in_implementation}\n") output.write(implementation_code.getvalue()) output.write("{$ENDIF in_implementation}\n") if not (output is sys.stdout): output.close() try: os.unlink(output_name) except: pass os.rename(output_temp_name, output_name)