term_utils 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -2
  3. data/COPYING +3 -3
  4. data/README.md +51 -16
  5. data/doc/TermUtils.html +9 -9
  6. data/doc/TermUtils/AP.html +48 -159
  7. data/doc/TermUtils/AP/Article.html +48 -46
  8. data/doc/TermUtils/AP/ArticleResult.html +584 -0
  9. data/doc/TermUtils/AP/Flag.html +294 -77
  10. data/doc/TermUtils/AP/NoSuchValueError.html +13 -13
  11. data/doc/TermUtils/AP/Parameter.html +885 -97
  12. data/doc/TermUtils/AP/ParameterResult.html +980 -0
  13. data/doc/TermUtils/{FF/Cursor/Context.html → AP/ParameterWalkerHooks.html} +59 -59
  14. data/doc/TermUtils/AP/ParseError.html +13 -13
  15. data/doc/TermUtils/AP/Parser.html +174 -142
  16. data/doc/TermUtils/AP/Result.html +200 -527
  17. data/doc/TermUtils/AP/Syntax.html +102 -392
  18. data/doc/TermUtils/AP/SyntaxError.html +13 -13
  19. data/doc/TermUtils/AP/Walker.html +686 -0
  20. data/doc/TermUtils/FF.html +10 -10
  21. data/doc/TermUtils/FF/Config.html +201 -33
  22. data/doc/TermUtils/FF/Context.html +585 -0
  23. data/doc/TermUtils/FF/Entry.html +626 -0
  24. data/doc/TermUtils/FF/Query.html +402 -66
  25. data/doc/TermUtils/PropertyTreeNode.html +302 -188
  26. data/doc/TermUtils/Tab.html +90 -83
  27. data/doc/TermUtils/Tab/Column.html +94 -92
  28. data/doc/TermUtils/Tab/Header.html +26 -26
  29. data/doc/TermUtils/Tab/Holder.html +74 -74
  30. data/doc/TermUtils/Tab/Printer.html +42 -42
  31. data/doc/TermUtils/Tab/Table.html +124 -128
  32. data/doc/TermUtils/Tab/TableError.html +11 -11
  33. data/doc/_index.html +48 -34
  34. data/doc/class_list.html +3 -3
  35. data/doc/css/style.css +2 -2
  36. data/doc/file.README.html +64 -31
  37. data/doc/file_list.html +2 -2
  38. data/doc/frames.html +2 -2
  39. data/doc/index.html +64 -31
  40. data/doc/js/app.js +14 -3
  41. data/doc/method_list.html +387 -211
  42. data/doc/top-level-namespace.html +6 -6
  43. data/lib/term_utils.rb +8 -1
  44. data/lib/term_utils/ap.rb +41 -30
  45. data/lib/term_utils/ap/article.rb +14 -8
  46. data/lib/term_utils/ap/flag.rb +36 -19
  47. data/lib/term_utils/ap/parameter.rb +87 -18
  48. data/lib/term_utils/ap/parser.rb +141 -115
  49. data/lib/term_utils/ap/result.rb +207 -160
  50. data/lib/term_utils/ap/syntax.rb +52 -68
  51. data/lib/term_utils/ff.rb +11 -2
  52. data/lib/term_utils/ff/config.rb +20 -8
  53. data/lib/term_utils/{ap/no_such_value_error.rb → ff/entry.rb} +25 -7
  54. data/lib/term_utils/ff/query.rb +93 -14
  55. data/lib/term_utils/property_tree_node.rb +47 -19
  56. data/lib/term_utils/tab.rb +102 -58
  57. data/term_utils.gemspec +4 -4
  58. metadata +12 -14
  59. data/doc/TermUtils/AP/Element.html +0 -1025
  60. data/doc/TermUtils/AP/Level.html +0 -638
  61. data/doc/TermUtils/FF/Cursor.html +0 -929
  62. data/lib/term_utils/ap/element.rb +0 -78
  63. data/lib/term_utils/ap/level.rb +0 -57
  64. data/lib/term_utils/ap/parse_error.rb +0 -27
  65. data/lib/term_utils/ap/syntax_error.rb +0 -27
  66. 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.25
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
103
+ Generated on Sun Aug 2 18:35:09 2020 by
104
104
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
- 0.9.20 (ruby-2.6.5).
105
+ 0.9.25 (ruby-2.6.5).
106
106
  </div>
