lib/term_utils/ap/result.rb in term_utils-0.3.2 vs lib/term_utils/ap/result.rb in term_utils-0.4.0
- old
+ new
@@ -1,9 +1,9 @@
# frozen-string-literal: true
+
+# Copyright (C) 2020 Thomas Baron
#
-# 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.
@@ -13,185 +13,232 @@
# 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 <https://www.gnu.org/licenses/>.
+
module TermUtils
module AP
# Represents an argument parsing Result.
class Result
- # @return [TermUtils::PropertyTreeNode]
- attr_accessor :value
+ # @return [Syntax]
+ attr_reader :parameter
+ # @return [Array<ParameterResult>]
+ attr_reader :results
+ # @return [Array<String>]
+ attr_accessor :remaining_arguments
+
# 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
+ # @param syntax [Syntax]
+ def initialize(syntax)
+ @syntax = syntax
+ @results = []
+ @remaining_arguments = nil
end
- # Collects IDs.
- # @param id [Symbol, Array<Symbol>] ID path.
- # @return [Array<Symbol>]
- 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)
+
+ # Adds a ParameterResult.
+ # @param result [ParameterResult]
+ def add_result(result)
+ @results << result
+ end
+
+ # Returns the first ParameterResult for a given parameter id.
+ # @param id [Symbol]
+ # @return [ParameterResult]
+ def find_parameter(id)
+ @results.find { |r| r.param_id == id }
+ end
+
+ # Returns all ParameterResult(s) for a given parameter id.
+ # @param id [Symbol]
+ # @return [Array<ParameterResult>]
+ def find_parameters(id)
+ @results.find_all { |r| r.param_id == id }
+ end
+
+ # Walks through this one.
+ def walk(&block)
+ walker = TermUtils::AP::Walker.new
+ block.call(walker)
+ @results.each do |p|
+ p.results.each do |a|
+ walker.notify_article(a)
end
- else
- node.child_nodes.each do |n|
- res << n.key
- end
+ walker.notify_parameter(p)
end
- res
+ walker.notify_finished(@remaining_arguments)
end
- # Tests whether a given level/parameter/article is present in the result value.
- # @param id [Symbol, Array<Symbol>] 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
+
+ # Represents a result for a parameter.
+ class ParameterResult
+ # @return [Parameter]
+ attr_accessor :parameter
+ # @return [Array<ArticleResult>]
+ attr_accessor :results
+
+ # Constructs a new ParameterResult.
+ # @param parent [Result]
+ # @param parameter [Parameter]
+ def initialize(parent, parameter)
+ @parent = parent
+ @parent.add_result(self)
+ @parameter = parameter
+ @results = []
end
- # Evaluates the number of occurrences of a given level, parameter or article.
- # @param id [Symbol, Array<Symbol>]
- # @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
+
+ # Adds an ArticleResult.
+ # @param result [ArticleResult]
+ def add_result(result)
+ @results << result
end
- # Fetches a value.
- # @param id [Symbol, Array<Symbol>, 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 [Symbol]
+ def param_id
+ @parameter.id
+ end
+
+ # Returns the first ArticleResult for a given article id.
+ # @param id [Symbol]
+ # @return [ArticleResult]
+ def find_article(id)
+ @results.find { |r| r.art_id == id }
+ end
+
+ # Returns all ArticleResult(s) for a given article id.
+ # @param id [Symbol]
+ # @return [Array<Result>]
+ def find_articles(id)
+ @results.find_all { |r| r.art_id == id }
+ end
+
+ # Returns the value of the first ArticleResult.
+ # @param id [Symbol] Filter of article id.
# @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
+ def value(id = nil)
+ return @results.first.value unless id
+
+ find_article(id).value
+ end
+
+ # Returns the value of all ArticleResult(s).
+ # @param id [Symbol] Filter of article id.
+ # @return [Array<Object>]
+ def values(id = nil)
+ return @results.map(&:value) unless id
+
+ vals = []
+ @results.each do |r|
+ next if r.art_id != id
+
+ vals << r.values
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)
+ vals
+ end
+ end
+
+ # Represents a result for an article.
+ class ArticleResult
+ # @return [ParameterResult]
+ attr_accessor :parent
+ # @return [Article]
+ attr_accessor :article
+ # @return [Object]
+ attr_accessor :value
+
+ # Constructs a new ArticleResult.
+ # @param parent [ParameterResult]
+ # @param article [Article]
+ # @param value [Object]
+ def initialize(parent, article, value)
+ @parent = parent
+ @parent.add_result(self)
+ @article = article
+ @value = value
+ end
+
+ # @return [Symbol]
+ def art_id
+ @article.id
+ end
+ end
+
+ # Represents a Result Walker.
+ class Walker
+ # Constructs a new Walker.
+ def initialize
+ @anonymous_parameter_hook = nil
+ @anonymous_article_hook = nil
+ @parameter_hooks = {}
+ @finished_hook = nil
+ end
+
+ # Registers a parameter hook.
+ def parameter(param_id = nil, &block)
+ unless param_id
+ # Anonymous parameter hook
+ @anonymous_parameter_hook = block
+ return
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
+
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
+ @parameter_hooks[param_id].hook = block
end
- # Shifts this one.
- # @param id [Symbol, Array<Symbol>]
- # @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)
+
+ # Registers an article hook.
+ def article(param_id = nil, art_id = nil, &block)
+ unless param_id
+ # Anonymous article hook
+ @anonymous_article_hook = block
+ return
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"
+
+ unless art_id
+ # Anonymous article hook
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
+ @parameter_hooks[param_id].anonymous_article_hook = block
+ return
end
+
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
+ @parameter_hooks[param_id].article_hooks ||= {}
+ @parameter_hooks[param_id].article_hooks[art_id] = block
end
- private
- # Fetches a given syntax object.
- # @param id_path [Array<Symbol>]
- # @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
+
+ # Registers a walk finished hook.
+ def finished(&block)
+ @finished_hook = block
end
- # Fetches a given syntax object.
- # @param syntax [TermUtils::AP::Syntax]
- # @param id_path [Array<Symbol>]
- # @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
+
+ # Calls parameter hooks.
+ def notify_parameter(parameter)
+ # (1of2) ID parameter hook
+ param_hooks = @parameter_hooks[parameter.param_id]
+ param_hooks.hook.call(parameter) if param_hooks && param_hooks.hook
+ # (2of2) Anonymous parameter hook
+ @anonymous_parameter_hook.call(parameter) if @anonymous_parameter_hook
+ end
+
+ # Calls article hooks.
+ def notify_article(article)
+ # (1of2) ID article hook
+ param_hooks = @parameter_hooks[article.parent.param_id]
+ if param_hooks
+ # ID article hook
+ param_hooks.article_hooks[article.art_id].call(article) if param_hooks.article_hooks && param_hooks.article_hooks.key?(article.art_id)
+ # Anonymous article hook
+ param_hooks.anonymous_article_hook.call(article) if param_hooks.anonymous_article_hook
end
+ # (2of2) Anonymous article hook
+ @anonymous_article_hook.call(article) if @anonymous_article_hook
end
+
+ # Calls finished hook.
+ def notify_finished(remaining_arguments)
+ @finished_hook.call(remaining_arguments) if @finished_hook
+ end
end
+
+ # Parameter hooks for Walker.
+ ParameterWalkerHooks = Struct.new('ParameterWalkerHooks', :hook, :anonymous_article_hook, :article_hooks)
end
end