diff --git a/lib/comfpile/engines/parser_engine.rb b/lib/comfpile/engines/parser_engine.rb index 104017e..fff70b9 100644 --- a/lib/comfpile/engines/parser_engine.rb +++ b/lib/comfpile/engines/parser_engine.rb @@ -1,15 +1,118 @@ +require_relative '../artefact.rb' + module Compfile - class ParserArtefact < Artefact + class ParserArtefact < Comfpile::Artefact def initialize(*args, **opts) - super(*args, **opts) + super(*args) + + @search_regexes = opts[:search_regexes] + + @parsed_parameters = {} parent_artefact :sourcefile, @target add_step do - + sourcefile_parse_step + sourcefile_dependency_step end end + + # Attempts to find the set of values for a given + # parameter key. + # + # @param [String, Array] keys Key, or array of keys, to look for + # + # @return [Array] Array of values found for this key + # + def find_parsed_parameters(keys) + if keys.is_a? Array + output = [] + keys.each do |key| + output += @parsed_parameters[key] || [] + end + + output + else + @parsed_parameters[key] || [] + end + end + + # + # Try to find a relative or absolute source file + # artefact. + # Used to resolve the include, require and reference + # statements and find relative and/or absolute source + # files with matching name. Similar to how C++'s + # include statement it will check relative, then + # globally. + # + # @param [String, Array] name Path of the sourcefile to look for + # @param [Boolean] optional If true, will also check for absolute + # source file, and will discard the file if it can't be found. + # Useful for e.g. C/C++ header files that may not be in the search + # path of comfpile. + # + # @return [String] Resolved path of the sourcefile. Relative + # if a relative file is found, else absolute. + # + def resolve_sourcefile(name, optional: false) + if(name.is_a? Array) + name.map do |n| + resolve_sourcefile(n, optional: optional) + end + + name.compact + else + own_dir = File.dirname(@target) + relative_file = File.join(own_dir, name) + + if not craft_artefact(:sourcefile, relative_file).nil? + relative_file + elsif not craft_artefact(:sourcefile, name).nil? + name + elsif optional + relative_file + else + nil + end + end + end + + def sourcefile_parse_step + log "Parsing file #{@target}..." + + File.readlines(@parent_artefact[:file]).each do |line| + @search_regexes.each do |r| + match = r.match line + next if match.nil? + + key = match[:key] + value = match[:value] + + next if key.nil? + + log "Found k/v pair: '#{key}' '#{value}'", :debug + + @parsed_parameters[key] ||= [] + @parsed_parameters[key] << value + end + end + + log "Parsing completed, found #{@parsed_parameters.keys.size} parameters!" + end + + def sourcefile_dependency_step + included_artefacts = resolve_sourcefile find_parsed_parameters 'include' + required_artefacts = resolve_sourcefile find_parsed_parameters 'require' + referenced_artefacts = resolve_sourcefile find_parsed_parameters 'reference' + + included_artefacts.each { |a| include_artefact :parsed, a } + required_artefacts.each { |a| require_artefact :parsed, a } + referenced_artefacts.each { |a| reference_artefact :parsed, a } + + log "Generated dependencies. Included #{included_artefacts.size}, required #{required_artefact.size} and referenced #{referenced_artefacts.size}." + end end class ParserEngine < ArtefactEngine