107
107
 
108
108
  </div>
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen-string-literal: true
2
+
3
+ # Copyright (C) 2020 Thomas Baron
2
4
  #
3
5
  # This file is part of term_utils.
4
6
  #
@@ -13,6 +15,11 @@
13
15
  #
14
16
  # You should have received a copy of the GNU General Public License
15
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ # Utilities for terminal application.
20
+ module TermUtils
21
+ end
22
+
16
23
  require 'term_utils/ap'
17
24
  require 'term_utils/ff'
18
25
  require 'term_utils/tab'
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen-string-literal: true
2
+
3
+ # Copyright (C) 2020 Thomas Baron
2
4
  #
3
5
  # This file is part of term_utils.
4
6
  #
@@ -13,21 +15,31 @@
13
15
  #
14
16
  # You should have received a copy of the GNU General Public License
15
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
16
- require 'term_utils/property_tree_node'
17
- require 'term_utils/ap/no_such_value_error'
18
- require 'term_utils/ap/parse_error'
19
- require 'term_utils/ap/syntax_error'
20
- require 'term_utils/ap/article'
21
- require 'term_utils/ap/flag'
22
- require 'term_utils/ap/element'
23
- require 'term_utils/ap/level'
24
- require 'term_utils/ap/parameter'
25
- require 'term_utils/ap/syntax'
26
- require 'term_utils/ap/result'
27
- require 'term_utils/ap/parser'
18
+
28
19
  module TermUtils
29
- # The Argument Parser module provides a way to parse command arguments.
20
+ # The Argument Parser module provides a way to parse arguments.
30
21
  module AP
22
+ # NoSuchValueError.
23
+ class NoSuchValueError < StandardError
24
+ def initialize(msg)
25
+ super
26
+ end
27
+ end
28
+
29
+ # ParseError.
30
+ class ParseError < StandardError
31
+ def initialize(msg)
32
+ super
33
+ end
34
+ end
35
+
36
+ # SyntaxError.
37
+ class SyntaxError < StandardError
38
+ def initialize(msg)
39
+ super
40
+ end
41
+ end
42
+
31
43
  # Creates a new Syntax.
32
44
  # @return [TermUtils::AP::Syntax]
33
45
  def self.create_syntax(&block)
@@ -35,25 +47,24 @@ module TermUtils
35
47
  block.call(new_syntax) if block
36
48
  new_syntax
37
49
  end
38
- # Parses a given command.
39
- # @param syntax [TermUtils::AP::Syntax]
40
- # @param command [String]
41
- # @param opts [Hash]
42
- # @return [TermUtils::AP::Result]
43
- # @raise [TermUtils::AP::ParseError]
44
- # @raise [TermUtils::AP::SyntaxError]
45
- def self.parse_command(syntax, command, opts = {})
46
- TermUtils::AP::Parser.new.parse_command(syntax, command, opts)
47
- end
50
+
48
51
  # Parses a given list of arguments.
49
- # @param syntax [TermUtils::AP::Syntax]
52
+ # @param syntax [Syntax]
50
53
  # @param arguments [Array<String>]
51
54
  # @param opts [Hash]
52
- # @return [TermUtils::AP::Result]
53
- # @raise [TermUtils::AP::ParseError]
54
- # @raise [TermUtils::AP::SyntaxError]
55
- def self.parse_arguments(syntax, arguments, opts = {})
56
- TermUtils::AP::Parser.new.parse_arguments(syntax, arguments, opts)
55
+ # @option opts [Boolean] :strict Whether the Syntax must be considered strict.
56
+ # @return [Result]
57
+ # @raise [ParseError]
58
+ # @raise [SyntaxError]
59
+ def self.parse_arguments(syntax, arguments, opts = {}, &block)
60
+ TermUtils::AP::Parser.new.parse_arguments(syntax, arguments, opts, &block)
57
61
  end
