Merge: astbuilder: First implementation of clonable for ast nodes
authorJean Privat <jean@pryen.org>
Mon, 16 Sep 2019 14:34:36 +0000 (10:34 -0400)
committerJean Privat <jean@pryen.org>
Mon, 16 Sep 2019 14:34:36 +0000 (10:34 -0400)
## Astbuilder

- Now all the nodes of the ast is cloneable. Currently just a part of node have an implemented clone method.If we clone a node by default (not implemented) the program abort and print the following message: `The clone method is not implemented for ATestNode class.`. Currently the library is in **test** phase. You can have "maybe" some bug with the clone.

- Add a method to make the creation of a callsite easier.

## Futur work

Currently i am working on a test to make sure the result clone is the same as the original.

Pull-Request: #2779
Reviewed-by: Jean Privat <jean@pryen.org>

src/astbuilder.nit
src/test_astbuilder.nit [new file with mode: 0644]
tests/nitcg.skip
tests/niti.skip
tests/nitvm.skip
tests/sav/test_astbuilder.res [new file with mode: 0644]
tests/sav/test_astbuilder_args1.res [new file with mode: 0644]
tests/test_astbuilder.args [new file with mode: 0644]
tests/test_astbuilder/test_astbuilder.nit [new file with mode: 0644]

index 240e00b..681c4bf 100644 (file)
@@ -19,6 +19,7 @@ intrude import semantize::typing
 intrude import literal
 intrude import parser
 intrude import semantize::scope
+intrude import modelbuilder_base
 
 # General factory to build semantic nodes in the AST of expressions
 class ASTBuilder
@@ -116,14 +117,14 @@ class ASTBuilder
        end
 
        # Make a new method
-       fun make_method(n_visibility: nullable APublicVisibility,
+       fun make_method(n_visibility: nullable AVisibility,
                                        tk_redef: nullable TKwredef,
-                                       mmethoddef: MMethodDef,
+                                       mmethoddef: nullable MMethodDef,
                                        n_signature: nullable ASignature,
                                        n_annotations: nullable AAnnotations,
                                        n_extern_calls: nullable AExternCalls,
                                        n_extern_code_block: nullable AExternCodeBlock,
-                                       n_block: AExpr): AMethPropdef
+                                       n_block: nullable AExpr): AMethPropdef
        do
                return new AMethPropdef.make(n_visibility, tk_redef, mmethoddef, n_signature, n_annotations, n_extern_calls, n_extern_code_block, n_block)
        end
@@ -157,6 +158,23 @@ class ASTBuilder
        do
                return new AReturnExpr.make(expr)
        end
+
+       # Make a new not
+       fun make_not(expr: AExpr): ANotExpr
+       do
+               return new ANotExpr.make(expr)
+       end
+
+       # Build a callsite to call the `mproperty` in the current method `caller_method`.
+       # `is_self_call` indicate if the method caller is a property of `self`
+       fun create_callsite(modelbuilder: ModelBuilder, caller_method : AMethPropdef, mproperty: MMethod, is_self_call: Bool): CallSite
+       do
+               # FIXME It's not the better solution to call `TypeVisitor` here to build a model entity, but some make need to have a callsite
+               var type_visitor = new TypeVisitor(modelbuilder, caller_method.mpropdef.as(not null))
+               var callsite = type_visitor.build_callsite_by_property(caller_method, mproperty.intro_mclassdef.bound_mtype, mproperty, is_self_call)
+               assert callsite != null
+               return callsite
+       end
 end
 
 redef class AExpr
@@ -244,6 +262,13 @@ class APlaceholderExpr
        end
 end
 
+redef class ANotExpr
+       private init make(expr: AExpr)
+       do
+               self.init_anotexpr(new TKwnot, expr)
+       end
+end
+
 redef class AReturnExpr
        private init make(expr: nullable AExpr)
        do
@@ -252,24 +277,17 @@ redef class AReturnExpr
 end
 
 redef class ASuperExpr
