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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -2
- data/COPYING +3 -3
- data/README.md +51 -16
- data/doc/TermUtils.html +9 -9
- data/doc/TermUtils/AP.html +48 -159
- data/doc/TermUtils/AP/Article.html +48 -46
- data/doc/TermUtils/AP/ArticleResult.html +584 -0
- data/doc/TermUtils/AP/Flag.html +294 -77
- data/doc/TermUtils/AP/NoSuchValueError.html +13 -13
- data/doc/TermUtils/AP/Parameter.html +885 -97
- data/doc/TermUtils/AP/ParameterResult.html +980 -0
- data/doc/TermUtils/{FF/Cursor/Context.html → AP/ParameterWalkerHooks.html} +59 -59
- data/doc/TermUtils/AP/ParseError.html +13 -13
- data/doc/TermUtils/AP/Parser.html +174 -142
- data/doc/TermUtils/AP/Result.html +200 -527
- data/doc/TermUtils/AP/Syntax.html +102 -392
- data/doc/TermUtils/AP/SyntaxError.html +13 -13
- data/doc/TermUtils/AP/Walker.html +686 -0
- data/doc/TermUtils/FF.html +10 -10
- data/doc/TermUtils/FF/Config.html +201 -33
- data/doc/TermUtils/FF/Context.html +585 -0
- data/doc/TermUtils/FF/Entry.html +626 -0
- data/doc/TermUtils/FF/Query.html +402 -66
- data/doc/TermUtils/PropertyTreeNode.html +302 -188
- data/doc/TermUtils/Tab.html +90 -83
- data/doc/TermUtils/Tab/Column.html +94 -92
- data/doc/TermUtils/Tab/Header.html +26 -26
- data/doc/TermUtils/Tab/Holder.html +74 -74
- data/doc/TermUtils/Tab/Printer.html +42 -42
- data/doc/TermUtils/Tab/Table.html +124 -128
- data/doc/TermUtils/Tab/TableError.html +11 -11
- data/doc/_index.html +48 -34
- data/doc/class_list.html +3 -3
- data/doc/css/style.css +2 -2
- data/doc/file.README.html +64 -31
- data/doc/file_list.html +2 -2
- data/doc/frames.html +2 -2
- data/doc/index.html +64 -31
- data/doc/js/app.js +14 -3
- data/doc/method_list.html +387 -211
- data/doc/top-level-namespace.html +6 -6
- data/lib/term_utils.rb +8 -1
- data/lib/term_utils/ap.rb +41 -30
- data/lib/term_utils/ap/article.rb +14 -8
- data/lib/term_utils/ap/flag.rb +36 -19
- data/lib/term_utils/ap/parameter.rb +87 -18
- data/lib/term_utils/ap/parser.rb +141 -115
- data/lib/term_utils/ap/result.rb +207 -160
- data/lib/term_utils/ap/syntax.rb +52 -68
- data/lib/term_utils/ff.rb +11 -2
- data/lib/term_utils/ff/config.rb +20 -8
- data/lib/term_utils/{ap/no_such_value_error.rb → ff/entry.rb} +25 -7
- data/lib/term_utils/ff/query.rb +93 -14
- data/lib/term_utils/property_tree_node.rb +47 -19
- data/lib/term_utils/tab.rb +102 -58
- data/term_utils.gemspec +4 -4
- metadata +12 -14
- data/doc/TermUtils/AP/Element.html +0 -1025
- data/doc/TermUtils/AP/Level.html +0 -638
- data/doc/TermUtils/FF/Cursor.html +0 -929
- data/lib/term_utils/ap/element.rb +0 -78
- data/lib/term_utils/ap/level.rb +0 -57
- data/lib/term_utils/ap/parse_error.rb +0 -27
- data/lib/term_utils/ap/syntax_error.rb +0 -27
- data/lib/term_utils/ff/cursor.rb +0 -153
@@ -6,15 +6,15 @@
|
|
6
6
|
<title>
|
7
7
|
Top Level Namespace
|
8
8
|
|
9
|
-
— Documentation by YARD 0.9.
|
9
|
+
— Documentation by YARD 0.9.25
|
10
10
|
|
11
11
|
</title>
|
12
12
|
|
13
|
-
<link rel="stylesheet" href="css/style.css" type="text/css"
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
14
14
|
|
15
|
-
<link rel="stylesheet" href="css/common.css" type="text/css"
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
16
16
|
|
17
|
-
<script type="text/javascript"
|
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
|
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.
|
105
|
+
0.9.25 (ruby-2.6.5).
|
106
106
|
</div>
|
107
107
|
|
108
108
|
</div>
|
data/lib/term_utils.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
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'
|
data/lib/term_utils/ap.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
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
|
-
|
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
|
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
|
-
|
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 [
|
52
|
+
# @param syntax [Syntax]
|
50
53
|
# @param arguments [Array<String>]
|
51
54
|
# @param opts [Hash]
|
52
|
-
# @
|
53
|
-
# @
|
54
|
-
# @raise [
|
55
|
-
|
56
|
-
|
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)
|
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,
|
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,
|
50
|
-
raise TermUtils::AP::SyntaxError,
|
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
|
64
|
+
(@max_occurs == nil) || (@max_occurs.is_a?(Integer) && (@max_occurs > 1))
|
60
65
|
end
|
61
|
-
|
66
|
+
|
67
|
+
# Tests whether the number of occurs is fixed.
|
62
68
|
# @return [Boolean]
|
63
69
|
def occur_bounded?
|
64
|
-
(@max_occurs != nil) &&
|
70
|
+
(@max_occurs != nil) && @max_occurs.is_a?(Integer)
|
65
71
|
end
|
66
72
|
end
|
67
73
|
end
|
data/lib/term_utils/ap/flag.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
|
-
|
3
|
-
# Copyright (C)
|
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
|
-
|
24
|
-
# @return [Symbol] `:
|
25
|
-
|
24
|
+
attr_reader :label
|
25
|
+
# @return [Symbol] `:long`, `:short`.
|
26
|
+
attr_reader :flavor
|
27
|
+
|
26
28
|
# Constructs a new Flag.
|
27
|
-
# @param
|
28
|
-
# @
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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)
|
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
|
22
|
-
# @return [
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
50
|
-
@
|
51
|
-
|
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 [
|
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<
|
138
|
+
# @return [Array<Article>]
|
70
139
|
def fetch_articles
|
71
|
-
@articles.collect
|
140
|
+
@articles.collect(&:dup)
|
72
141
|
end
|
73
142
|
end
|
74
143
|
end
|
data/lib/term_utils/ap/parser.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
|
-
|
3
|
-
# Copyright (C)
|
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
|
-
|
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 [
|
28
|
+
# @param syntax [Syntax]
|
37
29
|
# @param arguments [Array<String>]
|
38
|
-
# @param opts [Hash]
|
39
|
-
# @
|
40
|
-
# @
|
41
|
-
# @raise [
|
42
|
-
|
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
|
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
|
56
|
-
# @param syntax [
|
71
|
+
# @param result [Result]
|
72
|
+
# @param syntax [Syntax]
|
57
73
|
# @param arguments [Array<String>]
|
58
|
-
|
59
|
-
|
60
|
-
|
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.
|
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
|
-
|
73
|
-
if arguments.first
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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
|
-
|
140
|
-
raise TermUtils::AP::ParseError,
|
141
|
-
|
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
|
-
|
144
|
-
if arguments.first ==
|
145
|
-
# End of
|
146
|
-
raise TermUtils::AP::ParseError,
|
147
|
-
|
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
|
-
|
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
|
-
|
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
|