58
62
  end
59
63
  end
64
+
65
+ require 'term_utils/ap/article'
66
+ require 'term_utils/ap/flag'
67
+ require 'term_utils/ap/parameter'
68
+ require 'term_utils/ap/syntax'
69
+ require 'term_utils/ap/result'
70
+ require 'term_utils/ap/parser'
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
2
+
3
+ # Copyright (C) 2020 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
1
  # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
2
+
3
+ # Copyright (C) 2020 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
1
  # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
2
+
3
+ # Copyright (C) 2020 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
1
  # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
2
+
3
+ # Copyright (C) 2020 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,170 +15,196 @@
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]
31
+ # @return [Result]
32
+ # @raise [ParseError]
33
+ # @raise [SyntaxError]
34
+ def parse_arguments(syntax, arguments, opts = {}, &block)
44
35
  syntax = syntax.dup
45
36
  syntax.finalize!
46
37
  arguments = arguments.dup
47
38
  res = TermUtils::AP::Result.new(syntax)
48
39
  catch :done do
49
- parse0(res.value, syntax, arguments, opts)
40
+ parse0(res, syntax, arguments, opts)
50
41
  end
42
+ res.remaining_arguments = arguments
43
+ res.walk(&block) if block
51
44
  res
52
45
  end
46
+
47
+ # Tests whether a given sample matches a shortcut flag.
48
+ # @param shortcut_flags [Hash<String, Flag>]
49
+ # @param arg [String]
50
+ # @return [Array<String>, nil] Replacements on success, nil otherwise.
51
+ def self.match_shortcut_flag(shortcut_flags, arg)
52
+ shortcut_flags.each do |label, flag|
53
+ next unless arg.start_with? label
54
+
55
+ return [flag.label, arg[label.length..-1]]
56
+ end
57
+
58
+ nil
59
+ end
60
+
61
+ # Evaluates the added number of min occurs of a given array of articles.
62
+ # @param articles [Array<TermUtils::AP::Article>]
63
+ # @return [Integer]
64
+ def self.eval_article_min_occurs(articles)
65
+ articles.inject(0) { |acc, a| acc + a.min_occurs }
66
+ end
67
+
53
68
  private
69
+
54
70
  # Parses a given argument list.
55
- # @param node [TermUtils::PropertyTreeNode]
56
- # @param syntax [TermUtils::AP::Syntax]
71
+ # @param result [Result]
72
+ # @param syntax [Syntax]
57
73
  # @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
74
+ # @raise [ParseError]
75
+ def parse0(result, syntax, arguments, opts = {})
76
+ unflagged_params, flagged_params, shortcut_flags = syntax.fetch_parameters
67
77
  fp_occ = {}
68
- syntax.elements.each { |e| fp_occ[e.id] = 0 if e.flagged? }
78
+ syntax.parameters.each { |p| fp_occ[p.id] = 0 if p.flagged? }
69
79
  up_occ = 0
70
80
  loop do
71
81
  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)
82
+
83
+ if arguments.first.start_with?('-') && !%w[- --].include?(arguments.first)
84
+ # Flagged
85
+ unless flagged_params.key? arguments.first
86
+ # Unknown flag
87
+ # Try shortcut flag
88
+ flag, arg = self.class.match_shortcut_flag(shortcut_flags, arguments.first)
89
+ if flag && arg
90
+ # Shortcut match
91
+ arguments.shift
92
+ arguments.unshift arg
93
+ arguments.unshift flag
93
94
  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
95
  end
