Merge: Generalized class members
authorJean Privat <jean@pryen.org>
Thu, 26 Feb 2015 02:23:18 +0000 (09:23 +0700)
committerJean Privat <jean@pryen.org>
Thu, 26 Feb 2015 02:23:18 +0000 (09:23 +0700)
This PR change the way class members are parsed.

Before, the order was strict: class annotations, then extern code block, then super clauses, then properties.
After, annotations and super clauses can appear in any order within the properties

The need is to enable class annotations inside the class (cf #1158) I did not find another simple way to do this.

Now, one can write
~~~nit
class B
   super A
   serializable
   var i: Int
   autoinit x, i
end
~~~

Pull-Request: #1159
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

1  2 
src/modelize/modelize_property.nit
src/parser/parser_nodes.nit
tests/sav/test_parser_args1.res

@@@ -181,7 -181,6 +181,7 @@@ redef class ModelBuilde
                                        mparameters.add(mparameter)
                                end
                                initializers.add(npropdef.mpropdef.mproperty)
 +                              npropdef.mpropdef.mproperty.is_autoinit = true
                        end
                        if npropdef isa AAttrPropdef then
                                if npropdef.mpropdef == null then return # Skip broken attribute
                                        # For autoinit attributes, call the reader to force
                                        # the lazy initialization of the attribute.
                                        initializers.add(npropdef.mreadpropdef.mproperty)
 +                                      npropdef.mreadpropdef.mproperty.is_autoinit = true
                                        continue
                                end
                                if npropdef.has_value then continue
                                if msetter == null then
                                        # No setter, it is a old-style attribute, so just add it
                                        initializers.add(npropdef.mpropdef.mproperty)
 +                                      npropdef.mpropdef.mproperty.is_autoinit = true
                                else
                                        # Add the setter to the list
                                        initializers.add(msetter.mproperty)
 +                                      msetter.mproperty.is_autoinit = true
                                end
                        end
                end
                        return
                end
  
 -              # Search the longest-one and checks for conflict
 -              var longest = spropdefs.first
 -              if spropdefs.length > 1 then
 -                      # Check for conflict in the order of initializers
 -                      # Each initializer list must me a prefix of the longest list
 -                      # part 1. find the longest list
 -                      for spd in spropdefs do
 -                              if spd.initializers.length > longest.initializers.length then longest = spd
 -                      end
 -                      # part 2. compare
 -                      for spd in spropdefs do
 -                              var i = 0
 -                              for p in spd.initializers do
 -                                      if p != longest.initializers[i] then
 -                                              self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
 -                                              return
 +              # Look at the autoinit class-annotation
 +              var autoinit = nclassdef.get_single_annotation("autoinit", self)
 +              var noautoinit = nclassdef.get_single_annotation("noautoinit", self)
 +              if autoinit != null then
 +                      # Just throws the collected initializers
 +                      mparameters.clear
 +                      initializers.clear
 +
 +                      if noautoinit != null then
 +                              error(autoinit, "Error: `autoinit` and `noautoinit` are incompatible.")
 +                      end
 +
 +                      if autoinit.n_args.is_empty then
 +                              error(autoinit, "Syntax error: `autoinit` expects method identifiers, use `noautoinit` to clear all autoinits.")
 +                      end
 +
 +                      # Get and check each argument
 +                      for narg in autoinit.n_args do
 +                              var id = narg.as_id
 +                              if id == null then
 +                                      error(narg, "Syntax error: `autoinit` expects method identifiers.")
 +                                      return
 +                              end
 +
 +                              # Search the property.
 +                              # To avoid bad surprises, try to get the setter first.
 +                              var p = try_get_mproperty_by_name(narg, mclassdef, id + "=")
 +                              if p == null then
 +                                      p = try_get_mproperty_by_name(narg, mclassdef, id)
 +                              end
 +                              if p == null then
 +                                      error(narg, "Error: unknown method `{id}`")
 +                                      return
 +                              end
 +                              if not p.is_autoinit then
 +                                      error(narg, "Error: `{p}` is not an autoinit method")
 +                                      return
 +                              end
 +
 +                              # Register the initializer and the parameters
 +                              initializers.add(p)
 +                              var pd = p.intro
 +                              if pd isa MMethodDef then
 +                                      # Get the signature resolved for the current receiver
 +                                      var sig = pd.msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mclassdef.mmodule, false)
 +                                      mparameters.add_all sig.mparameters
 +                              else
 +                                      # TODO attributes?
 +                                      abort
 +                              end
 +                      end
 +              else if noautoinit != null then
 +                      if initializers.is_empty then
 +                              warning(noautoinit, "useless-noautoinit", "Warning: the list of autoinit is already empty.")
 +                      end
 +                      # Just clear initializers
 +                      mparameters.clear
 +                      initializers.clear
 +              else
 +                      # Search the longest-one and checks for conflict
 +                      var longest = spropdefs.first
 +                      if spropdefs.length > 1 then
 +                              # Check for conflict in the order of initializers
 +                              # Each initializer list must me a prefix of the longest list
 +                              # part 1. find the longest list
 +                              for spd in spropdefs do
 +                                      if spd.initializers.length > longest.initializers.length then longest = spd
 +                              end
 +                              # part 2. compare
 +                              for spd in spropdefs do
 +                                      var i = 0
 +                                      for p in spd.initializers do
 +                                              if p != longest.initializers[i] then
 +                                                      self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
 +                                                      return
 +                                              end
 +                                              i += 1
                                        end
 -                                      i += 1
                                end
                        end
 -              end
  
 -              # Can we just inherit?
 -              if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then
 -                      self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3)
 -                      mclassdef.mclass.root_init = longest
 -                      return
 -              end
 +                      # Can we just inherit?
 +                      if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then
 +                              self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3)
 +                              mclassdef.mclass.root_init = longest
 +                              return
 +                      end
  
 -              # Combine the inherited list to what is collected
 -              if longest.initializers.length > 0 then
 -                      mparameters.prepend longest.new_msignature.mparameters
 -                      initializers.prepend longest.initializers
 +                      # Combine the inherited list to what is collected
 +                      if longest.initializers.length > 0 then
 +                              mparameters.prepend longest.new_msignature.mparameters
 +                              initializers.prepend longest.initializers
 +                      end
                end
  
                # If we already have a basic init definition, then setup its initializers
