X-Git-Url: http://nitlanguage.org diff --git a/src/mmloader.nit b/src/mmloader.nit index 4b0b6e5..8f7c2ac 100644 --- a/src/mmloader.nit +++ b/src/mmloader.nit @@ -2,6 +2,7 @@ # # Copyright 2006-2008 Floréal Morandat # Copyright 2008 Jean Privat +# Copyright 2009 Jean-Sebastien Gelinas # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,77 +16,162 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This package is used to load a metamodel +# This module is used to load a metamodel package mmloader import metamodel import opts +import location + +class Message + super Comparable + redef type OTHER: Message + + readable var _location: nullable Location + readable var _text: String + + redef fun <(other: OTHER): Bool do + if location == null then return true + if other.location == null then return false + + return location.as(not null) < other.location.as(not null) + end + + redef fun to_s: String do + var l = location + if l == null then + return text + else + return "{l}: {text}" + end + end +end # Global context for tools class ToolContext -special MMContext + super MMContext # Number of errors - readable attr _error_count: Int + readable var _error_count: Int = 0 # Number of warnings - readable attr _warning_count: Int + readable var _warning_count: Int = 0 + + # Directory where to generate log files + readable var _log_directory: String = "logs" + + # Messages + var _messages: Array[Message] = new Array[Message] + var _message_sorter: ComparableSorter[Message] = new ComparableSorter[Message] + + fun check_errors + do + if _messages.length > 0 then + _message_sorter.sort(_messages) + + for m in _messages do + stderr.write("{m}\n") + end + + _messages.clear + end + + if error_count > 0 then exit(1) + end # Display an error - meth error(s: String) + fun error(l: nullable Location, s: String) do - stderr.write("{s}\n") + _messages.add(new Message(l,s)) _error_count = _error_count + 1 + if opt_stop_on_first_error.value then check_errors + end + + # Add an error, show errors and quit + fun fatal_error(l: nullable Location, s: String) + do + error(l,s) + check_errors end # Display a warning - meth warning(s: String) + fun warning(l: nullable Location, s: String) do - if not _opt_warn.value then return - stderr.write("{s}\n") - _warning_count = _warning_count + 1 + if _opt_warn.value == 0 then return + _messages.add(new Message(l,s)) + if _opt_warn.value == 1 then + _warning_count = _warning_count + 1 + else + _error_count = _error_count + 1 + end + if opt_stop_on_first_error.value then check_errors + end + + # Display an info + fun info(s: String, level: Int) + do + if level <= verbose_level then + print "{s}" + end end # Paths where to locate modules files - readable attr _paths: Array[String] + readable var _paths: Array[String] = new Array[String] # List of module loaders - attr _loaders: Array[ModuleLoader] = new Array[ModuleLoader] + var _loaders: Array[ModuleLoader] = new Array[ModuleLoader] # Global OptionContext - readable attr _option_context: OptionContext = new OptionContext + readable var _option_context: OptionContext = new OptionContext # Option --warn - readable attr _opt_warn: OptionBool = new OptionBool("Show warnings", "-W", "--warn") + readable var _opt_warn: OptionCount = new OptionCount("Show warnings", "-W", "--warn") # Option --path - readable attr _opt_path: OptionArray = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path") + readable var _opt_path: OptionArray = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path") + + # Option --log + readable var _opt_log: OptionBool = new OptionBool("Generate various log files", "--log") + + # Option --log-dir + readable var _opt_log_dir: OptionString = new OptionString("Directory where to generate log files", "--log-dir") - # Option --lop - readable attr _opt_log: OptionBool = new OptionBool("Generate various log files", "--log") - # Option --only-metamodel - readable attr _opt_only_metamodel: OptionBool = new OptionBool("Stop after meta-model processing", "--only-metamodel") + readable var _opt_only_metamodel: OptionBool = new OptionBool("Stop after meta-model processing", "--only-metamodel") # Option --only-parse - readable attr _opt_only_parse: OptionBool = new OptionBool("Only proceed to parse step of loaders", "--only-parse") + readable var _opt_only_parse: OptionBool = new OptionBool("Only proceed to parse step of loaders", "--only-parse") # Option --help - readable attr _opt_help: OptionBool = new OptionBool("Show Help (This screen)", "-h", "-?", "--help") + readable var _opt_help: OptionBool = new OptionBool("Show Help (This screen)", "-h", "-?", "--help") + + # Option --version + readable var _opt_version: OptionBool = new OptionBool("Show version and exit", "--version") + + # Option --verbose + readable var _opt_verbose: OptionCount = new OptionCount("Verbose", "-v", "--verbose") + + # Option --stop-on-first-error + readable var _opt_stop_on_first_error: OptionBool = new OptionBool("Stop on first error", "--stop-on-first-error") + + # Verbose level + readable var _verbose_level: Int = 0 init do super - option_context.add_option(opt_warn, opt_path, opt_log, opt_only_parse, opt_only_metamodel, opt_help) + option_context.add_option(opt_warn, opt_stop_on_first_error, opt_path, opt_log, opt_log_dir, opt_only_parse, opt_only_metamodel, opt_help, opt_version, opt_verbose) end # Parse and process the options given on the command line - meth process_options + fun process_options do # init options option_context.parse(args) + # Set verbose level + _verbose_level = opt_verbose.value + # Setup the paths value - _paths = new Array[String] paths.append(opt_path.value) var path_env = once ("NIT_PATH".to_symbol).environ @@ -101,12 +187,18 @@ special MMContext var libname = "{sys.program_name.dirname}/../lib" if libname.file_exists then paths.add(libname) + + if opt_log_dir.value != null then _log_directory = opt_log_dir.value.as(not null) + if _opt_log.value then + # Make sure the output directory exists + log_directory.mkdir + end end # Load and process a module in a directory (or a parent directory). # If the module is already loaded, just return it without further processing. # If no module is found, just return null without complaining. - private meth try_to_load(module_name: Symbol, dir: MMDirectory): MMModule + private fun try_to_load(module_name: Symbol, dir: MMDirectory): nullable MMModule do # Look in the module directory for m in dir.modules do @@ -130,9 +222,7 @@ special MMContext var full_name = dir.full_name_for(module_name) if _processing_modules.has(full_name) then # FIXME: Generate better error - error("Error: Dependency loop for module {full_name}") - exit(1) - abort + fatal_error(null, "Error: Dependency loop for module {full_name}") end _processing_modules.add(full_name) var m = l.load_and_process_module(self, module_name, dir) @@ -147,12 +237,12 @@ special MMContext # List of module currently processed. # Used to prevent dependence loops. - attr _processing_modules: HashSet[Symbol] = new HashSet[Symbol] + var _processing_modules: HashSet[Symbol] = new HashSet[Symbol] # Locate, load and analysis a module (and its supermodules) from its file name. # If the module is already loaded, just return it without further processing. # Beware, the files are automatically considered root of their directory. - meth get_module_from_filename(filename: String): MMModule + fun get_module_from_filename(filename: String): MMModule do var path = filename.dirname var module_name = filename.basename(".nit").to_symbol @@ -170,8 +260,7 @@ special MMContext end if not filename.file_exists then - error("Error: File {filename} not found.") - exit(1) + fatal_error(null, "Error: File {filename} not found.") abort end @@ -179,18 +268,16 @@ special MMContext var m = try_to_load(module_name, dir) if m != null then return m - error("Error: {filename} is not a NIT source module.") - exit(1) + fatal_error(null, "Error: {filename} is not a NIT source module.") abort end # Locate, load and analysis a module (and its supermodules). # If the module is already loaded, just return it without further processing. - meth get_module(module_name: Symbol, from: MMModule): MMModule + fun get_module(module_name: Symbol, from: nullable MMModule): MMModule do - var m: MMModule if from != null then - var dir = from.directory + var dir: nullable MMDirectory = from.directory while dir != null do var m = try_to_load(module_name, dir) if m != null then return m @@ -203,13 +290,12 @@ special MMContext if m != null then return m end # FIXME: Generate better error - error("Error: No ressource found for module {module_name}.") - exit(1) + fatal_error(null, "Error: No ressource found for module {module_name}.") abort end # Return the module directory associated with a given path - private meth directory_for(path: String): MMDirectory + private fun directory_for(path: String): MMDirectory do if _path_dirs.has_key(path) then return _path_dirs[path] var dir = new MMDirectory(path.to_symbol, path, null) @@ -218,10 +304,10 @@ special MMContext end # Association bwtween plain path and module directories - attr _path_dirs: Map[String, MMDirectory] = new HashMap[String, MMDirectory] + var _path_dirs: Map[String, MMDirectory] = new HashMap[String, MMDirectory] # Register a new module loader - meth register_loader(ml: ModuleLoader) do _loaders.add(ml) + fun register_loader(ml: ModuleLoader) do _loaders.add(ml) end # A load handler know how to load a specific module type @@ -230,10 +316,10 @@ class ModuleLoader type MODULE: MMModule # Extension that the loadhandler accepts - meth file_type: String is abstract + fun file_type: String is abstract # Try to load a new module directory - meth try_to_load_dir(dirname: Symbol, parent_dir: MMDirectory): MMDirectory + fun try_to_load_dir(dirname: Symbol, parent_dir: MMDirectory): nullable MMDirectory do var fname = "{parent_dir.path}/{dirname}/" if not fname.file_exists then return null @@ -244,7 +330,7 @@ class ModuleLoader # Can the loadhandler load a given module? # Return the file found - meth can_handle(module_name: Symbol, dir: MMDirectory): Bool + fun can_handle(module_name: Symbol, dir: MMDirectory): Bool do var fname = "{dir.path}/{module_name}.{file_type}" if fname.file_exists then return true @@ -253,7 +339,7 @@ class ModuleLoader # Load the module and process it # filename is the result of can_handle - meth load_and_process_module(context: ToolContext, module_name: Symbol, dir: MMDirectory): MODULE + fun load_and_process_module(context: ToolContext, module_name: Symbol, dir: MMDirectory): MODULE do var filename = "{dir.path}/{module_name}.{file_type}" var m = load_module(context, module_name, dir, filename) @@ -262,7 +348,7 @@ class ModuleLoader end # Load an parse the module - private meth load_module(context: ToolContext, module_name: Symbol, dir: MMDirectory, filename: String): MODULE + private fun load_module(context: ToolContext, module_name: Symbol, dir: MMDirectory, filename: String): MODULE do var file: IFStream if filename == "-" then @@ -272,40 +358,16 @@ class ModuleLoader end if file.eof then - context.error("Error: Problem in opening file {filename}") - exit(1) - abort + context.fatal_error(null, "Error: Problem in opening file {filename}") end var m = parse_file(context, file, filename, module_name, dir) - m.filename = filename if file != stdin then file.close return m end # Parse the file to load a module - protected meth parse_file(context: ToolContext, file: IFStream, filename: String, module_name: Symbol, dir: MMDirectory): MODULE is abstract + protected fun parse_file(context: ToolContext, file: IFStream, filename: String, module_name: Symbol, dir: MMDirectory): MODULE is abstract # Process a parsed module - protected meth process_metamodel(context: ToolContext, module: MODULE) is abstract -end - -redef class MMModule - # The filename of the module - readable writable attr _filename: String - - # Last modification time - readable writable attr _mtime: Int - - # Recurcivelty process an import modules - meth import_supers_modules(names: Collection[Symbol]) - do - var c = context - assert c isa ToolContext - var supers = new Array[MMModule] - for n in names do - var m = c.get_module(n, self) - supers.add(m) - end - c.add_module(self,supers) - end + protected fun process_metamodel(context: ToolContext, mod: MODULE) is abstract end