X-Git-Url: http://nitlanguage.org diff --git a/src/annotation.nit b/src/annotation.nit index a0d397e..73c9985 100644 --- a/src/annotation.nit +++ b/src/annotation.nit @@ -15,9 +15,9 @@ # Management and utilities on annotations module annotation -import parser import modelbuilder import literal +import model::mmodule_data redef class Prod super ANode @@ -30,33 +30,13 @@ redef class Prod var res = get_annotations(name) if res.is_empty then return null if res.length > 1 then - modelbuilder.error(res[1], "Error: multiple annotation `{name}`. A previous one is defined line {res[0].location.line_start}") + modelbuilder.error(res[1], "Syntax Error: multiple `{name}`. A previous one is defined line {res[0].location.line_start}.") end return res.first end - - # Return all its annotations of a given name in the order of their declaration - # Retun an empty array if no such an annotation. - fun get_annotations(name: String): Array[AAnnotation] - do - var res = new Array[AAnnotation] - var nas = n_annotations - if nas == null then return res - for na in nas.n_items do - if na.name != name then continue - res.add(na) - end - return res - end end redef class AAnnotation - # The name of the annotation - fun name: String - do - return n_atid.n_id.text - end - # Get the single argument of `self` as a `String`. # Raise error and return null on any inconsistency. fun arg_as_string(modelbuilder: ModelBuilder): nullable String @@ -67,7 +47,7 @@ redef class AAnnotation if arg != null then return arg end - modelbuilder.error(self, "Annotation error: \"{name}\" expects a single String as argument.") + modelbuilder.error(self, "Syntax Error: `{name}` expects a single String as argument.") return null end @@ -81,7 +61,7 @@ redef class AAnnotation if arg != null then return arg end - modelbuilder.error(self, "Annotation error: \"{name}\" expects a single Int as argument.") + modelbuilder.error(self, "Syntax Error: `{name}` expects a single Int as argument.") return null end @@ -95,41 +75,75 @@ redef class AAnnotation if arg != null then return arg end - modelbuilder.error(self, "Annotation error: \"{name}\" expects a single identifier as argument.") + modelbuilder.error(self, "Syntax Error: `{name}` expects a single identifier as argument.") return null end end -redef class AAtArg - # Get `self` as a `String`. - # Return null if not a string. - fun as_string: nullable String +redef class ModelBuilder + # Collect all annotations by `name` assocated to `mmodule` and its imported modules. + # Note that visibility is not considered. + fun collect_annotations_on_modules(name: String, mmodule: MModule): Array[AAnnotation] + do + var annotations = new Array[AAnnotation] + for mmod in mmodule.in_importation.greaters do + var amod = mmodule2node(mmod) + if amod == null then continue + var module_decl = amod.n_moduledecl + if module_decl == null then continue + var aas = module_decl.get_annotations(name) + annotations.add_all aas + end + return annotations + end + + # Return the single annotation `name` locally assocated to `mmodule`, if any. + # Obviously, if there is no ast associated to `mmodule`, then nothing is returned. + fun get_mmodule_annotation(name: String, mmodule: MModule): nullable AAnnotation do - if not self isa AExprAtArg then return null - var nexpr = n_expr - if not nexpr isa AStringFormExpr then return null - return nexpr.value.as(not null) + var amod = mmodule2node(mmodule) + if amod == null then return null + var module_decl = amod.n_moduledecl + if module_decl == null then return null + var res = module_decl.get_single_annotation(name, self) + return res end - # Get `self` as an `Int`. - # Return null if not an integer. - fun as_int: nullable Int + private var collect_annotations_data_cache = new HashMap[String, MModuleData[AAnnotation]] + + # Collect all annotations by `name` in `mmodule` and its importations (direct and indirect) + # Note that visibility is not considered. + fun collect_annotations_data(name: String, mmodule: MModule): MModuleData[AAnnotation] do - if not self isa AExprAtArg then return null - var nexpr = n_expr - if not nexpr isa AIntExpr then return null - return nexpr.value.as(not null) + var res = collect_annotations_data_cache.get_or_null(name) + if res == null then + res = new MModuleData[AAnnotation](model) + collect_annotations_data_cache[name] = res + end + + for mmod in mmodule.in_importation.greaters do + if res.has_mmodule(mmod) then continue + var ass = get_mmodule_annotation(name, mmod) + if ass == null then continue + res[mmod] = ass + end + return res end - # Get `self` as a single identifier. - # Return null if not a single identifier. - fun as_id: nullable String + # Get an annotation by name from `mmodule` and its super modules. Will recursively search + # in imported module to find the "latest" declaration and detects priority conflicts. + fun lookup_annotation_on_modules(name: String, mmodule: MModule): nullable AAnnotation do - if not self isa AExprAtArg then return null - var nexpr = n_expr - if not nexpr isa ACallExpr then return null - if not nexpr.n_expr isa AImplicitSelfExpr then return null - if not nexpr.n_args.n_exprs.is_empty then return null - return nexpr.n_id.text + var data = collect_annotations_data(name, mmodule) + var annotations = data.lookup_values(mmodule, none_visibility) + if annotations.is_empty then return null + if annotations.length > 1 then + var locs = new Array[Location] + for annot in annotations do locs.add(annot.location) + + toolcontext.error(mmodule.location, + "Error: priority conflict on annotation `{name}`, it has been defined in: {locs.join(", ")}.") + end + return annotations.first end end