-       private init make(args: nullable Array[AExpr], n_qualified: nullable AQualified)
+       private init make(args: nullable Array[AExpr], n_qualified: nullable AQualified, mpropdef: nullable MMethodDef)
        do
                var n_args = new AListExprs
                if args != null then
                        n_args.n_exprs.add_all(args)
                end
+               _mpropdef = mpropdef
                self.init_asuperexpr(n_qualified, new TKwsuper, n_args)
        end
 end
 
-redef class AParExpr
-       private init make(expr: AExpr, annotations: nullable AAnnotations)
-       do
-               self.location = expr.location
-               self.init_aparexpr(new TOpar, expr, new TCpar, annotations)
-       end
-end
-
 redef class AOrExpr
        private init make(right_expr: AExpr, left_expr: AExpr)
        do
@@ -285,9 +303,9 @@ redef class AAndExpr
 end
 
 redef class AMethPropdef
-       private init make(n_visibility: nullable APublicVisibility,
+       private init make(n_visibility: nullable AVisibility,
                                        tk_redef: nullable TKwredef,
-                                       mmethoddef: MMethodDef,
+                                       mmethoddef: nullable MMethodDef,
                                        n_signature: nullable ASignature,
                                        n_annotations: nullable AAnnotations,
                                        n_extern_calls: nullable AExternCalls,
@@ -295,7 +313,6 @@ redef class AMethPropdef
                                        n_block: nullable AExpr)
        do
                var n_tid = new TId
-               n_tid.text = mmethoddef.name
                var n_methid = new AIdMethid.init_aidmethid(n_tid)
                if n_signature == null then n_signature = new ASignature
                if n_visibility == null then n_visibility = new APublicVisibility
@@ -314,18 +331,6 @@ redef class AAssertExpr
        end
 end
 
-redef class ABlockExpr
-       private init make
-       do
-               self.is_typed = true
-       end
-
-       redef fun add(expr)
-       do
-               n_expr.add expr
-       end
-end
-
 redef class ALoopExpr
        private init make
        do
@@ -388,26 +393,108 @@ redef class AIfExpr
                _n_kwelse = new TKwelse
                _n_else = new ABlockExpr.make
                self.mtype = mtype
-               self.is_typed = true
+               if mtype != null then self.is_typed = true
        end
 end
 
 redef class AType
-       private init make
+
+       private init make(t: nullable MType)
        do
                var n_id = new TClassid
                var n_qid = new AQclassid
                n_qid.n_id = n_id
                _n_qid = n_qid
+               _mtype = t
+       end
+
+       redef fun clone: SELF
+       do
+               return new AType.make(mtype)
        end
 end
 
+# Primitive type
+
 redef class AIntegerExpr
-       private init make(value: Int, t: MType)
+
+       private init make(value: nullable Numeric, t: nullable MType)
        do
-               self.value = value
-               self._n_integer = new TInteger # dummy
-               self.mtype = t
+               _mtype = t
+               if t != null then self.is_typed = true
+               _value = value
+               _n_integer = new TInteger # dummy
+       end
+
+       redef fun clone: SELF
+       do
+               return new AIntegerExpr.make(value, mtype)
+       end
+end
+
+redef class AFloatExpr
+
+       private init make(value: nullable Float, t: nullable MType)
+       do
+               _mtype = t
+               if t != null then self.is_typed = true
+               _value = value
+               _n_float = new TFloat # dummy
+       end
+
+       redef fun clone: SELF
+       do
+               return new AFloatExpr.make(value, mtype)
+       end
+end
+
+redef class ATrueExpr
+
+       private init make(t: nullable MType)
+       do
+               init_atrueexpr(new TKwtrue, null)
+               _mtype = t
+               if t != null then self.is_typed = true
+       end
+
+       redef fun clone: SELF
+       do
+               return new ATrueExpr.make(mtype)
+       end
+end
+
+redef class AFalseExpr
+
+       private init make(t: nullable MType)
+       do
+               init_afalseexpr(new TKwfalse, null)
+               _mtype = t
+               if t != null then self.is_typed = true
+       end
+
+       redef fun clone: SELF
+       do
+               return new AFalseExpr.make(mtype)
+       end
+end
+
+redef class ACharExpr
+
+       # `token_text` represent the real value as it's present in a file not only the char.
+       # `token_text` is needed if you want to use some methods (for exemple: `prefix`, `suffix` or `is_code_point methods`)
+       private init make(value: nullable Char, t: nullable MType, token_text: nullable String)
+       do
+               _value = value
+               _mtype = t
+               _n_char = new TChar
+               if token_text != null then n_char.text = token_text
+               if t != null then self.is_typed = true
+       end
+
+       redef fun clone: SELF
+       do
+               var self_clone = new ACharExpr.make(self.value, mtype, n_char.text)
+               return self_clone
        end
 end
 
@@ -432,7 +519,7 @@ redef class ANewExpr
 end
 
 redef class ACallExpr
-       private init make(recv: AExpr, callsite: CallSite, args: nullable Array[AExpr])
+       private init make(recv: AExpr, callsite: nullable CallSite, args: nullable Array[AExpr])
        do
                self._n_expr = recv
                _n_args = new AListExprs
@@ -442,25 +529,81 @@ redef class ACallExpr
                if args != null then
                        self.n_args.n_exprs.add_all(args)
                end
-               self.callsite = callsite
-               self.mtype = callsite.msignature.return_mtype
-               self.is_typed = true
+
+               if callsite != null then
+                       self.callsite = callsite
+                       self.mtype = callsite.msignature.return_mtype
+                       self.is_typed = true
+               end
+       end
+end
+
+redef class AAsCastExpr
+       private init make(n_expr: AExpr, n_type: AType)
+       do
+               init_aascastexpr(n_expr, new TKwas , null , n_type, null)
+       end
+end
+
+redef class AAsNotnullExpr
+       private init make(n_expr: AExpr, t: nullable MType)
+       do
+               init_aasnotnullexpr(n_expr, new TKwas, null, new TKwnot, new TKwnull, null)
+               _mtype = t
+               if t != null then _is_typed = true
+       end
+end
+
+redef class ANullExpr
+
+       private init make(t: nullable MType)
+       do
+               init_anullexpr(new TKwnull, null)
+               _mtype = t
+               if t != null then self.is_typed = true
+       end
+end
+
+redef class ASelfExpr
+
+       private init make(v: nullable Variable, t: nullable MType)
+       do
+               init_aselfexpr(new TKwself, null)
+               _mtype = t
+               if t != null then is_typed =true
+       end
+
+       redef fun clone: SELF
+       do
+               return new ASelfExpr.make(self.variable, self.mtype)
        end
 end
 
+redef class AImplicitSelfExpr
+
+       redef fun clone: SELF
+       do
+               var self_clone = new AImplicitSelfExpr.make(variable, mtype)
+               self_clone.is_sys = is_sys
+               return self_clone
+       end
+end
+
+
 redef class AAttrExpr
-       private init make(recv: AExpr, attribute: MAttribute, t: MType)
+       private init make(recv: AExpr, attribute: nullable MAttribute, t: nullable MType)
        do
                _n_expr = recv
                recv.parent = self
                _n_id = new TAttrid
-               mproperty = attribute
-               mtype = t
+               _mproperty = attribute
+               _mtype = t
+               if t != null then _is_typed = true
        end
 end
 
 redef class AAttrAssignExpr
-       private init make(recv: AExpr, attribute: MAttribute, value: AExpr)
+       private init make(recv: AExpr, attribute: nullable MAttribute, value: AExpr)
        do
                _n_expr = recv
                recv.parent = self
@@ -468,29 +611,128 @@ redef class AAttrAssignExpr
                _n_value = value
                value.parent = self
                _n_assign = new TAssign
-               mproperty = attribute
-               mtype = value.mtype
+               _mproperty = attribute
+               _mtype = value.mtype
        end
 end
 
 redef class AVarExpr
-       private init make(v: Variable, mtype: MType)
+       private init make(v: nullable Variable, t: nullable MType)
        do
                _n_id = new TId
-               variable = v
-               self.mtype = mtype
+               if v != null then _n_id.text = v.name
+               _variable = v
+               _mtype = t
+               if t != null then is_typed = true
        end
 end
 
 redef class AVarAssignExpr
-       private init make(v: Variable, value: AExpr)
+       private init make(v: nullable Variable, value: AExpr)
        do
                _n_id = new TId
+               if v != null then _n_id.text = v.name
                _n_value = value
                value.parent = self
                _n_assign = new TAssign
-               variable = v
-               mtype = value.mtype
+               _variable = v
+               _mtype = value.mtype
+               if _mtype != null then _is_typed = true
+       end
+end
+
+redef class ASignature
+       redef fun clone: SELF
+       do
+               var ntype = n_type
+               if ntype != null then ntype = n_type.clone
+               return new ASignature.init_asignature(null, n_params.clone, null, ntype)
+       end
+end
+
+redef class AParam
+
+       private init make(v: nullable Variable, t: nullable AType)
+       do
+               _n_id = new TId
+               _variable = v
+               _n_type = t
+       end
+
+       redef fun clone: SELF
+       do
+               var ntype = n_type
+               if ntype != null then ntype = n_type.clone
+               return new AParam.make(variable, ntype)
+       end
+end
+
+redef class ABlockExpr
+       private init make(t: nullable MType)
+       do
+               if t != null then
+                       _mtype = t
+                       _is_typed = true
+               end
+       end
+
+       redef fun add(expr)
+       do
+               n_expr.add expr
+               expr.parent = self
+       end
+
+       fun add_all(exprs: Array[AExpr])
+       do
+               for expr in exprs do
+                       add(expr)
+               end
+       end
+
+       redef fun clone: SELF
+       do
+               var clone = new ABlockExpr.make(mtype)
+               for expr in self.n_expr do
+                       clone.add(expr.clone)
+               end
+               return clone
+       end
+end
+
+redef class AQclassid
+       redef fun clone: SELF
+       do
+               return new AQclassid.init_aqclassid(n_qualified.clone, n_id)
+       end
+end
+
+redef class AQualified
+       redef fun clone: SELF
+       do
+               return new AQualified.init_aqualified(n_id.clone, n_classid)
+       end
+end
+
+redef class AQid
+       redef fun clone: SELF
+       do
+               var clone_n_qualified = n_qualified
+               if n_qualified != null then clone_n_qualified = n_qualified.clone
+               return new AQid.init_aqid(clone_n_qualified, n_id.clone)
+       end
+end
+
+redef class TId
+       redef fun clone: SELF
+       do
+               return new TId.init_tk(location)
+       end
+end
+
+redef class AParExpr
+       private init make(expr: AExpr, annotations: nullable AAnnotations)
+       do
+               self.init_aparexpr(new TOpar, expr, new TCpar, annotations)
        end
 end
 
@@ -505,7 +747,28 @@ class ASTValidationVisitor
        private var seen = new HashSet[ANode]
 end
 
+redef class ANodes
+       super Cloneable
+
+       redef fun clone: SELF
+       do
+               var clone_anodes = new ANodes[E](self.parent)
+               for node in self do
+                       clone_anodes.add(node.clone)
+               end
+               return clone_anodes
+       end
+end
+
 redef class ANode
+       super Cloneable
+
+       redef fun clone: SELF
+       do
+               # By default the clone abort to avoid surprises
+               print "The clone method is not implemented for the `{self.class_name}` class"
+               abort
+       end
        # Recursively validate a AST node.
        # This ensure that location and parenting are defined and coherent.
        #
@@ -552,8 +815,15 @@ redef class ANode
 end
 
 redef class AAnnotation
+
        redef fun accept_ast_validation(v)
        do
                # Do not enter in annotations
        end
+
+       private init make(n_args : ANodes[AExpr])
+       do
+               _n_visibility = new APublicVisibility
+               _n_args = n_args
+       end
 end
diff --git a/src/test_astbuilder.nit b/src/test_astbuilder.nit
new file mode 100644 (file)
index 0000000..e2f4b58
--- /dev/null
@@ -0,0 +1,118 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Program used to test the `clone` method of the astbuilder tool
+# Program use the nit interpreter to do his job. It take a nit file into parameter and replace the ast nodes by a clone.
+# After replace it run a basic interpretation.
+# When a new method `clone` is implemented you need to add a redefinition to test it.
+# When the number of method `clone` will increase its more practical to define the nodes to not clone.
+module test_astbuilder
+
+import nit
+import astbuilder
+
+redef class ModelBuilder
+       redef fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String])
+       do
+               var clone_visitor = new CloneVisitor
+               for nmodule in self.nmodules do
+                       clone_visitor.enter_visit(nmodule)
+               end
+               super
+       end
+end
+
+private class CloneVisitor
+       super Visitor
+
+       redef fun visit(node)
+       do
+               # return when the node is an annotation to avoid the clonage
+               if node isa AAnnotations then return
+               node.do_cloneable(self)
+               node.visit_all(self)
+       end
+end
+
+redef class ANode
+       private fun do_cloneable(v: CloneVisitor)do end
+
+       # Create a new clone of `self`
+       fun replace_clone
+       do
+               var self_clone = self.clone
+               replace_with(self.clone)
+               self_clone.location = location
+               #Call the `validate` method to set correctly the parents and the location
+               self_clone.validate
+       end
+end
+
+# Implement the `do_cloeable` method for each Node were the method `clone` is defined in the astbuilder module
+
+redef class ASignature
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class AImplicitSelfExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class ASelfExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class AIntegerExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class ATrueExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class AFloatExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class AFalseExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
+
+redef class ACharExpr
+       redef fun do_cloneable(v)
+       do
+               replace_clone
+       end
+end
index b836f65..16a81df 100644 (file)
@@ -9,3 +9,4 @@ test_model_index
 test_loader
 get_mclasses
 ^nit
