class TermUtils::AP::Parser
Represents the argument list parser.
Public Class Methods
Evaluates the added number of min occurs of a given array of articles. @param articles [Array<TermUtils::AP::Article>] @return [Integer]
# File lib/term_utils/ap/parser.rb, line 64 def self.eval_article_min_occurs(articles) articles.inject(0) { |acc, a| acc + a.min_occurs } end
Tests whether a given sample matches a shortcut flag. @param shortcut_flags [Hash<String, Flag>] @param arg [String] @return [Array<String>, nil] Replacements on success, nil otherwise.
# File lib/term_utils/ap/parser.rb, line 51 def self.match_shortcut_flag(shortcut_flags, arg) shortcut_flags.each do |label, flag| next unless arg.start_with? label return [flag.label, arg[label.length..-1]] end nil end
Constructs a new Parser
.
# File lib/term_utils/ap/parser.rb, line 24 def initialize end
Public Instance Methods
Parses a given list of arguments. @param syntax [Syntax] @param arguments [Array<String>] @param opts [Hash] @return [Result] @raise [ParseError] @raise [SyntaxError]
# File lib/term_utils/ap/parser.rb, line 34 def parse_arguments(syntax, arguments, opts = {}, &block) syntax = syntax.dup syntax.finalize! arguments = arguments.dup res = TermUtils::AP::Result.new(syntax) catch :done do parse0(res, syntax, arguments, opts) end res.remaining_arguments = arguments res.walk(&block) if block res end
Private Instance Methods
Parses a given argument list. @param result [Result] @param syntax [Syntax] @param arguments [Array<String>] @raise [ParseError]
# File lib/term_utils/ap/parser.rb, line 75 def parse0(result, syntax, arguments, opts = {}) unflagged_params, flagged_params, shortcut_flags = syntax.fetch_parameters fp_occ = {} syntax.parameters.each { |p| fp_occ[p.id] = 0 if p.flagged? } up_occ = 0 loop do break if arguments.empty? if arguments.first.start_with?('-') && !%w[- --].include?(arguments.first) # Flagged unless flagged_params.key? arguments.first # Unknown flag # Try shortcut flag flag, arg = self.class.match_shortcut_flag(shortcut_flags, arguments.first) if flag && arg # Shortcut match arguments.shift arguments.unshift arg arguments.unshift flag end end unless flagged_params.key? arguments.first # Unknown flag # End of parsing raise TermUtils::AP::ParseError, 'flagged parameter unexpected' if opts.fetch(:strict, false) break end param = flagged_params[arguments.first] if param.occur_bounded? && (fp_occ[param.id] >= param.max_occurs) # Max occurs reached raise TermUtils::AP::ParseError, "occur limit reached (#{param.id})" if opts.fetch(:strict, false) break end fp_occ[param.id] += 1 arguments.shift param_res = TermUtils::AP::ParameterResult.new(result, param) parse0_param(param_res, param, arguments) else # Unflagged if unflagged_params.empty? # End of parsing raise TermUtils::AP::ParseError, 'unflagged parameter unexpected' if opts.fetch(:strict, false) break end param = unflagged_params.first if arguments.first == '--' # End of parameter raise TermUtils::AP::ParseError, "parameter not consumed (#{param.id})" if up_occ < param.min_occurs arguments.shift unflagged_params.shift up_occ = 0 next end up_occ += 1 param_res = TermUtils::AP::ParameterResult.new(result, param) case parse0_param(param_res, param, arguments) when :esc_param raise TermUtils::AP::ParseError, "parameter not consumed (#{param.id})" if up_occ < param.min_occurs unflagged_params.shift up_occ = 0 else if !param.multiple_occurs? || (param.occur_bounded? && (up_occ >= param.max_occurs)) unflagged_params.shift up_occ = 0 end end end end # loop # Check min occurs syntax.parameters.each do |p| next if result.find_parameters(p.id).length >= p.min_occurs raise TermUtils::AP::ParseError, "parameter not consumed (#{p.id})" end end
Parses with a given Parameter
. @param result [ParameterResult] @param param [Parameter] @param arguments [Array<String>] @return [Symbol, nil] `:esc_param`, or nil.
# File lib/term_utils/ap/parser.rb, line 165 def parse0_param(result, param, arguments) arts = param.fetch_articles occ = 0 loop do break if arts.empty? if arguments.empty? # End of arguments raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs raise TermUtils::AP::ParseError, 'article not consumed' if self.class.eval_article_min_occurs(arts[1..-1]) > 0 break end if arguments.first == '-' # End of article raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs arguments.shift arts.shift occ = 0 next elsif arguments.first.start_with? '-' # End of parameter raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs raise TermUtils::AP::ParseError, 'article not consumed' if self.class.eval_article_min_occurs(arts[1..-1]) > 0 if arguments.first == '--' arguments.shift return :esc_param end break end art = arts.first TermUtils::AP::ArticleResult.new(result, art, arguments.shift) occ += 1 if !art.multiple_occurs? || (art.occur_bounded? && (occ >= art.max_occurs)) arts.shift occ = 0 end end # loop nil end