# HG changeset patch # User Paul Boddie # Date 1477934796 -3600 # Node ID 1d1b42fb167bdc5b52f1c3d66f1b114ba4a32f8a # Parent 6bb416f1c55048661a55770eecf7eb1e9076fe6d Added initial support for literal sequence instantiation using special functions which create "fragments" - simple size-annotated arrays of attributes - and which assign such fragments to the __data__ attribute of each sequence instance. Added exception handling to the main function in generated programs. diff -r 6bb416f1c550 -r 1d1b42fb167b encoders.py --- a/encoders.py Sun Oct 30 22:33:22 2016 +0100 +++ b/encoders.py Mon Oct 31 18:26:36 2016 +0100 @@ -340,6 +340,14 @@ else: return '"%s"' % str(value).replace('"', '\\"') +def encode_literal_instantiator(path): + + """ + Encode a reference to an instantiator for a literal having the given 'path'. + """ + + return "__newliteral_%s" % encode_path(path) + def encode_literal_reference(n): "Encode a reference to a literal constant with the number 'n'." diff -r 6bb416f1c550 -r 1d1b42fb167b generator.py --- a/generator.py Sun Oct 30 22:33:22 2016 +0100 +++ b/generator.py Mon Oct 31 18:26:36 2016 +0100 @@ -23,7 +23,8 @@ from encoders import encode_bound_reference, encode_function_pointer, \ encode_instantiator_pointer, \ encode_literal_constant, encode_literal_constant_member, \ - encode_literal_constant_value, encode_literal_reference, \ + encode_literal_constant_value, \ + encode_literal_instantiator, encode_literal_reference, \ encode_path, \ encode_predefined_reference, encode_size, \ encode_symbol, encode_tablename, \ @@ -61,6 +62,12 @@ ("__builtins__.notimplemented", "NotImplemented"), ) + literal_instantiator_types = ( + "__builtins__.dict.dict", + "__builtins__.list.list", + "__builtins__.tuple.tuple", + ) + def __init__(self, importer, optimiser, output): self.importer = importer self.optimiser = optimiser @@ -120,7 +127,9 @@ """ print >>f_code, """\ #include +#include #include "types.h" +#include "exceptions.h" #include "ops.h" #include "progconsts.h" #include "progtypes.h" @@ -201,13 +210,9 @@ init_ref = attrs["__init__"] - # Signature: __attr __new_(__attr[]); - - print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) - # Write instantiator definitions. - self.write_instantiator(f_code, path, init_ref) + self.write_instantiator(f_code, f_signatures, path, init_ref) # Write parameter table. @@ -797,11 +802,12 @@ for name, default in self.importer.function_defaults.get(path): structure.append(self.encode_member(path, name, default, "")) - def write_instantiator(self, f_code, path, init_ref): + def write_instantiator(self, f_code, f_signatures, path, init_ref): """ - Write an instantiator to 'f_code' for instances of the class with the - given 'path', with 'init_ref' as the initialiser function reference. + Write an instantiator to 'f_code', with a signature to 'f_signatures', + for instances of the class with the given 'path', with 'init_ref' as the + initialiser function reference. NOTE: This also needs to initialise any __fn__ and __args__ members NOTE: where __call__ is provided by the class. @@ -812,8 +818,13 @@ print >>f_code, """\ __attr %s(__attr __args[]) { + /* Allocate the structure. */ __args[0] = __new(&%s, &%s, sizeof(%s)); + + /* Call the initialiser. */ %s(__args); + + /* Return the allocated object details. */ return __args[0]; } """ % ( @@ -824,6 +835,39 @@ encode_function_pointer(init_ref.get_origin()) ) + print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) + + # Write additional literal instantiators. These do not call the + # initialisers but instead populate the structures directly. + + if path in self.literal_instantiator_types: + print >>f_code, """\ +__attr %s(__attr __args[], unsigned int number) +{ + __attr data; + + /* Allocate the structure. */ + __args[0] = __new(&%s, &%s, sizeof(%s)); + + /* Allocate a structure for the data. */ + data = __newdata(__args, number); + + /* Store a reference to the data in the object's __data__ attribute. */ + __store_via_object(__args[0].value, %s, data); + + /* Return the allocated object details. */ + return __args[0]; +} +""" % ( + encode_literal_instantiator(path), + encode_tablename("", path), + encode_path(path), + encode_symbol("obj", path), + encode_symbol("pos", "__data__") + ) + + print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path) + def write_main_program(self, f_code, f_signatures): """ @@ -833,7 +877,9 @@ print >>f_code, """\ int main(int argc, char *argv[]) -{""" +{ + __Try + {""" for name in self.importer.modules.keys(): function_name = "__main_%s" % encode_path(name) @@ -846,8 +892,14 @@ %s();""" % function_name print >>f_code, """\ - __main___main__(); - return 0; + __main___main__(); + return 0; + } + __Catch_anonymous + { + fprintf(stderr, "Program terminated due to exception.\\n"); + return 1; + } } """ diff -r 6bb416f1c550 -r 1d1b42fb167b lib/__builtins__/list.py --- a/lib/__builtins__/list.py Sun Oct 30 22:33:22 2016 +0100 +++ b/lib/__builtins__/list.py Mon Oct 31 18:26:36 2016 +0100 @@ -31,13 +31,13 @@ "Initialise the list." + # Reserve space for a fragment reference. + + self.__data__ = native._list_init(args) # reserve space for elements + if args is not None: self.extend(args) - # Reserve space for a fragment reference. - - self.__data__ = None - def __getitem__(self, index): "Return the item or slice specified by 'index'." diff -r 6bb416f1c550 -r 1d1b42fb167b lib/__builtins__/tuple.py --- a/lib/__builtins__/tuple.py Sun Oct 30 22:33:22 2016 +0100 +++ b/lib/__builtins__/tuple.py Mon Oct 31 18:26:36 2016 +0100 @@ -20,13 +20,18 @@ """ from __builtins__.iterator import listiterator -from __builtins__.sequence import _getitem, _getslice, _tuple +from __builtins__.sequence import _getitem, _getslice +import native class tuple(object): "Implementation of tuple." - def __init__(self, args): pass + def __init__(self, args=None): + + "Initialise the tuple." + + self.__data__ = native._tuple_init(args) # allocate and copy elements def __getitem__(self, index): @@ -38,10 +43,16 @@ "Return a slice starting from 'start', with the optional 'end'." - return _tuple(_getslice(self, start, end)) + return native._list_to_tuple(_getslice(self, start, end)) + + def __len__(self): - def __len__(self): pass + "Return the length of the tuple." + + return native._tuple_len(self) + def __add__(self, other): pass + def __str__(self): pass def __bool__(self): @@ -58,6 +69,7 @@ # Special implementation methods. - def __get_single_item__(self, index): pass + def __get_single_item__(self, index): + return native._tuple_element(self, index) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 6bb416f1c550 -r 1d1b42fb167b lib/native.py --- a/lib/native.py Sun Oct 30 22:33:22 2016 +0100 +++ b/lib/native.py Mon Oct 31 18:26:36 2016 +0100 @@ -48,10 +48,17 @@ def _str_len(self): pass def _str_nonempty(self): pass +def _list_init(args): pass def _list_len(self): pass def _list_nonempty(self): pass def _list_element(self, index): pass +def _list_to_tuple(l): pass + +def _tuple_init(args): pass +def _tuple_len(self): pass +def _tuple_element(self, index): pass + def _isinstance(obj, cls): pass # vim: tabstop=4 expandtab shiftwidth=4 diff -r 6bb416f1c550 -r 1d1b42fb167b templates/progops.c --- a/templates/progops.c Sun Oct 30 22:33:22 2016 +0100 +++ b/templates/progops.c Mon Oct 31 18:26:36 2016 +0100 @@ -21,6 +21,23 @@ return self; } +/* Generic internal data allocation. */ + +__attr __newdata(__attr args[], unsigned int number) +{ + __fragment *data = calloc(sizeof(__attr), number); + __attr attr = {0, .data=data}; + unsigned int i, j; + + /* Copy the given number of values, starting from the second element. */ + + for (i = 1, j = 0; i <= number; i++, j++) + data->attrs[j] = args[i]; + + data->size = number; + return attr; +} + /* Generic invocation operations. */ /* Invoke the given callable, supplying keyword argument details in the given diff -r 6bb416f1c550 -r 1d1b42fb167b templates/progops.h --- a/templates/progops.h Sun Oct 30 22:33:22 2016 +0100 +++ b/templates/progops.h Mon Oct 31 18:26:36 2016 +0100 @@ -5,6 +5,9 @@ /* Common operations. */ __attr __new(const __table *table, __ref cls, int size); + +__attr __newdata(__attr args[], unsigned int number); + __attr __invoke(__attr callable, int always_callable, unsigned int nkwargs, __param kwcodes[], __attr kwargs[], unsigned int nargs, __attr args[]); @@ -16,5 +19,7 @@ /* Generic operations depending on specific program details. */ void __SETDEFAULT(__ref obj, int pos, __attr value); + __attr __GETDEFAULT(__ref obj, int pos); + int __BOOL(__attr attr); diff -r 6bb416f1c550 -r 1d1b42fb167b templates/types.h --- a/templates/types.h Sun Oct 30 22:33:22 2016 +0100 +++ b/templates/types.h Mon Oct 31 18:26:36 2016 +0100 @@ -31,6 +31,7 @@ Attribute references are references to single attributes. */ typedef struct __obj __obj; +typedef struct __fragment __fragment; typedef struct __attr { @@ -49,9 +50,10 @@ const __ptable * ptable;/* parameter table */ struct __attr (*fn)(); /* callable details */ - int intvalue; /* integer value */ - double floatvalue; /* floating point value */ - char * strvalue; /* string value */ + int intvalue; /* integer value */ + double floatvalue; /* floating point value */ + char * strvalue; /* string value */ + __fragment * data; /* sequence data */ }; } __attr; @@ -64,6 +66,14 @@ typedef __obj * __ref; +/* Fragments are simple collections of attributes employed by sequence types. */ + +typedef struct __fragment +{ + unsigned int size; + __attr attrs[]; +} __fragment; + /* Special instance position value. The pos member of __obj refers to the special type attribute for classes, indicating which position holds the attribute describing the class type. For instances, it is set to zero. */ diff -r 6bb416f1c550 -r 1d1b42fb167b translator.py --- a/translator.py Sun Oct 30 22:33:22 2016 +0100 +++ b/translator.py Mon Oct 31 18:26:36 2016 +0100 @@ -836,10 +836,20 @@ expr = self.process_structure_node(n.node) objpath = expr.get_origin() target = None + literal_instantiation = False # Obtain details of the callable. - if objpath: + # Literals may be instantiated specially. + + if expr.is_name() and expr.name.startswith("$L") and objpath: + literal_instantiation = True + parameters = None + target = encode_literal_instantiator(objpath) + + # Identified targets employ function pointers directly. + + elif objpath: parameters = self.importer.function_parameters.get(objpath) if expr.has_kind(""): target = encode_instantiator_pointer(objpath) @@ -847,6 +857,9 @@ elif expr.has_kind(""): target = encode_function_pointer(objpath) target_structure = encode_path(objpath) + + # Other targets are retrieved at run-time. + else: parameters = None @@ -906,6 +919,12 @@ kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0" kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0" + # Where literal instantiation is occurring, add an argument indicating + # the number of values. + + if literal_instantiation: + argstr += ", %d" % (len(args) - 1) + # First, the invocation expression is presented. stages = [] @@ -1260,6 +1279,9 @@ # Output generation. def start_output(self): + + "Write the declarations at the top of each source file." + print >>self.out, """\ #include "types.h" #include "exceptions.h" @@ -1271,16 +1293,25 @@ """ def start_module(self): + + "Write the start of each module's main function." + print >>self.out, "void __main_%s()" % encode_path(self.name) print >>self.out, "{" self.indent += 1 self.write_temporaries() def end_module(self): + + "End each module by closing its main function." + self.indent -= 1 print >>self.out, "}" def start_function(self, name): + + "Start the function having the given 'name'." + print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name) print >>self.out, "{" self.indent += 1 @@ -1311,17 +1342,30 @@ self.write_parameters(name, True) def end_function(self, name): + + "End the function having the given 'name'." + self.write_parameters(name, False) self.indent -= 1 print >>self.out, "}" print >>self.out def write_temporaries(self): + + "Write temporary storage employed by functions." + self.writeline("__ref __tmp_context, __tmp_value;") self.writeline("__attr __tmp_target, __tmp_result;") self.writeline("__exc __tmp_exc;") def write_parameters(self, name, define=True): + + """ + For the function having the given 'name', write definitions of + parameters found in the arguments array if 'define' is set to a true + value, or write "undefinitions" if 'define' is set to a false value. + """ + parameters = self.importer.function_parameters[name] # Generate any self reference. @@ -1370,6 +1414,16 @@ for result in results: self.statement(result) + def writeline(self, s): + print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1)) + + def writestmt(self, s): + print >>self.out + self.writeline(s) + + def write_comment(self, s): + self.writestmt("/* %s */" % s) + def pad(self, extra=0): return (self.indent + extra) * self.tabstop @@ -1384,14 +1438,4 @@ levels -= 1 return "\n".join(out) - def writeline(self, s): - print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1)) - - def writestmt(self, s): - print >>self.out - self.writeline(s) - - def write_comment(self, s): - self.writestmt("/* %s */" % s) - # vim: tabstop=4 expandtab shiftwidth=4