+test_astbuilder
index 4beb4fb..6e60c30 100644 (file)
@@ -46,3 +46,4 @@ test_csv
 repeating_key_xor_solve
 nitpm
 nitdoc
+test_astbuilder
index 097ca87..61f68f8 100644 (file)
@@ -49,3 +49,4 @@ base_notnull_lit_alt2
 assertions
 nitpm
 nitdoc
+test_astbuilder
diff --git a/tests/sav/test_astbuilder.res b/tests/sav/test_astbuilder.res
new file mode 100644 (file)
index 0000000..6dae4e5
--- /dev/null
@@ -0,0 +1,3 @@
+Usage: nit [OPTION]... <file.nit>...
+Interprets and debugs Nit programs.
+Use --help for help
diff --git a/tests/sav/test_astbuilder_args1.res b/tests/sav/test_astbuilder_args1.res
new file mode 100644 (file)
index 0000000..a29ab05
--- /dev/null
@@ -0,0 +1,3 @@
+Test print
+Test astbuilder
+c
diff --git a/tests/test_astbuilder.args b/tests/test_astbuilder.args
new file mode 100644 (file)
index 0000000..bebc463
--- /dev/null
@@ -0,0 +1 @@
+test_astbuilder/test_astbuilder.nit
diff --git a/tests/test_astbuilder/test_astbuilder.nit b/tests/test_astbuilder/test_astbuilder.nit
new file mode 100644 (file)
index 0000000..337ad91
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The goal of this code is to check the astbuilder features
+
+class A
+
+       fun titi
+       do
+               print "Test print"
+       end
+
+       fun toto(int: Int, float: Float, bool: Bool, char: Char): Int
+       do
+               var test_int = int
+               var test_float = float
+               var test_bool = bool
+               var test_char = char
+               self.titi
+               return 1
+       end
+end
+
+var a = new A
+a.toto(10, 2.0, true, 'c')
+print "Test astbuilder"
+print 'c'