# HG changeset patch # User Paul Boddie # Date 1515367823 -3600 # Node ID 5e73c8cae40cb6de671050b6c4f3e13e07394bfc # Parent 0df7efebdf5140e7770cadf2ae2f1204088de42d Support instance attribute initialisation using a method parameter notation. diff -r 0df7efebdf51 -r 5e73c8cae40c compiler/transformer.py --- a/compiler/transformer.py Mon Jan 08 00:13:18 2018 +0100 +++ b/compiler/transformer.py Mon Jan 08 00:30:23 2018 +0100 @@ -719,7 +719,7 @@ # varargslist: # (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) # | fpdef ['=' test] (',' fpdef ['=' test])* [','] - # fpdef: NAME | '(' fplist ')' + # fpdef: NAME | '.' NAME | '(' fplist ')' # fplist: fpdef (',' fpdef)* [','] names = [] defaults = [] @@ -748,7 +748,7 @@ break - # fpdef: NAME | '(' fplist ')' + # fpdef: NAME | '.' NAME | '(' fplist ')' names.append(self.com_fpdef(node)) i = i + 1 @@ -766,9 +766,11 @@ return names, defaults, flags def com_fpdef(self, node): - # fpdef: NAME | '(' fplist ')' + # fpdef: NAME | '.' NAME | '(' fplist ')' if node[1][0] == token["LPAR"]: return self.com_fplist(node[2]) + elif node[1][0] == token["DOT"]: + return node[1][1] + node[2][1] return node[1][1] def com_fplist(self, node): diff -r 0df7efebdf51 -r 5e73c8cae40c importer.py --- a/importer.py Mon Jan 08 00:13:18 2018 +0100 +++ b/importer.py Mon Jan 08 00:30:23 2018 +0100 @@ -4,7 +4,7 @@ Import logic. Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, - 2014, 2015, 2016, 2017 Paul Boddie + 2014, 2015, 2016, 2017, 2018 Paul Boddie 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 @@ -79,6 +79,7 @@ self.objects = {} self.classes = {} self.function_parameters = {} + self.function_attr_initialisers = {} self.function_defaults = {} self.function_locals = {} diff -r 0df7efebdf51 -r 5e73c8cae40c inspector.py --- a/inspector.py Mon Jan 08 00:13:18 2018 +0100 +++ b/inspector.py Mon Jan 08 00:30:23 2018 +0100 @@ -4,7 +4,7 @@ Inspect and obtain module structure. Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, - 2014, 2015, 2016, 2017 Paul Boddie + 2014, 2015, 2016, 2017, 2018 Paul Boddie 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 @@ -591,11 +591,31 @@ if is_method and argnames and argnames[0] == "self": del argnames[0] + # Convert .name entries in the parameters, provided this is a method. + + l = [] + attr_initialisers = [] + + for argname in argnames: + if argname[0] == ".": + if not is_method: + raise InspectError("Attribute initialisers are only allowed amongst method parameters.", function_name, n) + + argname = argname[1:] + attr_initialisers.append(argname) + + l.append(argname) + + argnames = l + # Copy and propagate the parameters. self.importer.function_parameters[function_name] = \ self.function_parameters[function_name] = argnames[:] + self.importer.function_attr_initialisers[function_name] = \ + self.function_attr_initialisers[function_name] = attr_initialisers + # Define all arguments/parameters in the local namespace. locals = \ @@ -652,8 +672,15 @@ # Track attribute usage within the namespace. path = self.get_namespace_path() + self.start_tracking(locals) - self.start_tracking(locals) + # Establish attributes for .name entries, provided this is a method. + + for argname in attr_initialisers: + self.process_assignment_node( + compiler.ast.AssAttr(compiler.ast.Name("self"), argname, "OP_ASSIGN"), + compiler.ast.Name(argname)) + self.process_structure_node(n.code) returns_value = self.stop_tracking() diff -r 0df7efebdf51 -r 5e73c8cae40c modules.py --- a/modules.py Mon Jan 08 00:13:18 2018 +0100 +++ b/modules.py Mon Jan 08 00:30:23 2018 +0100 @@ -4,7 +4,7 @@ Module abstractions. Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, - 2014, 2015, 2016, 2017 Paul Boddie + 2014, 2015, 2016, 2017, 2018 Paul Boddie 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 @@ -63,6 +63,7 @@ # Function details. self.function_parameters = {} + self.function_attr_initialisers = {} self.function_defaults = {} self.function_locals = {} self.scope_globals = {} @@ -391,6 +392,7 @@ self._get_initialised_names(f) self._get_aliased_names(f) self._get_function_parameters(f) + self._get_function_attr_initialisers(f) self._get_function_defaults(f) self._get_function_locals(f) self.from_lines(f, self.scope_globals) # "scope globals:" @@ -510,6 +512,15 @@ self.function_parameters[function] = names != "{}" and names.split(", ") or [] line = f.readline().rstrip() + def _get_function_attr_initialisers(self, f): + f.readline() # "function attribute initialisers:" + line = f.readline().rstrip() + while line: + function, names = self._get_fields(line) + self.importer.function_attr_initialisers[function] = \ + self.function_attr_initialisers[function] = names != "{}" and names.split(", ") or [] + line = f.readline().rstrip() + def _get_function_defaults(self, f): f.readline() # "function default parameters:" line = f.readline().rstrip() @@ -782,6 +793,17 @@ print >>f, function, "{}" print >>f + print >>f, "function attribute initialisers:" + functions = self.function_attr_initialisers.keys() + functions.sort() + for function in functions: + parameters = self.function_attr_initialisers[function] + if parameters: + print >>f, function, ", ".join(parameters) + else: + print >>f, function, "{}" + + print >>f print >>f, "function default parameters:" functions = self.function_defaults.keys() functions.sort() diff -r 0df7efebdf51 -r 5e73c8cae40c pyparser/data/Grammar-Lichen --- a/pyparser/data/Grammar-Lichen Mon Jan 08 00:13:18 2018 +0100 +++ b/pyparser/data/Grammar-Lichen Mon Jan 08 00:30:23 2018 +0100 @@ -11,10 +11,13 @@ funcdef: 'def' NAME parameters ':' suite parameters: '(' [varargslist] ')' +# NOTE: Remove * and ** support. varargslist: ((fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [',']) -fpdef: NAME | '(' fplist ')' +# NOTE: Replace with flat parameter lists? +# Supporting .name for attribute assignment convenience. +fpdef: NAME | '.' NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] stmt: simple_stmt | compound_stmt diff -r 0df7efebdf51 -r 5e73c8cae40c translator.py --- a/translator.py Mon Jan 08 00:13:18 2018 +0100 +++ b/translator.py Mon Jan 08 00:30:23 2018 +0100 @@ -3,7 +3,7 @@ """ Translate programs. -Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie +Copyright (C) 2015, 2016, 2017 Paul Boddie 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 @@ -854,6 +854,15 @@ if self.in_method(): self.generate_guard("self") + # Make assignments for .name entries in the parameters, provided this is + # a method. + + if self.in_method(): + for name in self.importer.function_attr_initialisers.get(function_name) or []: + self.process_assignment_node( + compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"), + compiler.ast.Name(name)) + # Produce the body and any additional return statement. expr = self.process_structure_node(n.code) or \