term_utils 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/COPYING +3 -3
  4. data/README.md +7 -1
  5. data/doc/TermUtils/AP/Article.html +991 -0
  6. data/doc/TermUtils/AP/Element.html +1025 -0
  7. data/doc/TermUtils/AP/Flag.html +539 -0
  8. data/doc/TermUtils/AP/Level.html +638 -0
  9. data/doc/TermUtils/AP/NoSuchValueError.html +217 -0
  10. data/doc/TermUtils/AP/Parameter.html +804 -0
  11. data/doc/TermUtils/AP/ParseError.html +217 -0
  12. data/doc/TermUtils/AP/Parser.html +572 -0
  13. data/doc/TermUtils/AP/Result.html +1029 -0
  14. data/doc/TermUtils/AP/ResultView.html +597 -0
  15. data/doc/TermUtils/AP/Syntax.html +1051 -0
  16. data/doc/TermUtils/AP/SyntaxError.html +217 -0
  17. data/doc/TermUtils/AP.html +505 -0
  18. data/doc/TermUtils/FF/Config.html +1 -1
  19. data/doc/TermUtils/FF/Cursor/Context.html +1 -1
  20. data/doc/TermUtils/FF/Cursor.html +1 -1
  21. data/doc/TermUtils/FF/Query.html +1 -1
  22. data/doc/TermUtils/FF.html +1 -1
  23. data/doc/TermUtils/PropertyTreeNode.html +1999 -0
  24. data/doc/TermUtils/Tab/Column.html +1 -1
  25. data/doc/TermUtils/Tab/Holder.html +1 -1
  26. data/doc/TermUtils/Tab/Printer.html +1 -1
  27. data/doc/TermUtils/Tab/Table.html +1 -1
  28. data/doc/TermUtils/Tab.html +1 -1
  29. data/doc/TermUtils.html +5 -3
  30. data/doc/_index.html +143 -1
  31. data/doc/class_list.html +1 -1
  32. data/doc/file.README.html +8 -2
  33. data/doc/index.html +8 -2
  34. data/doc/method_list.html +638 -22
  35. data/doc/top-level-namespace.html +1 -1
  36. data/lib/term_utils/ap/article.rb +68 -0
  37. data/lib/term_utils/ap/element.rb +78 -0
  38. data/lib/term_utils/ap/flag.rb +48 -0
  39. data/lib/term_utils/ap/level.rb +57 -0
  40. data/lib/term_utils/ap/no_such_value_error.rb +27 -0
  41. data/lib/term_utils/ap/parameter.rb +75 -0
  42. data/lib/term_utils/ap/parse_error.rb +27 -0
  43. data/lib/term_utils/ap/parser.rb +185 -0
  44. data/lib/term_utils/ap/result.rb +175 -0
  45. data/lib/term_utils/ap/syntax.rb +112 -0
  46. data/lib/term_utils/ap/syntax_error.rb +27 -0
  47. data/lib/term_utils/ap.rb +59 -0
  48. data/lib/term_utils/property_tree_node.rb +200 -0
  49. data/lib/term_utils.rb +1 -0
  50. data/term_utils.gemspec +2 -2
  51. metadata +29 -2
@@ -100,7 +100,7 @@
100
100
  </div>
101
101
 
102
102
  <div id="footer">
103
- Generated on Thu Oct 17 22:50:57 2019 by
103
+ Generated on Sun Nov 10 18:36:19 2019 by
104
104
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
105
  0.9.20 (ruby-2.6.5).
106
106
  </div>
