Lichen

Changeset

819:5e73c8cae40c
2018-01-08 Paul Boddie raw files shortlog changelog graph Support instance attribute initialisation using a method parameter notation.
compiler/transformer.py (file) importer.py (file) inspector.py (file) modules.py (file) pyparser/data/Grammar-Lichen (file) translator.py (file)
     1.1 --- a/compiler/transformer.py	Mon Jan 08 00:13:18 2018 +0100
     1.2 +++ b/compiler/transformer.py	Mon Jan 08 00:30:23 2018 +0100
     1.3 @@ -719,7 +719,7 @@
     1.4          # varargslist:
     1.5          #     (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME)
     1.6          #   | fpdef ['=' test] (',' fpdef ['=' test])* [',']
     1.7 -        # fpdef: NAME | '(' fplist ')'
     1.8 +        # fpdef: NAME | '.' NAME | '(' fplist ')'
     1.9          # fplist: fpdef (',' fpdef)* [',']
    1.10          names = []
    1.11          defaults = []
    1.12 @@ -748,7 +748,7 @@
    1.13  
    1.14                  break
    1.15  
    1.16 -            # fpdef: NAME | '(' fplist ')'
    1.17 +            # fpdef: NAME | '.' NAME | '(' fplist ')'
    1.18              names.append(self.com_fpdef(node))
    1.19  
    1.20              i = i + 1
    1.21 @@ -766,9 +766,11 @@
    1.22          return names, defaults, flags
    1.23  
    1.24      def com_fpdef(self, node):
    1.25 -        # fpdef: NAME | '(' fplist ')'
    1.26 +        # fpdef: NAME | '.' NAME | '(' fplist ')'
    1.27          if node[1][0] == token["LPAR"]:
    1.28              return self.com_fplist(node[2])
    1.29 +        elif node[1][0] == token["DOT"]:
    1.30 +            return node[1][1] + node[2][1]
    1.31          return node[1][1]
    1.32  
    1.33      def com_fplist(self, node):
     2.1 --- a/importer.py	Mon Jan 08 00:13:18 2018 +0100
     2.2 +++ b/importer.py	Mon Jan 08 00:30:23 2018 +0100
     2.3 @@ -4,7 +4,7 @@
     2.4  Import logic.
     2.5  
     2.6  Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
     2.7 -              2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     2.8 +              2014, 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     2.9  
    2.10  This program is free software; you can redistribute it and/or modify it under
    2.11  the terms of the GNU General Public License as published by the Free Software
    2.12 @@ -79,6 +79,7 @@
    2.13          self.objects = {}
    2.14          self.classes = {}
    2.15          self.function_parameters = {}
    2.16 +        self.function_attr_initialisers = {}
    2.17          self.function_defaults = {}
    2.18          self.function_locals = {}
    2.19  
     3.1 --- a/inspector.py	Mon Jan 08 00:13:18 2018 +0100
     3.2 +++ b/inspector.py	Mon Jan 08 00:30:23 2018 +0100
     3.3 @@ -4,7 +4,7 @@
     3.4  Inspect and obtain module structure.
     3.5  
     3.6  Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
     3.7 -              2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     3.8 +              2014, 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     3.9  
    3.10  This program is free software; you can redistribute it and/or modify it under
    3.11  the terms of the GNU General Public License as published by the Free Software
    3.12 @@ -591,11 +591,31 @@
    3.13          if is_method and argnames and argnames[0] == "self":
    3.14              del argnames[0]
    3.15  
    3.16 +        # Convert .name entries in the parameters, provided this is a method.
    3.17 +
    3.18 +        l = []
    3.19 +        attr_initialisers = []
    3.20 +
    3.21 +        for argname in argnames:
    3.22 +            if argname[0] == ".":
    3.23 +                if not is_method:
    3.24 +                    raise InspectError("Attribute initialisers are only allowed amongst method parameters.", function_name, n)
    3.25 +
    3.26 +                argname = argname[1:]
    3.27 +                attr_initialisers.append(argname)
    3.28 +
    3.29 +            l.append(argname)
    3.30 +
    3.31 +        argnames = l
    3.32 +
    3.33          # Copy and propagate the parameters.
    3.34  
    3.35          self.importer.function_parameters[function_name] = \
    3.36              self.function_parameters[function_name] = argnames[:]
    3.37  
    3.38 +        self.importer.function_attr_initialisers[function_name] = \
    3.39 +            self.function_attr_initialisers[function_name] = attr_initialisers
    3.40 +
    3.41          # Define all arguments/parameters in the local namespace.
    3.42  
    3.43          locals = \
    3.44 @@ -652,8 +672,15 @@
    3.45          # Track attribute usage within the namespace.
    3.46  
    3.47          path = self.get_namespace_path()
    3.48 +        self.start_tracking(locals)
    3.49  
    3.50 -        self.start_tracking(locals)
    3.51 +        # Establish attributes for .name entries, provided this is a method.
    3.52 +
    3.53 +        for argname in attr_initialisers:
    3.54 +            self.process_assignment_node(
    3.55 +                    compiler.ast.AssAttr(compiler.ast.Name("self"), argname, "OP_ASSIGN"),
    3.56 +                    compiler.ast.Name(argname))
    3.57 +
    3.58          self.process_structure_node(n.code)
    3.59          returns_value = self.stop_tracking()
    3.60  
     4.1 --- a/modules.py	Mon Jan 08 00:13:18 2018 +0100
     4.2 +++ b/modules.py	Mon Jan 08 00:30:23 2018 +0100
     4.3 @@ -4,7 +4,7 @@
     4.4  Module abstractions.
     4.5  
     4.6  Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
     4.7 -              2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     4.8 +              2014, 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     4.9  
    4.10  This program is free software; you can redistribute it and/or modify it under
    4.11  the terms of the GNU General Public License as published by the Free Software
    4.12 @@ -63,6 +63,7 @@
    4.13          # Function details.
    4.14  
    4.15          self.function_parameters = {}
    4.16 +        self.function_attr_initialisers = {}
    4.17          self.function_defaults = {}
    4.18          self.function_locals = {}
    4.19          self.scope_globals = {}
    4.20 @@ -391,6 +392,7 @@
    4.21              self._get_initialised_names(f)
    4.22              self._get_aliased_names(f)
    4.23              self._get_function_parameters(f)
    4.24 +            self._get_function_attr_initialisers(f)
    4.25              self._get_function_defaults(f)
    4.26              self._get_function_locals(f)
    4.27              self.from_lines(f, self.scope_globals)  # "scope globals:"
    4.28 @@ -510,6 +512,15 @@
    4.29                  self.function_parameters[function] = names != "{}" and names.split(", ") or []
    4.30              line = f.readline().rstrip()
    4.31  
    4.32 +    def _get_function_attr_initialisers(self, f):
    4.33 +        f.readline() # "function attribute initialisers:"
    4.34 +        line = f.readline().rstrip()
    4.35 +        while line:
    4.36 +            function, names = self._get_fields(line)
    4.37 +            self.importer.function_attr_initialisers[function] = \
    4.38 +                self.function_attr_initialisers[function] = names != "{}" and names.split(", ") or []
    4.39 +            line = f.readline().rstrip()
    4.40 +
    4.41      def _get_function_defaults(self, f):
    4.42          f.readline() # "function default parameters:"
    4.43          line = f.readline().rstrip()
    4.44 @@ -782,6 +793,17 @@
    4.45                      print >>f, function, "{}"
    4.46  
    4.47              print >>f
    4.48 +            print >>f, "function attribute initialisers:"
    4.49 +            functions = self.function_attr_initialisers.keys()
    4.50 +            functions.sort()
    4.51 +            for function in functions:
    4.52 +                parameters = self.function_attr_initialisers[function]
    4.53 +                if parameters:
    4.54 +                    print >>f, function, ", ".join(parameters)
    4.55 +                else:
    4.56 +                    print >>f, function, "{}"
    4.57 +
    4.58 +            print >>f
    4.59              print >>f, "function default parameters:"
    4.60              functions = self.function_defaults.keys()
    4.61              functions.sort()
     5.1 --- a/pyparser/data/Grammar-Lichen	Mon Jan 08 00:13:18 2018 +0100
     5.2 +++ b/pyparser/data/Grammar-Lichen	Mon Jan 08 00:30:23 2018 +0100
     5.3 @@ -11,10 +11,13 @@
     5.4  
     5.5  funcdef: 'def' NAME parameters ':' suite
     5.6  parameters: '(' [varargslist] ')'
     5.7 +# NOTE: Remove * and ** support.
     5.8  varargslist: ((fpdef ['=' test] ',')*
     5.9                ('*' NAME [',' '**' NAME] | '**' NAME) |
    5.10                fpdef ['=' test] (',' fpdef ['=' test])* [','])
    5.11 -fpdef: NAME | '(' fplist ')'
    5.12 +# NOTE: Replace with flat parameter lists?
    5.13 +# Supporting .name for attribute assignment convenience.
    5.14 +fpdef: NAME | '.' NAME | '(' fplist ')'
    5.15  fplist: fpdef (',' fpdef)* [',']
    5.16  
    5.17  stmt: simple_stmt | compound_stmt
     6.1 --- a/translator.py	Mon Jan 08 00:13:18 2018 +0100
     6.2 +++ b/translator.py	Mon Jan 08 00:30:23 2018 +0100
     6.3 @@ -3,7 +3,7 @@
     6.4  """
     6.5  Translate programs.
     6.6  
     6.7 -Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     6.8 +Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
     6.9  
    6.10  This program is free software; you can redistribute it and/or modify it under
    6.11  the terms of the GNU General Public License as published by the Free Software
    6.12 @@ -854,6 +854,15 @@
    6.13          if self.in_method():
    6.14              self.generate_guard("self")
    6.15  
    6.16 +        # Make assignments for .name entries in the parameters, provided this is
    6.17 +        # a method.
    6.18 +
    6.19 +        if self.in_method():
    6.20 +            for name in self.importer.function_attr_initialisers.get(function_name) or []:
    6.21 +                self.process_assignment_node(
    6.22 +                    compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"),
    6.23 +                    compiler.ast.Name(name))
    6.24 +
    6.25          # Produce the body and any additional return statement.
    6.26  
    6.27          expr = self.process_structure_node(n.code) or \