term_utils 0.3.2 → 0.4.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 (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