term_utils 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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