@@@ -469,9 -405,9 +469,9 @@@ redef class APropde
        # The associated propdef once build by a `ModelBuilder`
        var mpropdef: nullable MPROPDEF is writable
  
-       private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract
-       private fun build_signature(modelbuilder: ModelBuilder) is abstract
-       private fun check_signature(modelbuilder: ModelBuilder) is abstract
+       private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) do end
+       private fun build_signature(modelbuilder: ModelBuilder) do end
+       private fun check_signature(modelbuilder: ModelBuilder) do end
        private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility
        do
                var mvisibility = public_visibility
@@@ -992,11 -928,10 +992,11 @@@ redef class AAttrPropde
                has_value = n_expr != null or n_block != null
  
                var atnoinit = self.get_single_annotation("noinit", modelbuilder)
 +              if atnoinit == null then atnoinit = self.get_single_annotation("noautoinit", modelbuilder)
                if atnoinit != null then
                        noinit = true
                        if has_value then
 -                              modelbuilder.error(atnoinit, "Error: `noinit` attributes cannot have an initial value")
 +                              modelbuilder.error(atnoinit, "Error: `noautoinit` attributes cannot have an initial value")
                                return
                        end
                end
@@@ -17,7 -17,6 +17,7 @@@
  module parser_nodes
  
  import location
 +import ordered_tree
  
  # Root of the AST class-hierarchy
  abstract class ANode
                sys.stderr.write "{hot_location} {self.class_name}: {message}\n{hot_location.colored_line("0;32")}\n"
        end
  
 +      # Write the subtree on stdout.
 +      # See `ASTDump`
 +      fun dump_tree
 +      do
 +              var d = new ASTDump
 +              d.enter_visit(self)
 +              d.write_to(sys.stdout)
 +      end
 +
        # Parent of the node in the AST
        var parent: nullable ANode = null
  
@@@ -168,33 -158,6 +168,33 @@@ private class CollectAnnotationsByNameV
        end
  end
  
 +# A helper class to handle (print) Nit AST as an OrderedTree
 +class ASTDump
 +      super Visitor
 +      super OrderedTree[ANode]
 +
 +      # Reference to the last parent in the Ordered Tree
 +      # Is used to handle the initial node parent and workaround possible inconsistent `ANode::parent`
 +      private var last_parent: nullable ANode = null
 +
 +      redef fun visit(n)
 +      do
 +              var p = last_parent
 +              add(p, n)
 +              last_parent = n
 +              n.visit_all(self)
 +              last_parent = p
 +      end
 +
 +      redef fun display(n)
 +      do
 +              if n isa Token then
 +                      return "{n.class_name} \"{n.text.escape_to_c}\" @{n.location}"
 +              else
 +                      return "{n.class_name} @{n.location}"
 +              end
 +      end
 +end
  
  # A sequence of nodes
  # It is a specific class (instead of using a Array) to track the parent/child relation when nodes are added or removed
@@@ -352,11 -315,17 +352,17 @@@ abstract class Pro
        do
                var res = new Array[AAnnotation]
                var nas = n_annotations
-               if nas == null then return res
-               for na in nas.n_items do
+               if nas != null then for na in nas.n_items do
                        if na.name != name then continue
                        res.add(na)
                end
