X-Git-Url: http://nitlanguage.org diff --git a/src/annotation.nit b/src/annotation.nit index 4daafef..73c9985 100644 --- a/src/annotation.nit +++ b/src/annotation.nit @@ -15,17 +15,28 @@ # Management and utilities on annotations module annotation -import parser import modelbuilder import literal +import model::mmodule_data -redef class AAnnotation - # The name of the annotation - fun name: String +redef class Prod + super ANode + + # Try to get its single annotation with a given name + # If there is no such an annotation, null is returned. + # If there is more than one annotation, a error message is printed and the first annotation is returned + fun get_single_annotation(name: String, modelbuilder: ModelBuilder): nullable AAnnotation do - return n_atid.n_id.text + var res = get_annotations(name) + if res.is_empty then return null + if res.length > 1 then + modelbuilder.error(res[1], "Syntax Error: multiple `{name}`. A previous one is defined line {res[0].location.line_start}.") + end + return res.first end +end +redef class AAnnotation # 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 @@ -36,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 @@ -50,41 +61,89 @@ 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 + + # Get the single argument of `self` as an identifier. + # Raise error and return null on any inconsistency. + fun arg_as_id(modelbuilder: ModelBuilder): nullable String + do + var args = n_args + if args.length == 1 then + var arg = args.first.as_id + if arg != null then return arg + end + + 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