@@ -0,0 +1,68 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents a Article.
21
+ class Article
22
+ # @return [Symbol]
23
+ attr_accessor :id
24
+ # @return [Integer]
25
+ attr_accessor :min_occurs
26
+ # @return [Integer]
27
+ attr_accessor :max_occurs
28
+ # @return [Symbol] `:integer`, `:string`
29
+ attr_accessor :type
30
+ # @return [String] `%d`, `%s`.
31
+ attr_accessor :format
32
+ # Constructs a new Article.
33
+ # @param opts [Hash]
34
+ # @option opts [Symbol] :id
35
+ # @option opts [Integer] :min_occurs Default value is `1`.
36
+ # @option opts [Integer] :max_occurs Default value is `1`.
37
+ # @option opts [Symbol] :type `:integer`, `:string`.
38
+ # @option opts [Symbol] :format
39
+ def initialize(opts = {})
40
+ @id = opts.fetch(:id, nil)
41
+ @min_occurs = opts.fetch(:min_occurs, 1)
42
+ @max_occurs = opts.fetch(:max_occurs, 1)
43
+ @type = opts.fetch(:type, :string)
44
+ @format = opts.fetch(:format, "%s")
45
+ end
46
+ # Finalizes this one. Internal use.
47
+ # @return [nil]
48
+ def finalize!(opts = {})
49
+ raise TermUtils::AP::SyntaxError, "min_occurs must be equal or greater than 0" unless (@min_occurs.is_a? Integer) && (@min_occurs >= 0)
50
+ raise TermUtils::AP::SyntaxError, "max_occurs must be equal or greater than min_occurs" unless !occur_bounded? || (@max_occurs >= @min_occurs)
51
+ unless @id
52
+ opts[:anonymous] += 1
53
+ @id = "anonymous#{opts[:anonymous]}".intern
54
+ end
55
+ end
56
+ # Tests whether this one has mutiple occurs.
57
+ # @return [Boolean]
58
+ def multiple_occurs?
59
+ (@max_occurs == nil) || (@max_occurs == :infinity) || ((@max_occurs.is_a? Integer) && (@max_occurs > 1))
60
+ end
61
+ # Tests whether the number of occurs are fixed.
62
+ # @return [Boolean]
63
+ def occur_bounded?
64
+ (@max_occurs != nil) && (@max_occurs != :infinity) && (@max_occurs.is_a? Integer)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,78 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents a syntax element (abstract).
21
+ class Element
22
+ # @return [Symbol]
23
+ attr_accessor :id
24
+ # @return [Integer]
25
+ attr_accessor :min_occurs
26
+ # @return [Integer]
27
+ attr_accessor :max_occurs
28
+ # @return [Array<TermUtils::AP::Flag>]
29
+ attr_accessor :flags
30
+ # For dup method.
31
+ def initialize_dup(other)
32
+ if other.flags
33
+ @flags = []
34
+ other.flags.each do |f|
35
+ @flags << f.dup
36
+ end
37
+ end
38
+ super
39
+ end
40
+ # Finalizes this one. Internal use.
41
+ # @return [nil]
42
+ def finalize!(opts = {})
43
+ raise TermUtils::AP::SyntaxError, "min_occurs must be equal or greater than 0" unless (@min_occurs.is_a? Integer) && (@min_occurs >= 0)
44
+ raise TermUtils::AP::SyntaxError, "max_occurs must be equal or greater than min_occurs" unless !occur_bounded? || (@max_occurs >= @min_occurs)
45
+ unless @id
46
+ opts[:anonymous] += 1
47
+ @id = "anonymous#{opts[:anonymous]}".intern
48
+ end
49
+ end
50
+ # Tests whether this one has mutiple occurs.
51
+ # @return [Boolean]
52
+ def multiple_occurs?
53
+ (@max_occurs == nil) || (@max_occurs == :infinity) || ((@max_occurs.is_a? Integer) && (@max_occurs > 1))
54
+ end
55
+ # Tests whether the number of occurs are fixed.
56
+ # @return [Boolean]
57
+ def occur_bounded?
58
+ (@max_occurs != nil) && (@max_occurs != :infinity) && (@max_occurs.is_a? Integer)
59
+ end
60
+ # Tests whether this one is flagged.
61
+ # @return [Boolean]
62
+ def flagged?
63
+ !@flags.empty?
64
+ end
65
+ # Adds a new Flag to this one.
66
+ # @param opts [Hash]
67
+ # @option opts [String] :label
68
+ # @option opts [Symbol] :flavor `:anchor`, `:long`, `:short`.
69
+ # @return [TermUtils::AP::Flag]
70
+ def define_flag(opts = {}, &block)
71
+ new_flag = TermUtils::AP::Flag.new(opts)
72
+ @flags << new_flag
73
+ block.call(new_flag) if block
74
+ new_flag
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,48 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents a Flag.
21
+ class Flag
22
+ # @return [String]
23
+ attr_accessor :label
24
+ # @return [Symbol] `:anchor`, `:long`, `:short`.
25
+ attr_accessor :flavor
26
+ # Constructs a new Flag.
27
+ # @param opts [Hash]
28
+ # @option opts [String] :label
29
+ # @option opts [Symbol] :flavor `:anchor`, `:long`, `:short`.
30
+ def initialize(opts = {})
31
+ @label = opts.fetch(:label, nil)
32
+ @flavor = opts.fetch(:flavor, nil)
33
+ end
34
+ # Returns the string representation of this one.
35
+ # @return [String]
36
+ def to_s
37
+ case @flavor
38
+ when :anchor
39
+ @label
40
+ when :long
41
+ "--%s" % @label
42
+ when :short
43
+ "-%s" % @label
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,57 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents an argument list subset. It must lead with a Flag. It has its
21
+ # own syntax.
22
+ class Level < TermUtils::AP::Element
23
+ # @return [TermUtils::AP::Syntax]
24
+ attr_accessor :syntax
25
+ # Constructs a new Level.
26
+ # @param opts [Hash]
27
+ # @option opts [Symbol] :id
28
+ # @option opts [Integer] :min_occurs Default value is `0`.
29
+ # @option opts [Integer] :max_occurs Default value is `1`.
30
+ def initialize(opts = {})
31
+ @id = opts.fetch(:id, nil)
32
+ @min_occurs = opts.fetch(:min_occurs, 0)
33
+ @max_occurs = opts.fetch(:max_occurs, 1)
34
+ @flags = []
35
+ @syntax = TermUtils::AP::Syntax.new
36
+ end
37
+ # For dup method.
38
+ def initialize_dup(other)
39
+ @syntax = other.syntax.dup
40
+ super
41
+ end
42
+ # Finalizes this one. Internal use.
43
+ # @return [nil]
44
+ def finalize!(opts = {})
45
+ super
46
+ raise TermUtils::AP::SyntaxError, "level must contain at least one flag" if @flags.empty?
47
+ @syntax.finalize!(opts)
48
+ end
49
+ # Defines the syntax.
50
+ # @return [TermUtils::AP::Syntax]
51
+ def define_syntax(&block)
52
+ block.call(@syntax) if block
53
+ @syntax
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # No such value error.
21
+ class NoSuchValueError < StandardError
22
+ def initialize(msg)
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,75 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents a Parameter.
21
+ class Parameter < TermUtils::AP::Element
22
+ # @return [Array<TermUtils::AP::Article>]
23
+ attr_accessor :articles
24
+ # Constructs a new Parameter.
25
+ # @param opts [Hash]
26
+ # @option opts [Symbol] :id
27
+ # @option opts [Integer] :min_occurs Default value is `0`.
28
+ # @option opts [Integer] :max_occurs Default value is `1`.
29
+ def initialize(opts = {})
30
+ @id = opts.fetch(:id, nil)
31
+ @min_occurs = opts.fetch(:min_occurs, 0)
32
+ @max_occurs = opts.fetch(:max_occurs, 1)
33
+ @flags = []
34
+ @articles = []
35
+ end
36
+ # For dup method.
37
+ def initialize_dup(other)
38
+ if other.articles
39
+ @articles = []
40
+ other.articles.each do |a|
41
+ @articles << a.dup
42
+ end
43
+ end
44
+ super
45
+ end
46
+ # Finalizes this one. Internal use.
47
+ # @return [nil]
48
+ def finalize!(opts = {})
49
+ super
50
+ @articles.each do |a|
51
+ a.finalize!(opts)
52
+ end
53
+ end
54
+ # Adds a new Article to this one.
55
+ # @param opts [Hash]
56
+ # @option opts [Symbol] :id
57
+ # @option opts [Integer] :min_occurs Default value is `1`.
58
+ # @option opts [Integer] :max_occurs Default value is `1`.
59
+ # @option opts [Symbol] :type `:integer`, `:string`.
60
+ # @option opts [Symbol] :format
61
+ # @return [TermUtils::AP::Article]
62
+ def define_article(opts = {}, &block)
63
+ new_article = TermUtils::AP::Article.new(opts)
64
+ @articles << new_article
65
+ block.call(new_article) if block
66
+ new_article
67
+ end
68
+ # Fetches all articles.
69
+ # @return [Array<TermUtils::AP::Article>]
70
+ def fetch_articles
71
+ @articles.collect { |a| a.dup }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Parse error.
21
+ class ParseError < StandardError
22
+ def initialize(msg)
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,185 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # Copyright (C) 2019 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+ module TermUtils
19
+ module AP
20
+ # Represents the argument list parser.
21
+ class Parser
22
+ def initialize
23
+ end
24
+ # Parses a given command.
25
+ # @param syntax [TermUtils::AP::Syntax]
26
+ # @param command [String]
27
+ # @param opts [Hash] `:program`.
28
+ # @option opts [Boolean] :program Whether the first argument is the program name.
29
+ # @return [TermUtils::AP::Result]
30
+ # @raise [TermUtils::AP::ParseError]
31
+ # @raise [TermUtils::AP::SyntaxError]
32
+ def parse_command(syntax, command, opts = {})
33
+ parse_arguments(syntax, command.split(" "), opts)
34
+ end
35
+ # Parses a given list of arguments.
36
+ # @param syntax [TermUtils::AP::Syntax]
37
+ # @param arguments [Array<String>]
38
+ # @param opts [Hash] `:program`.
39
+ # @option opts [Boolean] :program Whether the first argument is the program name.
40
+ # @return [TermUtils::AP::Result]
41
+ # @raise [TermUtils::AP::ParseError]
42
+ # @raise [TermUtils::AP::SyntaxError]
43
+ def parse_arguments(syntax, arguments, opts = {})
44
+ syntax = syntax.dup
45
+ syntax.finalize!
46
+ arguments = arguments.dup
47
+ res = TermUtils::AP::Result.new(syntax)
48
+ catch :done do
49
+ parse0(res.value, syntax, arguments, opts)
50
+ end
51
+ res
52
+ end
53
+ private
54
+ # Parses a given argument list.
55
+ # @param node [TermUtils::PropertyTreeNode]
56
+ # @param syntax [TermUtils::AP::Syntax]
57
+ # @param arguments [Array<String>]
58
+ def parse0(node, syntax, arguments, opts = {})
59
+ # puts "parse0"
60
+ opts = opts.dup
61
+ if opts[:program]
62
+ arguments.shift
63
+ opts.delete :program
64
+ end
65
+ throw :done if arguments.empty?
66
+ flagged_elems, unflagged_params = syntax.fetch_elements
67
+ fp_occ = {}
68
+ syntax.elements.each { |e| fp_occ[e.id] = 0 if e.flagged? }
69
+ up_occ = 0
70
+ loop do
71
+ break if arguments.empty?
72
+ # puts "# %s" % arguments.first
73
+ if arguments.first === "---"
74
+ arguments.shift
75
+ break
76
+ end
77
+ if flagged_elems.has_key? arguments.first
78
+ # Flagged parameter/level.
79
+ elem = flagged_elems[arguments.first]
80
+ raise TermUtils::AP::ParseError, "parameter reached its occur limit" if elem.occur_bounded? && (fp_occ[elem.id] >= elem.max_occurs)
81
+ fp_occ[elem.id] += 1
82
+ if elem.is_a? TermUtils::AP::Parameter
83
+ arguments.shift
84
+ sub_node = node.child_node(elem.id)
85
+ sub_node = node.define_node(:key => elem.id) unless sub_node
86
+ if elem.multiple_occurs?
87
+ sub_node.child_nodes = [] if sub_node.leaf?
88
+ idx = sub_node.child_nodes.length
89
+ sub_node = sub_node.define_node(:key => idx)
90
+ end
91
+ catch :param_done do
92
+ parse0_param(sub_node, elem, arguments, opts)
93
+ end
94
+ elsif elem.is_a? TermUtils::AP::Level
95
+ arguments.shift
96
+ sub_node = node.child_node(elem.id)
97
+ sub_node = node.define_node(:key => elem.id) unless sub_node
98
+ if elem.multiple_occurs?
99
+ sub_node.child_nodes = [] if sub_node.leaf?
100
+ idx = sub_node.child_nodes.length
101
+ sub_node = sub_node.define_node(:key => idx)
102
+ end
103
+ parse0(sub_node, elem.syntax, arguments, opts)
104
+ else
105
+ raise TermUtils::AP::ParseError, "internal error - wrong kind of syntax element"
106
+ end
107
+ else
108
+ # Unflagged parameter.
109
+ raise TermUtils::AP::ParseError, "unexpected unflagged parameter" if unflagged_params.empty?
110
+ raise TermUtils::AP::ParseError, "unexpected flagged parameter" if arguments.first.start_with? "-"
111
+ elem = unflagged_params.shift
112
+ sub_node = node.child_node(elem.id)
113
+ sub_node = node.define_node(:key => elem.id) unless sub_node
114
+ if elem.multiple_occurs?
115
+ sub_node.child_nodes = [] if sub_node.leaf?
116
+ idx = sub_node.child_nodes.length
117
+ sub_node = sub_node.define_node(:key => idx)
118
+ end
119
+ catch :param_done do
120
+ occ = up_occ
121
+ up_occ = 0
122
+ parse0_param(sub_node, elem, arguments, opts)
123
+ up_occ = occ
124
+ up_occ += 1
125
+ if elem.multiple_occurs? && (!elem.occur_bounded? || (up_occ < elem.max_occurs))
126
+ unflagged_params.unshift elem
127
+ else
128
+ up_occ = 0
129
+ end
130
+ end
131
+ end
132
+ end # loop
133
+ end
134
+ def parse0_param(node, param, arguments, opts = {})
135
+ arts = param.fetch_articles
136
+ occ = 0
137
+ loop do
138
+ if arguments.empty?
139
+ raise TermUtils::AP::ParseError, "unexpected end of parameter" if !arts.empty? && (occ < arts.first.min_occurs)
140
+ raise TermUtils::AP::ParseError, "unexpected end of parameter" if !arts.empty? && (self.class.eval_article_min_occurs(arts[1..-1]) > 0)
141
+ throw :done
142
+ end
143
+ break if arts.empty?
144
+ if arguments.first == "--"
145
+ # End of parameter.
146
+ raise TermUtils::AP::ParseError, "unexpected end of parameter" if !arts.empty? && (occ < arts.first.min_occurs)
147
+ raise TermUtils::AP::ParseError, "unexpected end of parameter" if !arts.empty? && (self.class.eval_article_min_occurs(arts[1..-1]) > 0)
148
+ arguments.shift
149
+ throw :param_done
150
+ elsif arguments.first == "-"
151
+ # End of article.
152
+ raise TermUtils::AP::ParseError, "unexpected article escape" if arts.empty?
153
+ raise TermUtils::AP::ParseError, "unexpected end of parameter" if !arts.empty? && (occ < arts.first.min_occurs)
154
+ arguments.shift
155
+ arts.shift
156
+ occ = 0
157
+ next
158
+ end
159
+ art = arts.first
160
+ sub_node = node.child_node(art.id)
161
+ sub_node = node.define_node(:key => art.id) unless sub_node
162
+ if art.multiple_occurs?
163
+ sub_node.child_nodes = [] if sub_node.leaf?
164
+ idx = sub_node.child_nodes.length
165
+ sub_node = sub_node.define_node(:key => idx)
166
+ end
167
+ sub_node.value = arguments.shift
168
+ occ += 1
169
+ if !art.multiple_occurs? || (art.occur_bounded? && (occ >= art.max_occurs))
170
+ arts.shift
171
+ occ = 0
172
+ end
173
+ end # loop
174
+ end
175
+ # Evaluates the added number of min occurs of a given array of articles.
176
+ # @param articles [Array<TermUtils::AP::Article>]
177
+ # @return [Integer]
178
+ def self.eval_article_min_occurs(articles)
179
+ occs = 0
180
+ articles.each { |a| occs += a.min_occurs }
181
+ occs
182
+ end
183
+ end
184
+ end
185
+ end