term_utils 0.3.2 → 0.5.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -2
  3. data/COPYING +3 -3
  4. data/README.md +51 -16
  5. data/Rakefile +6 -0
  6. data/doc/TermUtils/AP/Article.html +57 -55
  7. data/doc/TermUtils/AP/ArticleResult.html +584 -0
  8. data/doc/TermUtils/AP/Flag.html +295 -78
  9. data/doc/TermUtils/AP/Parameter.html +891 -103
  10. data/doc/TermUtils/AP/ParameterResult.html +980 -0
  11. data/doc/TermUtils/{FF/Cursor/Context.html → AP/ParameterWalkerHooks.html} +60 -60
  12. data/doc/TermUtils/AP/ParseError.html +651 -19
  13. data/doc/TermUtils/AP/Parser.html +181 -121
  14. data/doc/TermUtils/AP/Result.html +201 -528
  15. data/doc/TermUtils/AP/Syntax.html +103 -393
  16. data/doc/TermUtils/AP/SyntaxError.html +9 -91
  17. data/doc/TermUtils/AP/Walker.html +686 -0
  18. data/doc/TermUtils/AP.html +49 -160
  19. data/doc/TermUtils/FF/Config.html +203 -35
  20. data/doc/TermUtils/FF/Context.html +585 -0
  21. data/doc/TermUtils/FF/Entry.html +626 -0
  22. data/doc/TermUtils/FF/Finder.html +850 -0
  23. data/doc/TermUtils/FF/{Cursor.html → FinderEntry.html} +473 -211
  24. data/doc/TermUtils/FF/FinderQuery.html +946 -0
  25. data/doc/TermUtils/FF/Query.html +402 -70
  26. data/doc/TermUtils/FF.html +135 -11
  27. data/doc/TermUtils/PropertyTreeNode.html +304 -190
  28. data/doc/TermUtils/Tab/Column.html +98 -96
  29. data/doc/TermUtils/Tab/Header.html +30 -30
  30. data/doc/TermUtils/Tab/Holder.html +81 -81
  31. data/doc/TermUtils/Tab/Printer.html +43 -43
  32. data/doc/TermUtils/Tab/Table.html +124 -128
  33. data/doc/TermUtils/Tab/TableError.html +7 -89
  34. data/doc/TermUtils/Tab.html +93 -86
  35. data/doc/TermUtils.html +10 -10
  36. data/doc/_index.html +62 -42
  37. data/doc/class_list.html +3 -3
  38. data/doc/css/style.css +3 -2
  39. data/doc/file.README.html +63 -26
  40. data/doc/file_list.html +2 -2
  41. data/doc/frames.html +2 -2
  42. data/doc/index.html +63 -26
  43. data/doc/js/app.js +14 -3
  44. data/doc/method_list.html +708 -236
  45. data/doc/top-level-namespace.html +7 -7
  46. data/lib/term_utils/ap/article.rb +15 -9
  47. data/lib/term_utils/ap/flag.rb +37 -20
  48. data/lib/term_utils/ap/parameter.rb +88 -19
  49. data/lib/term_utils/ap/parser.rb +143 -116
  50. data/lib/term_utils/ap/result.rb +208 -161
  51. data/lib/term_utils/ap/syntax.rb +53 -69
  52. data/lib/term_utils/ap.rb +79 -24
  53. data/lib/term_utils/ff/config.rb +22 -10
  54. data/lib/term_utils/{ap/no_such_value_error.rb → ff/entry.rb} +26 -8
  55. data/lib/term_utils/ff/finder.rb +255 -0
  56. data/lib/term_utils/ff/query.rb +94 -17
  57. data/lib/term_utils/ff.rb +12 -2
  58. data/lib/term_utils/property_tree_node.rb +47 -19
  59. data/lib/term_utils/tab.rb +106 -61
  60. data/lib/term_utils.rb +8 -1
  61. data/term_utils.gemspec +4 -4
  62. metadata +18 -17
  63. data/doc/TermUtils/AP/Element.html +0 -1025
  64. data/doc/TermUtils/AP/Level.html +0 -638
  65. data/doc/TermUtils/AP/NoSuchValueError.html +0 -217
  66. data/lib/term_utils/ap/element.rb +0 -78
  67. data/lib/term_utils/ap/level.rb +0 -57
  68. data/lib/term_utils/ap/parse_error.rb +0 -27
  69. data/lib/term_utils/ap/syntax_error.rb +0 -27
  70. data/lib/term_utils/ff/cursor.rb +0 -153
