# HG changeset patch # User Paul Boddie # Date 1477429829 -7200 # Node ID c88dbd0095198eefac5ce3c76b38dfdfdfd1ba6e # Parent 43aac14966b1fc2d438682513a1805cdf9baa51f Added constant generation and support for internal data. Supported special implicit name lookup in the translator to access operator functions. Separated instance structure generation from general structure generation. Fixed and tidied some invocation target operations. diff -r 43aac14966b1 -r c88dbd009519 encoders.py --- a/encoders.py Tue Oct 25 23:06:37 2016 +0200 +++ b/encoders.py Tue Oct 25 23:10:29 2016 +0200 @@ -281,6 +281,33 @@ return "__new_%s" % encode_path(path) +def encode_literal_constant(n): + + "Encode a name for the literal constant with the number 'n'." + + return "__const%d" % n + +def encode_literal_constant_member(value): + + "Encode the member name for the 'value' in the final program." + + return "%svalue" % value.__class__.__name__ + +def encode_literal_constant_value(value): + + "Encode the given 'value' in the final program." + + if isinstance(value, (int, float)): + return str(value) + else: + return '"%s"' % str(value).replace('"', '\\"') + +def encode_literal_reference(n): + + "Encode a reference to a literal constant with the number 'n'." + + return "__constvalue%d" % n + def encode_path(path): "Encode 'path' as an output program object, translating special symbols." @@ -290,6 +317,12 @@ else: return path.replace("#", "__").replace("$", "__").replace(".", "_") +def encode_predefined_reference(path): + + "Encode a reference to a predefined constant value for 'path'." + + return "__predefined_%s" % encode_path(path) + def encode_symbol(symbol_type, path=None): "Encode a symbol with the given 'symbol_type' and optional 'path'." diff -r 43aac14966b1 -r c88dbd009519 generator.py --- a/generator.py Tue Oct 25 23:06:37 2016 +0200 +++ b/generator.py Tue Oct 25 23:10:29 2016 +0200 @@ -21,7 +21,11 @@ from common import CommonOutput from encoders import encode_bound_reference, encode_function_pointer, \ - encode_instantiator_pointer, encode_path, encode_symbol, \ + encode_instantiator_pointer, \ + encode_literal_constant, encode_literal_constant_member, \ + encode_literal_constant_value, encode_literal_reference, \ + encode_path, \ + encode_predefined_reference, encode_symbol, \ encode_type_attribute from os import listdir from os.path import isdir, join, split @@ -59,6 +63,13 @@ "" : "i" } + predefined_constant_members = ( + ("__builtins__.bool", "False"), + ("__builtins__.bool", "True"), + ("__builtins__.none", "None"), + ("__builtins__.notimplemented", "NotImplemented"), + ) + def __init__(self, importer, optimiser, output): self.importer = importer self.optimiser = optimiser @@ -212,7 +223,12 @@ self.make_parameter_table(f_decls, f_defs, path, init_ref.get_origin()) self.populate_structure(Reference(kind, path), attrs, kind, structure) - self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) + + if kind == "": + self.write_instance_structure(f_decls, path, structure_size) + + self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure, + kind == "" and path) # Record function instance details for function generation below. @@ -261,8 +277,7 @@ # A bound version of a method. structure = self.populate_function(path, function_instance_attrs, False) - self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure, - encode_bound_reference(path)) + self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure_size, structure) # An unbound version of a method. @@ -284,6 +299,16 @@ self.make_parameter_table(f_decls, f_defs, path, path) + # Generate predefined constants. + + for path, name in self.predefined_constant_members: + self.make_predefined_constant(f_decls, f_defs, path, name) + + # Generate literal constants. + + for value, n in self.optimiser.constants.items(): + self.make_literal_constant(f_decls, f_defs, n, value) + # Output more boilerplate. print >>f_consts, """\ @@ -311,6 +336,76 @@ f_signatures.close() f_code.close() + def make_literal_constant(self, f_decls, f_defs, n, value): + + """ + Write literal constant details to 'f_decls' (to declare a structure) and + to 'f_defs' (to define the contents) for the constant with the number + 'n' with the given literal 'value'. + """ + + const_path = encode_literal_constant(n) + structure_name = encode_literal_reference(n) + + # NOTE: This makes assumptions about the __builtins__ structure. + + typename = value.__class__.__name__ + ref = Reference("", "__builtins__.%s.%s" % (typename, typename)) + + self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value) + + def make_predefined_constant(self, f_decls, f_defs, path, name): + + """ + Write predefined constant details to 'f_decls' (to declare a structure) + and to 'f_defs' (to define the contents) for the constant located in + 'path' with the given 'name'. + """ + + # Determine the details of the constant. + + attr_path = "%s.%s" % (path, name) + structure_name = encode_predefined_reference(attr_path) + ref = self.importer.get_object(attr_path) + + self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) + + def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None): + + """ + Write constant details to 'f_decls' (to declare a structure) and to + 'f_defs' (to define the contents) for the constant described by 'ref' + having the given 'path' and 'structure_name' (for the constant structure + itself). + """ + + # Obtain the attributes. + + cls = ref.get_origin() + indexes = self.optimiser.attr_table[ref] + attrnames = self.get_attribute_names(indexes) + attrs = self.get_instance_attributes(cls, attrnames) + + # Set the data, if provided. + + if data is not None: + attrs["__data__"] = data + + # Define the structure details. An object is created for the constant, + # but an attribute is provided, referring to the object, for access to + # the constant in the program. + + structure = [] + table_name = encode_tablename("Instance", cls) + structure_size = encode_size(self.structure_size_prefixes[""], cls) + self.populate_structure(ref, attrs, ref.get_kind(), structure) + self.write_structure(f_decls, f_defs, structure_name, table_name, structure_size, structure) + + # Define a macro for the constant. + + attr_name = encode_path(const_path) + print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name) + def make_parameter_table(self, f_decls, f_defs, path, function_path): """ @@ -405,17 +500,13 @@ table_name, structure_size, ",\n ".join([("{%s, %s}" % t) for t in table])) - def write_structure(self, f_decls, f_defs, path, table_name, structure_size, structure, copy=None): + def write_instance_structure(self, f_decls, path, structure_size): """ - Write the declarations to 'f_decls' and definitions to 'f_defs' for - the object having the given 'path', the given 'table_name', and the - given 'structure_size', with 'structure' details used to populate the - definition. + Write a declaration to 'f_decls' for the object having the given 'path' + and the given 'structure_size'. """ - print >>f_decls, "extern __obj %s;\n" % encode_path(path) - # Write an instance-specific type definition for instances of classes. # See: templates/types.h @@ -425,11 +516,21 @@ unsigned int pos; __attr attrs[%s]; } %s; -""" % (structure_size, encode_symbol("obj", copy or path)) +""" % (structure_size, encode_symbol("obj", path)) + + def write_structure(self, f_decls, f_defs, structure_name, table_name, structure_size, structure, path=None): - # Write the corresponding definition. + """ + Write the declarations to 'f_decls' and definitions to 'f_defs' for + the object having the given 'structure_name', the given 'table_name', + and the given 'structure_size', with 'structure' details used to + populate the definition. + """ - is_class = self.importer.get_object(path).has_kind("") + if f_decls: + print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) + + is_class = path and self.importer.get_object(path).has_kind("") pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" print >>f_defs, """\ @@ -440,7 +541,7 @@ %s }}; """ % ( - encode_path(copy or path), table_name, pos, + encode_path(structure_name), table_name, pos, ",\n ".join(structure)) def get_argument_limits(self, path): @@ -595,6 +696,8 @@ else: attr = attrs[attrname] + # Special function pointer member. + if attrname == "__fn__": # Provide bound method references and the unbound function @@ -622,10 +725,19 @@ structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr)) continue + # Special argument specification member. + elif attrname == "__args__": structure.append("{.min=%s, .ptable=&%s}" % (attr, encode_tablename("Function", origin))) continue + # Special internal data member. + + elif attrname == "__data__": + structure.append("{0, .%s=%s}" % (encode_literal_constant_member(attr), + encode_literal_constant_value(attr))) + continue + structure.append(self.encode_member(origin, attrname, attr, kind)) def encode_member(self, path, name, ref, structure_type): @@ -650,6 +762,12 @@ constant_value = "const%d" % constant_number return "{&%s, &%s} /* %s */" % (constant_value, constant_value, name) + # Predefined constant references. + + if (path, name) in self.predefined_constant_members: + attr_path = encode_predefined_reference("%s.%s" % (path, name)) + return "{&%s, &%s} /* %s */" % (attr_path, attr_path, name) + # General undetermined members. if kind in ("", ""): diff -r 43aac14966b1 -r c88dbd009519 templates/types.h --- a/templates/types.h Tue Oct 25 23:06:37 2016 +0200 +++ b/templates/types.h Tue Oct 25 23:10:29 2016 +0200 @@ -48,6 +48,10 @@ __obj * value; /* attribute value */ const __ptable * ptable;/* parameter table */ struct __attr (*fn)(); /* callable details */ + + int intvalue; /* integer value */ + double floatvalue; /* floating point value */ + char * strvalue; /* string value */ }; } __attr; diff -r 43aac14966b1 -r c88dbd009519 translator.py --- a/translator.py Tue Oct 25 23:06:37 2016 +0200 +++ b/translator.py Tue Oct 25 23:10:29 2016 +0200 @@ -108,7 +108,7 @@ "A constant value reference in the translation." def __str__(self): - return "const%d" % self.number + return encode_literal_constant(self.number) class TrLiteralSequenceRef(results.LiteralSequenceRef, TranslationResult): @@ -137,7 +137,7 @@ return False def __repr__(self): - return "AttrResult(%r, %r)" % (self.s, self.origin) + return "AttrResult(%r, %r)" % (self.s, self.get_origin()) class PredefinedConstantRef(AttrResult): @@ -147,7 +147,14 @@ self.value = value def __str__(self): - return self.value + if self.value in ("False", "True"): + return encode_path("__builtins__.bool.%s" % self.value) + elif self.value == "None": + return encode_path("__builtins__.none.%s" % self.value) + elif self.value == "NotImplemented": + return encode_path("__builtins__.notimplemented.%s" % self.value) + else: + return self.value def __repr__(self): return "PredefinedConstantRef(%r)" % self.value @@ -831,13 +838,6 @@ if not args[argnum+1]: args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target, i) - if None in args: - print self.get_namespace_path() - print n - print expr - print target - print args - argstr = "__ARGS(%s)" % ", ".join(args) kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0" kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0" @@ -845,13 +845,13 @@ # The callable is then obtained. if target: - callable = "__tmp_target" + callable = target elif self.always_callable: - callable = "__load_via_object(__tmp_target, %s)" % \ + callable = "__load_via_object(__tmp_target.value, %s).fn" % \ encode_symbol("pos", "__fn__") else: - callable = "__check_and_load_via_object(__tmp_target, %s, %s)" % ( + callable = "__check_and_load_via_object(__tmp_target.value, %s, %s).fn" % ( encode_symbol("pos", "__fn__"), encode_symbol("code", "__fn__")) stages.append(callable) @@ -859,7 +859,7 @@ # With a known target, the function is obtained directly and called. if target: - output = "(\n%s.fn\n)(%s)" % (",\n".join(stages), argstr) + output = "(\n%s\n)(%s)" % (",\n".join(stages), argstr) # With unknown targets, the generic invocation function is applied to # the callable and argument collections. @@ -942,8 +942,7 @@ # Convert operator function names to references. elif n.name.startswith("$op"): - opname = n.name[len("$op"):] - ref = self.importer.get_object("operator.%s" % opname) + ref = self.importer.get_module(self.name).special.get(n.name) return TrResolvedNameRef(n.name, ref) # Get the appropriate name for the name reference, using the same method @@ -1095,7 +1094,8 @@ print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name) print >>self.out, "{" self.indent += 1 - self.writeline("__attr __tmp_context, __tmp_target, __tmp_value;") + self.writeline("__ref __tmp_context, __tmp_value;") + self.writeline("__attr __tmp_target;") # Obtain local names from parameters.