+               if self isa AClassdef then for na in n_propdefs do
+                       if na isa AAnnotPropdef then
+                               if na.name != name then continue
+                               res.add na
+                       end
+               end
                return res
        end
  
@@@ -1067,12 -1036,13 +1073,13 @@@ class AStdClassde
        # The extern block code
        var n_extern_code_block: nullable AExternCodeBlock = null is writable
  
        # The `end` keyword
        var n_kwend: TKwend is writable, noinit
  
+       fun n_superclasses: Array[ASuperPropdef] do
+               return [for d in n_propdefs do if d isa ASuperPropdef then d]
+       end
        redef fun hot_location do return n_id.location
  end
  
@@@ -1148,17 -1118,6 +1155,6 @@@ class AFormalde
        var n_type: nullable AType = null is writable
  end
  
- # A super-class. eg `super X`
- class ASuperclass
-       super Prod
-       # The super keyword
-       var n_kwsuper: TKwsuper is writable, noinit
-       # The super-class (indicated as a type)
-       var n_type: AType is writable, noinit
- end
  # The definition of a property
  abstract class APropdef
        super ADefinition
@@@ -1237,6 -1196,23 +1233,23 @@@ class AMainMethPropde
        super AMethPropdef
  end
  
+ class AAnnotPropdef
+       super APropdef
+       super AAnnotation
+ end
+ # A super-class. eg `super X`
+ class ASuperPropdef
+       super APropdef
+       # The super keyword
+       var n_kwsuper: TKwsuper is writable, noinit
+       # The super-class (indicated as a type)
+       var n_type: AType is writable, noinit
+ end
  # Declaration of callbacks for extern methods
  class AExternCalls
        super Prod
@@@ -22,7 -22,8 +22,8 @@@ Start ../src/test_parser.nit:17,1--147,
        AConcreteClasskind ../src/test_parser.nit:23,1--5
          TKwclass "class" ../src/test_parser.nit:23,1--5
        TClassid "PrintTreeVisitor" ../src/test_parser.nit:23,7--22
-       ASuperclass ../src/test_parser.nit:24,2--14
+       ASuperPropdef ../src/test_parser.nit:24,2--14
+         APublicVisibility ../src/test_parser.nit:24,2
          TKwsuper "super" ../src/test_parser.nit:24,2--6
          AType ../src/test_parser.nit:24,8--14
            TClassid "Visitor" ../src/test_parser.nit:24,8--14
                                  AListExprs ../src/test_parser.nit:116,44
                                TCpar ")" ../src/test_parser.nit:116,45
                        ABlockExpr ../src/test_parser.nit:118,4--121,5
 -                        AVardeclExpr ../src/test_parser.nit:118,4--31
 +                        AVardeclExpr ../src/test_parser.nit:118,4--33
                            TKwvar "var" ../src/test_parser.nit:118,4--6
                            TId "f" ../src/test_parser.nit:118,8
                            TAssign "=" ../src/test_parser.nit:118,10
 -                          ANewExpr ../src/test_parser.nit:118,12--31
 +                          ANewExpr ../src/test_parser.nit:118,12--33
                              TKwnew "new" ../src/test_parser.nit:118,12--14
 -                            AType ../src/test_parser.nit:118,16--23
 -                              TClassid "IFStream" ../src/test_parser.nit:118,16--23
 -                            TId "open" ../src/test_parser.nit:118,25--28
 -                            AParExprs ../src/test_parser.nit:118,29--31
 -                              TOpar "(" ../src/test_parser.nit:118,29
 -                              ACallExpr ../src/test_parser.nit:118,30
 -                                AImplicitSelfExpr ../src/test_parser.nit:118,30
 -                                TId "a" ../src/test_parser.nit:118,30
 -                                AListExprs ../src/test_parser.nit:118,30
 -                              TCpar ")" ../src/test_parser.nit:118,31
 +                            AType ../src/test_parser.nit:118,16--25
 +                              TClassid "FileReader" ../src/test_parser.nit:118,16--25
 +                            TId "open" ../src/test_parser.nit:118,27--30
 +                            AParExprs ../src/test_parser.nit:118,31--33
 +                              TOpar "(" ../src/test_parser.nit:118,31
 +                              ACallExpr ../src/test_parser.nit:118,32
 +                                AImplicitSelfExpr ../src/test_parser.nit:118,32
 +                                TId "a" ../src/test_parser.nit:118,32
 +                                AListExprs ../src/test_parser.nit:118,32
 +                              TCpar ")" ../src/test_parser.nit:118,33
                          ACallAssignExpr ../src/test_parser.nit:119,4--32
                            AImplicitSelfExpr ../src/test_parser.nit:119,4
                            TId "source" ../src/test_parser.nit:119,4--9