@@ -6,15 +6,15 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.9.20
9
+ &mdash; Documentation by YARD 0.9.34
10
10
 
11
11
  </title>
12
12
 
13
- <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
14
 
15
- <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
16
 
17
- <script type="text/javascript" charset="utf-8">
17
+ <script type="text/javascript">
18
18
  pathId = "";
19
19
  relpath = '';
20
20
  </script>
@@ -100,9 +100,9 @@
100
100
  </div>
101
101
 
102
102
  <div id="footer">
103
- Generated on Fri Feb 7 18:54:38 2020 by
104
- <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
- 0.9.20 (ruby-2.6.5).
103
+ Generated on Wed Aug 9 17:34:25 2023 by
104
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.34 (ruby-3.2.2).
106
106
  </div>
107
107
 
108
108
  </div>
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,6 +15,7 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
20
  module AP
20
21
  # Represents a Article.
@@ -29,6 +30,7 @@ module TermUtils
29
30
  attr_accessor :type
30
31
  # @return [String] `%d`, `%s`.
31
32
  attr_accessor :format
33
+
32
34
  # Constructs a new Article.
33
35
  # @param opts [Hash]
34
36
  # @option opts [Symbol] :id
@@ -41,27 +43,31 @@ module TermUtils
41
43
  @min_occurs = opts.fetch(:min_occurs, 1)
42
44
  @max_occurs = opts.fetch(:max_occurs, 1)
43
45
  @type = opts.fetch(:type, :string)
44
- @format = opts.fetch(:format, "%s")
46
+ @format = opts.fetch(:format, '%s')
45
47
  end
48
+
46
49
  # Finalizes this one. Internal use.
47
50
  # @return [nil]
48
51
  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)
52
+ raise TermUtils::AP::SyntaxError, 'min_occurs must be equal or greater than 0' if !@min_occurs.is_a?(Integer) || (@min_occurs < 0)
53
+ raise TermUtils::AP::SyntaxError, 'max_occurs must be equal or greater than min_occurs' if occur_bounded? && (@max_occurs < @min_occurs)
54
+
51
55
  unless @id
52
56
  opts[:anonymous] += 1
53
57
  @id = "anonymous#{opts[:anonymous]}".intern
54
58
  end
55
59
  end
60
+
56
61
  # Tests whether this one has mutiple occurs.
57
62
  # @return [Boolean]
58
63
  def multiple_occurs?
59
- (@max_occurs == nil) || (@max_occurs == :infinity) || ((@max_occurs.is_a? Integer) && (@max_occurs > 1))
64
+ (@max_occurs == nil) || (@max_occurs.is_a?(Integer) && (@max_occurs > 1))
60
65
  end
61
- # Tests whether the number of occurs are fixed.
66
+
67
+ # Tests whether the number of occurs is fixed.
62
68
  # @return [Boolean]
63
69
  def occur_bounded?
64
- (@max_occurs != nil) && (@max_occurs != :infinity) && (@max_occurs.is_a? Integer)
70
+ (@max_occurs != nil) && @max_occurs.is_a?(Integer)
65
71
  end
66
72
  end
67
73
  end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,33 +15,50 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
20
  module AP
20
21
  # Represents a Flag.
21
22
  class Flag
22
23
  # @return [String]
23
- attr_accessor :label
24
- # @return [Symbol] `:anchor`, `:long`, `:short`.
25
- attr_accessor :flavor
24
+ attr_reader :label
25
+ # @return [Symbol] `:long`, `:short`.
26
+ attr_reader :flavor
27
+
26
28
  # 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)
29
+ # @param label [String]
30
+ # @param flavor [Symbol] `:short`, `:long`.
31
+ def initialize(label, flavor)
32
+ raise TermUtils::AP::SyntaxError, 'wrong flag label' if !label.is_a?(String) || /^-+$/.match?(label) || !/^-[0-9A-Za-z_-]+$/.match?(label)
33
+ raise TermUtils::AP::SyntaxError, '' unless %i[short long].include?(flavor)
34
+
35
+ @label = label
36
+ @flavor = flavor
37
+ end
38
+
39
+ # Tests whether this one is equal to a given Flag.
40
+ def ==(other)
41
+ return false unless other.is_a?(TermUtils::AP::Flag)
42
+
43
+ @label == other.label
44
+ end
45
+
46
+ # Tests whether this one represents a long flag.
47
+ # @return [Boolean]
48
+ def long?
49
+ @flavor == :long
50
+ end
51
+
52
+ # Tests whether this one represents a short flag.
53
+ # @return [Boolean]
54
+ def short?
55
+ @flavor == :short
33
56
  end
