nitunit: the working dir is now `nitunit.out`
[nit.git] / src / frontend / serialization_phase.nit
index a12a51f..8b3de28 100644 (file)
@@ -24,8 +24,13 @@ import modelize
 private import annotation
 
 redef class ToolContext
+
+       # Apply the annotation `serialize_as`
+       var serialization_phase_rename: Phase = new SerializationPhaseRename(self, null)
+
        # Generate serialization and deserialization methods on `auto_serializable` annotated classes.
-       var serialization_phase_pre_model: Phase = new SerializationPhasePreModel(self, null)
+       var serialization_phase_pre_model: Phase = new SerializationPhasePreModel(self,
+               [serialization_phase_rename])
 
        # The second phase of the serialization
        var serialization_phase_post_model: Phase = new SerializationPhasePostModel(self,
@@ -56,7 +61,34 @@ redef class ADefinition
        end
 end
 
-# TODO add annotations on attributes (volatile, sensitive or do_not_serialize?)
+private class SerializationPhaseRename
+       super Phase
+
+       redef fun process_annotated_node(node, nat)
+       do
+               var text = nat.n_atid.n_id.text
+               if text != "serialize_as" then return
+
+               if not node isa AAttrPropdef then
+                       toolcontext.error(node.location,
+                               "Syntax Error: annotation `{text}` applies only to attributes.")
+                       return
+               end
+
+               # Can't use `arg_as_string` because it needs the literal phase
+               var args = nat.n_args
+               if args.length != 1 or not args.first isa AStringFormExpr then
+                       toolcontext.error(node.location,
+                               "Syntax Error: annotation `{text}` expects a single string literal as argument.")
+                       return
+               end
+
+               var t = args.first.collect_text
+               var val = t.substring(1, t.length-2)
+               node.serialize_name = val
+       end
+end
+
 private class SerializationPhasePreModel
        super Phase
 
@@ -140,7 +172,7 @@ private class SerializationPhasePreModel
                # collect all classes
                var auto_serializable_nclassdefs = new Array[AStdClassdef]
                for nclassdef in nmodule.n_classdefs do
-                       if nclassdef isa AStdClassdef and nclassdef.is_serialize then
+                       if nclassdef isa AStdClassdef and nclassdef.how_serialize != null then
                                auto_serializable_nclassdefs.add nclassdef
                        end
                end
@@ -165,8 +197,7 @@ private class SerializationPhasePreModel
                        if (per_attribute and not attribute.is_serialize) or
                                attribute.is_noserialize then continue
 
-                       var name = attribute.name
-                       code.add "      v.serialize_attribute(\"{name}\", {name})"
+                       code.add "      v.serialize_attribute(\"{attribute.serialize_name}\", {attribute.name})"
                end
 
                code.add "end"
@@ -180,6 +211,16 @@ private class SerializationPhasePreModel
        do
                var npropdefs = nclassdef.n_propdefs
 
+               # Do not insert a `from_deserializer` if it already exists
+               for npropdef in npropdefs do
+                       if npropdef isa AMethPropdef then
+                               var methid = npropdef.n_methid
+                               if methid != null and methid.collect_text == "from_deserializer" then
+                                       return
+                               end
+                       end
+               end
+
                var code = new Array[String]
                code.add """
 redef init from_deserializer(v: Deserializer)
@@ -205,11 +246,11 @@ do
                        var name = attribute.name
 
                        code.add """
-       var {{{name}}} = v.deserialize_attribute("{{{name}}}")
+       var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}")
        if not {{{name}}} isa {{{type_name}}} then
                # Check if it was a subjectent error
                v.errors.add new AttributeTypeError("TODO remove this arg on c_src regen",
-                       self, "{{{name}}}", {{{name}}}, "{{{type_name}}}")
+                       self, "{{{attribute.serialize_name}}}", {{{name}}}, "{{{type_name}}}")
 
                # Clear subjacent error
                if v.keep_going == false then return
@@ -251,7 +292,7 @@ do
                end
 
                for nclassdef in nclassdefs do
-                       var name = nclassdef.n_id.text
+                       var name = nclassdef.n_qid.n_id.text
                        if nclassdef.n_formaldefs.is_empty and
                           nclassdef.n_classkind isa AConcreteClasskind then
 
@@ -313,16 +354,16 @@ redef class AIsaExpr
 end
 
 redef class AAttrPropdef
-       private fun name: String
-       do
-               return n_id2.text
-       end
+       private fun name: String do return n_id2.text
+
+       # Name of this attribute in the serialized format
+       private var serialize_name: String = name is lazy
 end
 
 redef class AType
        private fun type_name: String
        do
-               var name = n_id.text
+               var name = n_qid.n_id.text
 
                if n_kwnullable != null then name = "nullable {name}"
 
@@ -339,7 +380,7 @@ redef class AModule
        private fun deserializer_nclassdef: nullable AStdClassdef
        do
                for nclassdef in n_classdefs do
-                       if nclassdef isa AStdClassdef and nclassdef.n_id.text == "Deserializer" then
+                       if nclassdef isa AStdClassdef and nclassdef.n_qid.n_id.text == "Deserializer" then
                                return nclassdef
                        end
                end