96
+ unless flagged_params.key? arguments.first
97
+ # Unknown flag
98
+ # End of parsing
99
+ raise TermUtils::AP::ParseError, 'flagged parameter unexpected' if opts.fetch(:strict, false)
100
+
101
+ break
102
+ end
103
+
104
+ param = flagged_params[arguments.first]
105
+ if param.occur_bounded? && (fp_occ[param.id] >= param.max_occurs)
106
+ # Max occurs reached
107
+ raise TermUtils::AP::ParseError, "occur limit reached (#{param.id})" if opts.fetch(:strict, false)
108
+
109
+ break
110
+ end
111
+
112
+ fp_occ[param.id] += 1
113
+ arguments.shift
114
+ param_res = TermUtils::AP::ParameterResult.new(result, param)
115
+ parse0_param(param_res, param, arguments)
107
116
  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)
117
+ # Unflagged
118
+ if unflagged_params.empty?
119
+ # End of parsing
120
+ raise TermUtils::AP::ParseError, 'unflagged parameter unexpected' if opts.fetch(:strict, false)
121
+
122
+ break
118
123
  end
119
- catch :param_done do
120
- occ = up_occ
124
+
125
+ param = unflagged_params.first
126
+ if arguments.first == '--'
127
+ # End of parameter
128
+ raise TermUtils::AP::ParseError, "parameter not consumed (#{param.id})" if up_occ < param.min_occurs
129
+
130
+ arguments.shift
131
+ unflagged_params.shift
121
132
  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
133
+ next
134
+ end
135
+
136
+ up_occ += 1
137
+ param_res = TermUtils::AP::ParameterResult.new(result, param)
138
+ case parse0_param(param_res, param, arguments)
139
+ when :esc_param
140
+ raise TermUtils::AP::ParseError, "parameter not consumed (#{param.id})" if up_occ < param.min_occurs
141
+
142
+ unflagged_params.shift
143
+ up_occ = 0
144
+ else
145
+ if !param.multiple_occurs? || (param.occur_bounded? && (up_occ >= param.max_occurs))
146
+ unflagged_params.shift
128
147
  up_occ = 0
129
148
  end
130
149
  end
131
150
  end
132
151
  end # loop
152
+ # Check min occurs
153
+ syntax.parameters.each do |p|
154
+ next if result.find_parameters(p.id).length >= p.min_occurs
155
+
156
+ raise TermUtils::AP::ParseError, "parameter not consumed (#{p.id})"
157
+ end
133
158
  end
134
- def parse0_param(node, param, arguments, opts = {})
159
+
160
+ # Parses with a given Parameter.
161
+ # @param result [ParameterResult]
162
+ # @param param [Parameter]
163
+ # @param arguments [Array<String>]
164
+ # @return [Symbol, nil] `:esc_param`, or nil.
165
+ def parse0_param(result, param, arguments)
135
166
  arts = param.fetch_articles
136
167
  occ = 0
137
168
  loop do
169
+ break if arts.empty?
170
+
138
171
  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
172
+ # End of arguments
173
+ raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs
174
+ raise TermUtils::AP::ParseError, 'article not consumed' if self.class.eval_article_min_occurs(arts[1..-1]) > 0
175
+
176
+ break
142
177
  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)
178
+
179
+ if arguments.first == '-'
180
+ # End of article
181
+ raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs
182
+
154
183
  arguments.shift
155
184
  arts.shift
156
185
  occ = 0
157
186
  next
187
+ elsif arguments.first.start_with? '-'
188
+ # End of parameter
189
+ raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs
190
+ raise TermUtils::AP::ParseError, 'article not consumed' if self.class.eval_article_min_occurs(arts[1..-1]) > 0
191
+
192
+ if arguments.first == '--'
193
+ arguments.shift
194
+ return :esc_param
195
+ end
196
+
197
+ break
158
198
  end
159
199
  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
200
+ TermUtils::AP::ArticleResult.new(result, art, arguments.shift)
168
201
  occ += 1
169
202
  if !art.multiple_occurs? || (art.occur_bounded? && (occ >= art.max_occurs))
170
203
  arts.shift
171
204
  occ = 0
172
205
  end
173
206
  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
207
+ nil
182
208
  end
183
209
  end
184
210
  end