57
+
34
58
  # Returns the string representation of this one.
35
59
  # @return [String]
36
60
  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
61
+ @label
45
62
  end
46
63
  end
47
64
  end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,12 +15,22 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
20
  module AP
20
21
  # Represents a Parameter.
21
- class Parameter < TermUtils::AP::Element
22
- # @return [Array<TermUtils::AP::Article>]
22
+ class Parameter
23
+ # @return [Symbol]
24
+ attr_accessor :id
25
+ # @return [Integer]
26
+ attr_accessor :min_occurs
27
+ # @return [Integer]
28
+ attr_accessor :max_occurs
29
+ # @return [Array<Flag>]
30
+ attr_accessor :flags
31
+ # @return [Array<Article>]
23
32
  attr_accessor :articles
33
+
24
34
  # Constructs a new Parameter.
25
35
  # @param opts [Hash]
26
36
  # @option opts [Symbol] :id
@@ -33,42 +43,101 @@ module TermUtils
33
43
  @flags = []
34
44
  @articles = []
35
45
  end
46
+
36
47
  # For dup method.
37
48
  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
49
+ super(other)
50
+ @flags = other.flags.map(&:dup) if other.flags
51
+ @articles = other.articles.map(&:dup) if other.articles
45
52
  end
53
+
46
54
  # Finalizes this one. Internal use.
47
55
  # @return [nil]
56
+ # @raise [SyntaxError]
48
57
  def finalize!(opts = {})
49
- super
50
- @articles.each do |a|
51
- a.finalize!(opts)
58
+ raise TermUtils::AP::SyntaxError, 'min_occurs must be equal or greater than 0' if !@min_occurs.is_a?(Integer) || (@min_occurs < 0)
59
+ raise TermUtils::AP::SyntaxError, 'max_occurs must be equal or greater than min_occurs' if occur_bounded? && (@max_occurs < @min_occurs)
60
+ raise TermUtils::AP::SyntaxError, 'empty' if @flags.empty? && @articles.empty?
61
+
62
+ unless @id
63
+ opts[:anonymous] += 1
64
+ @id = "anonymous#{opts[:anonymous]}".intern
52
65
  end
66
+ @flags.each do |f|
67
+ raise TermUtils::AP::SyntaxError, 'duplicate flag label' if opts[:flag_labels].include?(f.to_s)
68
+
69
+ opts[:flag_labels] << f.to_s
70
+ end
71
+ @articles.each { |a| a.finalize!(opts) }
72
+ nil
73
+ end
74
+
75
+ # Tests whether this one has mutiple occurs.
76
+ # @return [Boolean]
77
+ def multiple_occurs?
78
+ (@max_occurs == nil) || (@max_occurs.is_a?(Integer) && (@max_occurs > 1))
79
+ end
80
+
81
+ # Tests whether the number of occurs is fixed.
82
+ # @return [Boolean]
83
+ def occur_bounded?
84
+ (@max_occurs != nil) && @max_occurs.is_a?(Integer)
53
85
  end
86
+
87
+ # Tests whether this one is flagged.
88
+ # @return [Boolean]
89
+ def flagged?
90
+ !@flags.empty?
91
+ end
92
+
93
+ # Adds a new Flag to this one.
94
+ # @param label [String]
95
+ # @return [Flag]
96
+ def define_flag(label, &block)
97
+ flavor = (label.length == 2) ? :short : :long
98
+ new_flag = TermUtils::AP::Flag.new(label, flavor)
99
+ raise TermUtils::AP::SyntaxError, 'duplicate flag label' if @flags.include? new_flag
100
+
101
+ @flags << new_flag
102
+ block.call(new_flag) if block
103
+ new_flag
104
+ end
105
+
54
106
  # Adds a new Article to this one.
107
+ # @param id [Symbol, nil]
55
108
  # @param opts [Hash]
56
109
  # @option opts [Symbol] :id
57
110
  # @option opts [Integer] :min_occurs Default value is `1`.
58
111
  # @option opts [Integer] :max_occurs Default value is `1`.
59
112
  # @option opts [Symbol] :type `:integer`, `:string`.
60
113
  # @option opts [Symbol] :format
