# HG changeset patch # User Paul Boddie # Date 1235338281 -3600 # Node ID 932777a0111f978c755e391affe9afc0fc9f0f7f # Parent daa47807b50581dc7ea471e6f0579f68b149df6c Removed some parts of the structure documentation since the assignment documentation now covers context transformations more accurately. Moved the production of raw image details to the individual data and program classes. Split the default function parameter tests up into smaller units. diff -r daa47807b505 -r 932777a0111f docs/assignment.txt --- a/docs/assignment.txt Sun Feb 22 02:04:30 2009 +0100 +++ b/docs/assignment.txt Sun Feb 22 22:31:21 2009 +0100 @@ -51,3 +51,18 @@ instance preserved LoadAttr LoadAttrIndex Access to a namespace may not preserve the stored context + +Access to class attributes via instances: + + Access to stored value with... Effect on context + ------------------------------ ----------------- + compatible class as context overridden + incompatible class as context preserved + null context preserved + other context (instance) preserved + + LoadAttrIndex must therefore check whether the context must be overridden + + Since the object table encodes sufficient information (an instance must be + compatible to access the class attribute, and compatibility information is + stored), an explicit compatibility test is not required at run-time diff -r daa47807b505 -r 932777a0111f docs/structures.txt --- a/docs/structures.txt Sun Feb 22 02:04:30 2009 +0100 +++ b/docs/structures.txt Sun Feb 22 22:31:21 2009 +0100 @@ -20,112 +20,28 @@ Stored Values and Contexts -------------------------- -In a program image, generated attribute data will employ values, and these -values will generally have the following context definitions according to the -type of the referenced objects: - - Referenced Object Type Context - ---------------------- ------- - - Function None - - Method Parent object (class) - - Class None - -Value and Context Transformations ---------------------------------- - -Values are acquired through name lookups and attribute access, yielding -the appropriate object reference together with a context reference as -indicated in the following table: - - Type of Access Context Notes - -------------- ------- ----- - - Local name Preserved Functions provide no context - - Global name Preserved Modules provide no context - - Class-originating Accessor Class accessor preserves the stored - attribute -or- context; instance accessor overrides - Preserved the stored context if it is null or - belongs to the instance's class - hierarchy - - Instance-originating Preserved Methods retain their original context - attribute - -There may be some scope for simplifying the above, to the detriment of Python -compatibility, since the unbound vs. bound methods situation can be confusing. +See assignment.txt for information about contexts and transformations. Acquiring Values ---------------- -According to the table describing value acquisition, different instructions -must implement different operations when acquiring values: - - Instruction Purpose Context Operations - ----------- ------- ------------------ - - LoadConst Load class, function, Combine null context with loaded - module, constant object +There are two classes of instructions which provide values: - LoadAddress Load attribute from Classes, functions and modules - known object cause the loaded attribute to be - (typically classes and retrieved unchanged; whereas - modules) constants (representing instances) - cause the constant to override the - attribute's own context (since all - attributes should belong to the - constant's class hierarchy) - - LoadAddressContext Load attribute Override loaded context with a - from known object predetermined object (provided - (typically classes) that the object and context are - for an instance compatible, which can be tested at - compile-time) + Instruction Purpose Context Operations + ----------- ------- ------------------ - LoadAttr Load attribute from Contexts are preserved (since only - instance values stored on instances can be - accessed in this way, and their - contexts would never be overridden - upon access - - LoadAttrIndex Load attribute from Classes, functions and modules as - object (can be the unknown object accessor cause - classes, modules, the loaded attribute to be - instances...) retrieved unchanged; instances - cause the LoadAttr rules to apply - (class compatibility applies) - -A certain amount of run-time testing might be required for both LoadAttr and -LoadAttrIndex instructions. However, with certain restrictions in place around -class attributes, some simplifications are possible: + LoadConst Load class, function, Combine null context with + module, constant loaded object - * Since only class-originating attributes may cause context overriding, and - since class attributes may only be defined within class definitions, the - attributes whose context may be modified should be known at compile-time. - (These will be those attributes whose context agrees with their parent - class.) - - * By recording a special context value for attributes whose context can be - overridden, this value can be tested efficiently at run-time where the - appropriate conditions are satisfied. (This special context value or - indicator will be present in the object table record for the attribute.) - - * It should be possible to move the instance compatibility condition testing - to compile-time by testing the compatibility of the origin of an attribute - with the class on which it is stored. However, some compatibility testing - will still be required if invoking methods via classes, since the instance - will be specified in the argument list instead of being presented in an - attribute lookup instruction. + LoadAddress Load attribute from Preserve or override stored + LoadAddressContext class, module, context (as described in + LoadAttr instance assignment.txt) + LoadAttrIndex Storing Values -------------- -According to the table describing value acquisition, different instructions -must implement different operations when acquiring values: +There is only one class of instruction for storing values: Instruction Purpose Context Operations ----------- ------- ------------------ @@ -150,6 +66,7 @@ assignments to classes Note that contexts are never changed in the stored value: they are preserved. +See assignment.txt for more information. Objects ------- diff -r daa47807b505 -r 932777a0111f micropython/__init__.py --- a/micropython/__init__.py Sun Feb 22 02:04:30 2009 +0100 +++ b/micropython/__init__.py Sun Feb 22 22:31:21 2009 +0100 @@ -29,8 +29,14 @@ importer.load_from_file(filename) importer.vacuum() -Such importer objects are the most convenient mechanism through which the -functionality of the micropython package may be accessed. +To generate programs, the above importer should be supplied in the +initialisation of a program instance, and then various methods are called: + +program = Program(importer) +image = program.get_raw_image() + +Such importer and program objects are the most convenient mechanism through +which the functionality of the micropython package may be accessed. """ from micropython.common import * @@ -242,7 +248,7 @@ item.code_location = pos + len(item.defaults) elif isinstance(item, micropython.data.Const): - pos += len(self.raw_data(item)) + pos += len(item.raw_data()) else: pos += 1 @@ -254,97 +260,26 @@ for item in self.code: if isinstance(item, micropython.data.Attr): - self.raw_code.append(( - item.context and item.context.location, - item.value and item.value.location # no useful context is provided - )) + self.raw_code += item.as_raw(objtable) elif isinstance(item, Block): - self.raw_code += self.raw_block(item) + assert item.location == len(self.raw_code) + self.raw_code += item.as_raw(objtable) # Using classcode, attrcode, codeaddr, codedetails, instance. elif isinstance(item, micropython.data.Class): assert item.instance_template_location == len(self.raw_code) - - classcode = objtable.as_list().get_code(item.full_name()) - attrcode = objtable.get_index(item.full_name()) - - # Append a template of an instance for use when - # instantiating classes. - - call_method = item.get("__call__") - call_method_code_location = call_method and call_method.value.code_location - - self.raw_code.append( - DataObject( - classcode, - attrcode, - call_method_code_location, - ( - call_method and len(call_method.value.positional_names), - call_method and len(call_method.value.defaults) - ), - 1, - item.full_name() - ) - ) - - assert item.location == len(self.raw_code) - - # NOTE: The instantiator code is the first block of the class. - - instantiator_code_location = item.get_instantiator().blocks[0].location - - # NOTE: Need initialiser details! - self.raw_code.append( - DataObject( - classcode, - attrcode, - instantiator_code_location, - ( - len(item.get_instantiator().positional_names), - len(item.get_instantiator().defaults) - ), - 0, - item.full_name() - ) - ) + self.raw_code += item.as_raw(objtable) + assert item.location == len(self.raw_code) - 1 elif isinstance(item, micropython.data.Const): assert item.location == len(self.raw_code) - - # NOTE: Need class details! - self.raw_code.append( - DataObject( - objtable.as_list().get_code(item.value_type_name()), - objtable.get_index(item.value_type_name()), - None, - None, - 1, - item.value_type_name() - ) - ) - - self.raw_code += self.raw_data(item) + self.raw_code += item.as_raw(objtable) elif isinstance(item, micropython.data.Function): assert item.location == len(self.raw_code) - - # NOTE: Need class and parameter details! Should arguably be types.FunctionType. - self.raw_code.append( - DataObject( - objtable.as_list().get_code("__builtins__.function"), - objtable.get_index("__builtins__.function"), - item.code_location, - ( - len(item.positional_names), - len(item.defaults) - ), - 0, - "__builtins__.function" - ) - ) + self.raw_code += item.as_raw(objtable) # Check the code location only where the code has been generated. @@ -353,17 +288,7 @@ elif isinstance(item, micropython.data.Module): assert item.location == len(self.raw_code) - - self.raw_code.append( - DataObject( - objtable.as_list().get_code(item.full_name()), - None, # module name not used as an attribute - None, - None, - 0, - item.full_name() - ) - ) + self.raw_code += item.as_raw(objtable) else: self.raw_code.append(item) @@ -380,29 +305,6 @@ self.code_location = self.importer.modules["__main__"].code_location return self.raw_code - def raw_block(self, block): - - "Return the code for the given 'block'." - - assert block.location == len(self.raw_code) - for i, item in enumerate(block.code): - if hasattr(item, "location"): - item.location = location + i - return block.code - - def raw_data(self, item): - - "Return the data for the given 'item'." - - datatype = item.value_type_name() - - # NOTE: Start simple and use single entries for most types. - - if datatype in ("__builtins__.tuple", "__builtins__.list"): - return [len(item.value)] + list(item.value) - else: - return [item.value] - def get_object_table(self): "Return a table with details of attributes for classes and modules." diff -r daa47807b505 -r 932777a0111f micropython/ast.py --- a/micropython/ast.py Sun Feb 22 02:04:30 2009 +0100 +++ b/micropython/ast.py Sun Feb 22 22:31:21 2009 +0100 @@ -581,7 +581,7 @@ # 5. Invocation of the target # 6. Discarding of the frame # - # In order to support nested invocations - eg. a(b(c)) - use of the + # In order to support nested invocations - such as a(b(c)) - use of the # temporary storage is essential. def _startCallFunc(self): @@ -855,9 +855,6 @@ self._endCallFuncArgs(nargs_max) # Or generate instructions to do this at run-time. - # NOTE: CheckFrame has to check the number of arguments and to fill in - # NOTE: defaults; it also has to shift the invocation frame according to - # NOTE: the context in use. else: max_pos = max(max(employed_positions or [-1]), max_keyword_pos, frame_pos - 1) diff -r daa47807b505 -r 932777a0111f micropython/common.py --- a/micropython/common.py Sun Feb 22 02:04:30 2009 +0100 +++ b/micropython/common.py Sun Feb 22 22:31:21 2009 +0100 @@ -84,6 +84,15 @@ def __repr__(self): return "Block(%r, location=%r)" % (id(self), self.location) + def as_raw(self, objtable): + + "Return the code for the given 'block'." + + for i, item in enumerate(self.code): + if hasattr(item, "location"): + item.location = location + i + return self.code + # Program data representations. class DataObject: diff -r daa47807b505 -r 932777a0111f micropython/data.py --- a/micropython/data.py Sun Feb 22 02:04:30 2009 +0100 +++ b/micropython/data.py Sun Feb 22 22:31:21 2009 +0100 @@ -310,6 +310,14 @@ self.name, shortrepr(self.value), self.assignments ) + def as_raw(self, objtable): + return [ + ( + self.context and self.context.location, + self.value and self.value.location + ) + ] + # Instances are special in that they need to be wrapped together with context in # a running program, but they are not generally constant. @@ -357,6 +365,26 @@ __shortrepr__ = __repr__ + def as_raw(self, objtable): + # NOTE: Need class details! + return [ + DataObject( + objtable.as_list().get_code(self.value_type_name()), + objtable.get_index(self.value_type_name()), + None, + None, + 1, + self.value_type_name() + ) + ] + self.raw_data() + + def raw_data(self): + # NOTE: Start simple and use single entries for most types. + if self.value_type_name() in ("__builtins__.tuple", "__builtins__.list"): + return [len(self.value)] + list(self.value) + else: + return [self.value] + # Support constants as dictionary keys in order to build constant tables. def __eq__(self, other): @@ -433,6 +461,42 @@ def __shortrepr__(self): return "Class(%r, %s)" % (self.name, shortrepr(self.parent)) + def as_raw(self, objtable): + classcode = objtable.as_list().get_code(self.full_name()) + attrcode = objtable.get_index(self.full_name()) + + # Append a template of an instance for use when instantiating classes. + + call_method = self.get("__call__") + call_method_code_location = call_method and call_method.value.code_location + + # NOTE: The instantiator code is the first block of the class. + + instantiator_code_location = self.get_instantiator().blocks[0].location + + return [ + DataObject( + classcode, attrcode, call_method_code_location, + ( + call_method and len(call_method.value.positional_names), + call_method and len(call_method.value.defaults) + ), + 1, + self.full_name() + ), + DataObject( + classcode, attrcode, instantiator_code_location, + ( + len(self.get_instantiator().positional_names), + len(self.get_instantiator().defaults) + ), + 0, + self.full_name() + ) + ] + + # Namespace-related methods. + def _context(self, value): """ @@ -797,6 +861,24 @@ self.name, shortrepr(self.parent) ) + def as_raw(self, objtable): + # NOTE: Need class and parameter details! Should arguably be types.FunctionType. + return [ + DataObject( + objtable.as_list().get_code("__builtins__.function"), + objtable.get_index("__builtins__.function"), + self.code_location, + ( + len(self.positional_names), + len(self.defaults) + ), + 0, + "__builtins__.function" + ) + ] + + # Namespace-related methods. + def store_default(self, value): attr = Attr(None, self, None, None, value) attr.update(value, 1) @@ -983,6 +1065,18 @@ def __shortrepr__(self): return "Module(%r)" % self.name + def as_raw(self, objtable): + return [ + DataObject( + objtable.as_list().get_code(self.full_name()), + None, # module name not used as an attribute + None, + None, + 0, + self.full_name() + ) + ] + # Attribute methods. "Return the module attribute names provided by the module." diff -r daa47807b505 -r 932777a0111f tests/call_func_default.py --- a/tests/call_func_default.py Sun Feb 22 02:04:30 2009 +0100 +++ b/tests/call_func_default.py Sun Feb 22 22:31:21 2009 +0100 @@ -1,29 +1,9 @@ #!/usr/bin/env python -x = 123 - def f(a, b, c=4): pass f(1, 2, 3) -f(1, b=2, c=3) -f(c=3, b=2, a=1) f(1, 2) -g = f -g(1, c=3, b=2) -g(1, 2) - -def g(a, c, b=5): - pass - -g(1, c=3, b=2) -g(1, 3) - -def h(a, b, c=f(1, 2, 3)): - pass - -h(1, 2, 3) -h(1, 2) - # vim: tabstop=4 expandtab shiftwidth=4 diff -r daa47807b505 -r 932777a0111f tests/call_func_default_dynamic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_default_dynamic.py Sun Feb 22 22:31:21 2009 +0100 @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +def f(a, b, c): + return c + +def h(a, b, c=f(1, 2, 3)): + return c + +h(1, 2, 3) +h(1, 2) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r daa47807b505 -r 932777a0111f tests/call_func_default_keyword.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_default_keyword.py Sun Feb 22 22:31:21 2009 +0100 @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +def f(a, b, c=4): + pass + +f(1, 2, 3) +f(1, b=2, c=3) +f(c=3, b=2, a=1) +f(1, 2) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r daa47807b505 -r 932777a0111f tests/call_func_default_redefine.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_default_redefine.py Sun Feb 22 22:31:21 2009 +0100 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +def f(a, b, c=4): + pass + +g = f +g(1, c=3, b=2) +g(1, 2) + +def g(a, c, b=5): + pass + +g(1, c=3, b=2) +g(1, 3) + +# vim: tabstop=4 expandtab shiftwidth=4