Rename REAMDE to README.md
[nit.git] / src / annotation.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Management and utilities on annotations
16 module annotation
17
18 import modelbuilder
19 import literal
20 import model::mmodule_data
21
22 redef class Prod
23 super ANode
24
25 # Try to get its single annotation with a given name
26 # If there is no such an annotation, null is returned.
27 # If there is more than one annotation, a error message is printed and the first annotation is returned
28 fun get_single_annotation(name: String, modelbuilder: ModelBuilder): nullable AAnnotation
29 do
30 var res = get_annotations(name)
31 if res.is_empty then return null
32 if res.length > 1 then
33 modelbuilder.error(res[1], "Syntax Error: multiple `{name}`. A previous one is defined line {res[0].location.line_start}.")
34 end
35 return res.first
36 end
37 end
38
39 redef class AAnnotation
40 # Get the single argument of `self` as a `String`.
41 # Raise error and return null on any inconsistency.
42 fun arg_as_string(modelbuilder: ModelBuilder): nullable String
43 do
44 var args = n_args
45 if args.length == 1 then
46 var arg = args.first.as_string
47 if arg != null then return arg
48 end
49
50 modelbuilder.error(self, "Syntax Error: `{name}` expects a single String as argument.")
51 return null
52 end
53
54 # Get the single argument of `self` as an `Int`.
55 # Raise error and return null on any inconsistency.
56 fun arg_as_int(modelbuilder: ModelBuilder): nullable Int
57 do
58 var args = n_args
59 if args.length == 1 then
60 var arg = args.first.as_int
61 if arg != null then return arg
62 end
63
64 modelbuilder.error(self, "Syntax Error: `{name}` expects a single Int as argument.")
65 return null
66 end
67
68 # Get the single argument of `self` as an identifier.
69 # Raise error and return null on any inconsistency.
70 fun arg_as_id(modelbuilder: ModelBuilder): nullable String
71 do
72 var args = n_args
73 if args.length == 1 then
74 var arg = args.first.as_id
75 if arg != null then return arg
76 end
77
78 modelbuilder.error(self, "Syntax Error: `{name}` expects a single identifier as argument.")
79 return null
80 end
81 end
82
83 redef class ModelBuilder
84 # Collect all annotations by `name` assocated to `mmodule` and its imported modules.
85 # Note that visibility is not considered.
86 fun collect_annotations_on_modules(name: String, mmodule: MModule): Array[AAnnotation]
87 do
88 var annotations = new Array[AAnnotation]
89 for mmod in mmodule.in_importation.greaters do
90 var amod = mmodule2node(mmod)
91 if amod == null then continue
92 var module_decl = amod.n_moduledecl
93 if module_decl == null then continue
94 var aas = module_decl.get_annotations(name)
95 annotations.add_all aas
96 end
97 return annotations
98 end
99
100 # Return the single annotation `name` locally assocated to `mmodule`, if any.
101 # Obviously, if there is no ast associated to `mmodule`, then nothing is returned.
102 fun get_mmodule_annotation(name: String, mmodule: MModule): nullable AAnnotation
103 do
104 var amod = mmodule2node(mmodule)
105 if amod == null then return null
106 var module_decl = amod.n_moduledecl
107 if module_decl == null then return null
108 var res = module_decl.get_single_annotation(name, self)
109 return res
110 end
111
112 private var collect_annotations_data_cache = new HashMap[String, MModuleData[AAnnotation]]
113
114 # Collect all annotations by `name` in `mmodule` and its importations (direct and indirect)
115 # Note that visibility is not considered.
116 fun collect_annotations_data(name: String, mmodule: MModule): MModuleData[AAnnotation]
117 do
118 var res = collect_annotations_data_cache.get_or_null(name)
119 if res == null then
120 res = new MModuleData[AAnnotation](model)
121 collect_annotations_data_cache[name] = res
122 end
123
124 for mmod in mmodule.in_importation.greaters do
125 if res.has_mmodule(mmod) then continue
126 var ass = get_mmodule_annotation(name, mmod)
127 if ass == null then continue
128 res[mmod] = ass
129 end
130 return res
131 end
132
133 # Get an annotation by name from `mmodule` and its super modules. Will recursively search
134 # in imported module to find the "latest" declaration and detects priority conflicts.
135 fun lookup_annotation_on_modules(name: String, mmodule: MModule): nullable AAnnotation
136 do
137 var data = collect_annotations_data(name, mmodule)
138 var annotations = data.lookup_values(mmodule, none_visibility)
139 if annotations.is_empty then return null
140 if annotations.length > 1 then
141 var locs = new Array[Location]
142 for annot in annotations do locs.add(annot.location)
143
144 toolcontext.error(mmodule.location,
145 "Error: priority conflict on annotation `{name}`, it has been defined in: {locs.join(", ")}.")
146 end
147 return annotations.first
148 end
149 end