61
- # @return [TermUtils::AP::Article]
62
- def define_article(opts = {}, &block)
114
+ # @return [Article]
115
+ def define_article(id = nil, opts = {}, &block)
116
+ if id
117
+ art = @articles.find { |p| p.id == id }
118
+ if art
119
+ block.call(art) if block
120
+ return art
121
+ end
122
+
123
+ opts[:id] = id
124
+ end
63
125
  new_article = TermUtils::AP::Article.new(opts)
64
126
  @articles << new_article
65
127
  block.call(new_article) if block
66
128
  new_article
67
129
  end
130
+
131
+ # Fetches all flags.
132
+ # @return [Array<Flag>]
133
+ def fetch_flags
134
+ @flags.dup
135
+ end
136
+
68
137
  # Fetches all articles.
69
- # @return [Array<TermUtils::AP::Article>]
138
+ # @return [Array<Article>]
70
139
  def fetch_articles
71
- @articles.collect { |a| a.dup }
140
+ @articles.collect(&:dup)
72
141
  end
73
142
  end
74
143
  end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,170 +15,197 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
20
  module AP
20
21
  # Represents the argument list parser.
21
22
  class Parser
23
+ # Constructs a new Parser.
22
24
  def initialize
23
25
  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
26
+
35
27
  # Parses a given list of arguments.
36
- # @param syntax [TermUtils::AP::Syntax]
28
+ # @param syntax [Syntax]
37
29
  # @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 = {})
30
+ # @param opts [Hash<Symbol, Object>]
31
+ # @option opts [Boolean] :strict Whether the Syntax must be considered as strict.
32
+ # @return [Result]
33
+ # @raise [ParseError]
34
+ # @raise [SyntaxError]
35
+ def parse_arguments(syntax, arguments, opts = {}, &block)
44
36
  syntax = syntax.dup
45
37
  syntax.finalize!
46
38
  arguments = arguments.dup
47
39
  res = TermUtils::AP::Result.new(syntax)
48
40
  catch :done do
49
- parse0(res.value, syntax, arguments, opts)
41
+ parse0(res, syntax, arguments, opts)
50
42
  end
43
+ res.remaining_arguments = arguments
44
+ res.walk(&block) if block
51
45
  res
52
46
  end
47
+
48
+ # Tests whether a given sample matches a shortcut flag.
49
+ # @param shortcut_flags [Hash<String, Flag>]
50
+ # @param arg [String]
51
+ # @return [Array<String>, nil] Replacements on success, nil otherwise.
52
+ def self.match_shortcut_flag(shortcut_flags, arg)
53
+ shortcut_flags.each do |label, flag|
54
+ next unless arg.start_with? label
55
+
56
+ return [flag.label, arg[label.length..]]
57
+ end
58
+
59
+ nil
60
+ end
61
+
62
+ # Evaluates the added number of min occurs of a given array of articles.
63
+ # @param articles [Array<TermUtils::AP::Article>]
64
+ # @return [Integer]
65
+ def self.eval_article_min_occurs(articles)
66
+ articles.inject(0) { |acc, a| acc + a.min_occurs }
67
+ end
68
+
53
69
  private
70
+
54
71
  # Parses a given argument list.
55
- # @param node [TermUtils::PropertyTreeNode]
56
- # @param syntax [TermUtils::AP::Syntax]
72
+ # @param result [Result]
73
+ # @param syntax [Syntax]
57
74
  # @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
75
+ # @raise [ParseError]
76
+ def parse0(result, syntax, arguments, opts = {})
77
+ unflagged_params, flagged_params, shortcut_flags = syntax.fetch_parameters
67
78
  fp_occ = {}
68
- syntax.elements.each { |e| fp_occ[e.id] = 0 if e.flagged? }
79
+ syntax.parameters.each { |p| fp_occ[p.id] = 0 if p.flagged? }
69
80
  up_occ = 0
70
81
  loop do
71
82
  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)
83
+
84
+ if arguments.first.start_with?('-') && !%w[- --].include?(arguments.first)
85
+ # Flagged
86
+ unless flagged_params.key? arguments.first
87
+ # Unknown flag
88
+ # Try shortcut flag
89
+ flag, arg = self.class.match_shortcut_flag(shortcut_flags, arguments.first)
90
+ if flag && arg
91
+ # Shortcut match
92
+ arguments.shift
93
+ arguments.unshift arg
94
+ arguments.unshift flag
93
95
  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
96
  end
