# frozen-string-literal: true # # Copyright (C) 2019 Thomas Baron # # This file is part of term_utils. # # term_utils is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 3 of the License. # # term_utils is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with term_utils. If not, see . module TermUtils module AP # Represents an argument parsing Result. class Result # @return [TermUtils::PropertyTreeNode] attr_accessor :value # Constructs a new Result. # @param syntax [Darn::AP::Syntax] Syntax. # @param element [Darn::AP::Element] Optional Element. # @param value [Darn::PropertyTreeNode] Optional value. def initialize(syntax, element = nil, value = nil) @syntax = syntax.dup @element = element ? element.dup : nil if value @value = value.dup @value.key = nil else @value = TermUtils::PropertyTreeNode.new end end # Collects IDs. # @param id [Symbol, Array] ID path. # @return [Array] def collect(id, &block) node = @value if id id = [id] if id.is_a? Symbol node = @value.find_node(id) end res = [] return res unless node && node.child_nodes if block node.child_nodes.each do |n| res << n.key if block.call(n.key) end else node.child_nodes.each do |n| res << n.key end end res end # Tests whether a given level/parameter/article is present in the result value. # @param id [Symbol, Array] ID path. # @return [Boolean] def present?(id) if id.is_a? Symbol !!@value.child_node(id) elsif id.is_a? Array @value.node_exists?(id) end end # Evaluates the number of occurrences of a given level, parameter or article. # @param id [Symbol, Array] # @return [Integer] def eval_occurs(id) id = [id] if id.is_a? Symbol obj = fetch_syntax_object(id) raise TermUtils::AP::NoSuchValueError, "no such syntax object" unless obj node = @value.find_node(id) return 0 unless node return 1 if obj.occur_bounded? && (obj.max_occurs == 1) # Parameter is multiple. node.child_nodes ? node.child_nodes.length : 0 end # Fetches a value. # @param id [Symbol, Array, nil] # @param opts [Hash] `:index`, `:multi`. # @option opts [Integer] :index The index of a multiple-occurrence level/parameter/article. # @option opts [Boolean] :multi Whether an array of values shall be returned instead of a single value. # @return [Object] # @raise [TermUtils::AP::NoSuchValueError] def fetch_value(id, opts = {}) index = opts.fetch(:index, nil) multi = opts.fetch(:multi, false) unless id node = @value if node && node.child_nodes && index node = node.child_node(index) end raise TermUtils::AP::NoSuchValueError, "no such value" unless node vals = node.collect_values raise TermUtils::AP::NoSuchValueError, "no such value" if vals.empty? return multi ? vals : vals.first end id = [id] if id.is_a? Symbol obj = fetch_syntax_object(id) raise TermUtils::AP::NoSuchValueError, "no such syntax object" unless obj node = @value.find_node(id) if node && node.child_nodes && index node = node.child_node(index) end raise TermUtils::AP::NoSuchValueError, "no such value" unless node catch :value do if obj.is_a? TermUtils::AP::Parameter raise TermUtils::AP::NoSuchValueError, "parameter has no article" if obj.articles.empty? raise TermUtils::AP::NoSuchValueError, "no such value" if node.leaf? vals = node.collect_values raise TermUtils::AP::NoSuchValueError, "no such value" if vals.empty? if multi throw :value, vals else throw :value, vals.first end elsif obj.is_a? TermUtils::AP::Article # raise TermUtils::AP::NoSuchValueError, "no such value" if node.leaf? vals = node.collect_values raise TermUtils::AP::NoSuchValueError, "no such value" if vals.empty? if multi throw :value, vals else throw :value, vals.first end end raise TermUtils::AP::NoSuchValueError, "wrong id" end end # Shifts this one. # @param id [Symbol, Array] # @param opts [Hash] `:index`, `:multi`. # @option opts [Integer] :index The index of a multiple-occurrence level/parameter/article. # @return [TermUtils::AP::Result] # @raise [TermUtils::AP::NoSuchValueError] def shift(id, opts = {}) index = opts.fetch(:index, nil) id = [id] if id.is_a? Symbol obj = fetch_syntax_object(id) raise TermUtils::AP::NoSuchValueError, "no such syntax object" unless obj node = @value.find_node(id) if node && node.child_nodes && index node = node.child_node(index) end raise TermUtils::AP::NoSuchValueError, "no such value" unless node catch :value do if obj.is_a? TermUtils::AP::Level throw :value, TermUtils::AP::Result.new(obj.syntax, nil, node) elsif obj.is_a? TermUtils::AP::Parameter throw :value, TermUtils::AP::Result.new(@syntax, obj, node) elsif obj.is_a? TermUtils::AP::Article throw :value, TermUtils::AP::Result.new(@syntax, obj, node) end raise TermUtils::AP::NoSuchValueError, "wrong id" end end private # Fetches a given syntax object. # @param id_path [Array] # @return [TermUtils::AP::Level, TermUtils::AP::Parameter, TermUtils::AP::Article, nil] def fetch_syntax_object(id_path) id = id_path.dup id = [@element.id].concat(id) if @element catch :done do fetch_syntax_object0(@syntax, id) end end # Fetches a given syntax object. # @param syntax [TermUtils::AP::Syntax] # @param id_path [Array] # @return [TermUtils::AP::Level, TermUtils::AP::Parameter, TermUtils::AP::Article, nil] def fetch_syntax_object0(syntax, id_path) id = id_path.shift syntax.elements.each do |e| next unless e.id == id throw :done, e if id_path.empty? if e.is_a? TermUtils::AP::Level fetch_syntax_object0(e.syntax, id_path) elsif e.is_a? TermUtils::AP::Parameter throw :done if id_path.empty? id = id_path.shift throw :done unless id_path.empty? e.articles.each do |a| throw :done, a if a.id = id end throw :done end end end end end end