97
+ unless flagged_params.key? arguments.first
98
+ # Unknown flag
99
+ # End of parsing
100
+ raise TermUtils::AP::ParseError.new(message: 'flagged parameter unexpected', fault: arguments.first) if opts.fetch(:strict, false)
101
+
102
+ break
103
+ end
104
+
105
+ param = flagged_params[arguments.first]
106
+ if param.occur_bounded? && (fp_occ[param.id] >= param.max_occurs)
107
+ # Max occurs reached
108
+ raise TermUtils::AP::ParseError.new(message: 'occur limit reached', parameter: param.id, fault: arguments.first) if opts.fetch(:strict, false)
109
+
110
+ break
111
+ end
112
+
113
+ fp_occ[param.id] += 1
114
+ arguments.shift
115
+ param_res = TermUtils::AP::ParameterResult.new(result, param)
116
+ parse0_param(param_res, param, arguments)
107
117
  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
+ # Unflagged
119
+ if unflagged_params.empty?
120
+ # End of parsing
121
+ raise TermUtils::AP::ParseError.new(message: 'unflagged parameter unexpected', fault: arguments.first) if opts.fetch(:strict, false)
122
+
123
+ break
118
124
  end
119
- catch :param_done do
120
- occ = up_occ
125
+
126
+ param = unflagged_params.first
127
+ if arguments.first == '--'
128
+ # End of parameter
129
+ raise TermUtils::AP::ParseError.new(message: 'parameter not consumed', parameter: param.id) if up_occ < param.min_occurs
130
+
131
+ arguments.shift
132
+ unflagged_params.shift
121
133
  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
134
+ next
135
+ end
136
+
137
+ up_occ += 1
138
+ param_res = TermUtils::AP::ParameterResult.new(result, param)
139
+ case parse0_param(param_res, param, arguments)
140
+ when :esc_param
141
+ raise TermUtils::AP::ParseError.new(message: 'parameter not consumed', parameter: param.id) if up_occ < param.min_occurs
142
+
143
+ unflagged_params.shift
144
+ up_occ = 0
145
+ else
146
+ if !param.multiple_occurs? || (param.occur_bounded? && (up_occ >= param.max_occurs))
147
+ unflagged_params.shift
128
148
  up_occ = 0
129
149
  end
130
150
  end
131
151
  end
132
152
  end # loop
153
+ # Check min occurs
154
+ syntax.parameters.each do |p|
155
+ next if result.find_parameters(p.id).length >= p.min_occurs
156
+
157
+ raise TermUtils::AP::ParseError.new(message: 'parameter not consumed', parameter: p.id)
158
+ end
133
159
  end
134
- def parse0_param(node, param, arguments, opts = {})
160
+
161
+ # Parses with a given Parameter.
162
+ # @param result [ParameterResult]
163
+ # @param param [Parameter]
164
+ # @param arguments [Array<String>]
165
+ # @return [Symbol, nil] `:esc_param`, or nil.
166
+ def parse0_param(result, param, arguments)
135
167
  arts = param.fetch_articles
136
168
  occ = 0
137
169
  loop do
170
+ break if arts.empty?
171
+
138
172
  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
173
+ # End of arguments
174
+ raise TermUtils::AP::ParseError.new(message: 'article not consumed', parameter: param.id) if occ < arts.first.min_occurs
175
+ raise TermUtils::AP::ParseError.new(message: 'article not consumed', parameter: param.id) if self.class.eval_article_min_occurs(arts[1..]) > 0
176
+
177
+ break
142
178
  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)
179
+
180
+ if arguments.first == '-'
181
+ # End of article
182
+ raise TermUtils::AP::ParseError.new(message: 'article not consumed', parameter: param.id) if occ < arts.first.min_occurs
183
+
154
184
  arguments.shift
155
185
  arts.shift
156
186
  occ = 0
157
187
  next
188
+ elsif arguments.first.start_with? '-'
189
+ # End of parameter
190
+ raise TermUtils::AP::ParseError.new(message: 'article not consumed', parameter: param.id) if occ < arts.first.min_occurs
191
+ raise TermUtils::AP::ParseError.new(message: 'article not consumed', parameter: param.id) if self.class.eval_article_min_occurs(arts[1..]) > 0
192
+
193
+ if arguments.first == '--'
194
+ arguments.shift
195
+ return :esc_param
196
+ end
197
+
198
+ break
158
199
  end
159
200
  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
201
+ TermUtils::AP::ArticleResult.new(result, art, arguments.shift)
168
202
  occ += 1
169
203
  if !art.multiple_occurs? || (art.occur_bounded? && (occ >= art.max_occurs))
170
204
  arts.shift
171
205
  occ = 0
172
206
  end
173
207
  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
208
+ nil
182
209
  end
183
210
  end
184
211
  end