term_utils 0.1.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHORS +1 -0
  3. data/CHANGELOG.md +51 -0
  4. data/COPYING +674 -0
  5. data/README.md +99 -0
  6. data/Rakefile +55 -0
  7. data/doc/TermUtils.html +138 -0
  8. data/doc/TermUtils/AP.html +394 -0
  9. data/doc/TermUtils/AP/Article.html +993 -0
  10. data/doc/TermUtils/AP/ArticleResult.html +584 -0
  11. data/doc/TermUtils/AP/Flag.html +756 -0
  12. data/doc/TermUtils/AP/NoSuchValueError.html +217 -0
  13. data/doc/TermUtils/AP/Parameter.html +1592 -0
  14. data/doc/TermUtils/AP/ParameterResult.html +980 -0
  15. data/doc/TermUtils/AP/ParameterWalkerHooks.html +409 -0
  16. data/doc/TermUtils/AP/ParseError.html +217 -0
  17. data/doc/TermUtils/AP/Parser.html +604 -0
  18. data/doc/TermUtils/AP/Result.html +837 -0
  19. data/doc/TermUtils/AP/Syntax.html +761 -0
  20. data/doc/TermUtils/AP/SyntaxError.html +217 -0
  21. data/doc/TermUtils/AP/Walker.html +686 -0
  22. data/doc/TermUtils/FF.html +128 -0
  23. data/doc/TermUtils/FF/Config.html +774 -0
  24. data/doc/TermUtils/FF/Context.html +585 -0
  25. data/doc/TermUtils/FF/Entry.html +626 -0
  26. data/doc/TermUtils/FF/Query.html +1085 -0
  27. data/doc/TermUtils/PropertyTreeNode.html +2113 -0
  28. data/doc/TermUtils/Tab.html +1486 -0
  29. data/doc/TermUtils/Tab/Column.html +1263 -0
  30. data/doc/TermUtils/Tab/Header.html +536 -0
  31. data/doc/TermUtils/Tab/Holder.html +1210 -0
  32. data/doc/TermUtils/Tab/Printer.html +967 -0
  33. data/doc/TermUtils/Tab/Table.html +1966 -0
  34. data/doc/TermUtils/Tab/TableError.html +217 -0
  35. data/doc/_index.html +387 -0
  36. data/doc/class_list.html +51 -0
  37. data/doc/css/common.css +1 -0
  38. data/doc/css/full_list.css +58 -0
  39. data/doc/css/style.css +496 -0
  40. data/doc/file.README.html +170 -0
  41. data/doc/file_list.html +56 -0
  42. data/doc/frames.html +17 -0
  43. data/doc/index.html +170 -0
  44. data/doc/js/app.js +314 -0
  45. data/doc/js/full_list.js +216 -0
  46. data/doc/js/jquery.js +4 -0
  47. data/doc/method_list.html +1539 -0
  48. data/doc/top-level-namespace.html +110 -0
  49. data/lib/term_utils.rb +11 -0
  50. data/lib/term_utils/ap.rb +70 -0
  51. data/lib/term_utils/ap/article.rb +74 -0
  52. data/lib/term_utils/ap/flag.rb +65 -0
  53. data/lib/term_utils/ap/parameter.rb +144 -0
  54. data/lib/term_utils/ap/parser.rb +211 -0
  55. data/lib/term_utils/ap/result.rb +244 -0
  56. data/lib/term_utils/ap/syntax.rb +96 -0
  57. data/lib/term_utils/ff.rb +27 -0
  58. data/lib/term_utils/ff/config.rb +55 -0
  59. data/lib/term_utils/ff/entry.rb +45 -0
  60. data/lib/term_utils/ff/query.rb +145 -0
  61. data/lib/term_utils/property_tree_node.rb +228 -0
  62. data/lib/term_utils/tab.rb +338 -88
  63. data/term_utils.gemspec +16 -0
  64. metadata +69 -7
@@ -0,0 +1,211 @@
1
+ # frozen-string-literal: true
2
+
3
+ # Copyright (C) 2020 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ module TermUtils
20
+ module AP
21
+ # Represents the argument list parser.
22
+ class Parser
23
+ # Constructs a new Parser.
24
+ def initialize
25
+ end
26
+
27
+ # Parses a given list of arguments.
28
+ # @param syntax [Syntax]
29
+ # @param arguments [Array<String>]
30
+ # @param opts [Hash]
31
+ # @return [Result]
32
+ # @raise [ParseError]
33
+ # @raise [SyntaxError]
34
+ def parse_arguments(syntax, arguments, opts = {}, &block)
35
+ syntax = syntax.dup
36
+ syntax.finalize!
37
+ arguments = arguments.dup
38
+ res = TermUtils::AP::Result.new(syntax)
39
+ catch :done do
40
+ parse0(res, syntax, arguments, opts)
41
+ end
42
+ res.remaining_arguments = arguments
43
+ res.walk(&block) if block
44
+ res
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
+
68
+ private
69
+
70
+ # Parses a given argument list.
71
+ # @param result [Result]
72
+ # @param syntax [Syntax]
73
+ # @param arguments [Array<String>]
74
+ # @raise [ParseError]
75
+ def parse0(result, syntax, arguments, opts = {})
76
+ unflagged_params, flagged_params, shortcut_flags = syntax.fetch_parameters
77
+ fp_occ = {}
78
+ syntax.parameters.each { |p| fp_occ[p.id] = 0 if p.flagged? }
79
+ up_occ = 0
80
+ loop do
81
+ break if arguments.empty?
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
94
+ end
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)
116
+ else
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
123
+ end
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
132
+ up_occ = 0
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
147
+ up_occ = 0
148
+ end
149
+ end
150
+ end
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
158
+ end
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)
166
+ arts = param.fetch_articles
167
+ occ = 0
168
+ loop do
169
+ break if arts.empty?
170
+
171
+ if arguments.empty?
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
177
+ end
178
+
179
+ if arguments.first == '-'
180
+ # End of article
181
+ raise TermUtils::AP::ParseError, 'article not consumed' if occ < arts.first.min_occurs
182
+
183
+ arguments.shift
184
+ arts.shift
185
+ occ = 0
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
198
+ end
199
+ art = arts.first
200
+ TermUtils::AP::ArticleResult.new(result, art, arguments.shift)
201
+ occ += 1
202
+ if !art.multiple_occurs? || (art.occur_bounded? && (occ >= art.max_occurs))
203
+ arts.shift
204
+ occ = 0
205
+ end
206
+ end # loop
207
+ nil
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,244 @@
1
+ # frozen-string-literal: true
2
+
3
+ # Copyright (C) 2020 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ module TermUtils
20
+ module AP
21
+ # Represents an argument parsing Result.
22
+ class Result
23
+ # @return [Syntax]
24
+ attr_reader :parameter
25
+ # @return [Array<ParameterResult>]
26
+ attr_reader :results
27
+ # @return [Array<String>]
28
+ attr_accessor :remaining_arguments
29
+
30
+ # Constructs a new Result.
31
+ # @param syntax [Syntax]
32
+ def initialize(syntax)
33
+ @syntax = syntax
34
+ @results = []
35
+ @remaining_arguments = nil
36
+ end
37
+
38
+ # Adds a ParameterResult.
39
+ # @param result [ParameterResult]
40
+ def add_result(result)
41
+ @results << result
42
+ end
43
+
44
+ # Returns the first ParameterResult for a given parameter id.
45
+ # @param id [Symbol]
46
+ # @return [ParameterResult]
47
+ def find_parameter(id)
48
+ @results.find { |r| r.param_id == id }
49
+ end
50
+
51
+ # Returns all ParameterResult(s) for a given parameter id.
52
+ # @param id [Symbol]
53
+ # @return [Array<ParameterResult>]
54
+ def find_parameters(id)
55
+ @results.find_all { |r| r.param_id == id }
56
+ end
57
+
58
+ # Walks through this one.
59
+ def walk(&block)
60
+ walker = TermUtils::AP::Walker.new
61
+ block.call(walker)
62
+ @results.each do |p|
63
+ p.results.each do |a|
64
+ walker.notify_article(a)
65
+ end
66
+ walker.notify_parameter(p)
67
+ end
68
+ walker.notify_finished(@remaining_arguments)
69
+ end
70
+ end
71
+
72
+ # Represents a result for a parameter.
73
+ class ParameterResult
74
+ # @return [Parameter]
75
+ attr_accessor :parameter
76
+ # @return [Array<ArticleResult>]
77
+ attr_accessor :results
78
+
79
+ # Constructs a new ParameterResult.
80
+ # @param parent [Result]
81
+ # @param parameter [Parameter]
82
+ def initialize(parent, parameter)
83
+ @parent = parent
84
+ @parent.add_result(self)
85
+ @parameter = parameter
86
+ @results = []
87
+ end
88
+
89
+ # Adds an ArticleResult.
90
+ # @param result [ArticleResult]
91
+ def add_result(result)
92
+ @results << result
93
+ end
94
+
95
+ # @return [Symbol]
96
+ def param_id
97
+ @parameter.id
98
+ end
99
+
100
+ # Returns the first ArticleResult for a given article id.
101
+ # @param id [Symbol]
102
+ # @return [ArticleResult]
103
+ def find_article(id)
104
+ @results.find { |r| r.art_id == id }
105
+ end
106
+
107
+ # Returns all ArticleResult(s) for a given article id.
108
+ # @param id [Symbol]
109
+ # @return [Array<Result>]
110
+ def find_articles(id)
111
+ @results.find_all { |r| r.art_id == id }
112
+ end
113
+
114
+ # Returns the value of the first ArticleResult.
115
+ # @param id [Symbol] Filter of article id.
116
+ # @return [Object]
117
+ def value(id = nil)
118
+ return @results.first.value unless id
119
+
120
+ find_article(id).value
121
+ end
122
+
123
+ # Returns the value of all ArticleResult(s).
124
+ # @param id [Symbol] Filter of article id.
125
+ # @return [Array<Object>]
126
+ def values(id = nil)
127
+ return @results.map(&:value) unless id
128
+
129
+ vals = []
130
+ @results.each do |r|
131
+ next if r.art_id != id
132
+
133
+ vals << r.values
134
+ end
135
+ vals
136
+ end
137
+ end
138
+
139
+ # Represents a result for an article.
140
+ class ArticleResult
141
+ # @return [ParameterResult]
142
+ attr_accessor :parent
143
+ # @return [Article]
144
+ attr_accessor :article
145
+ # @return [Object]
146
+ attr_accessor :value
147
+
148
+ # Constructs a new ArticleResult.
149
+ # @param parent [ParameterResult]
150
+ # @param article [Article]
151
+ # @param value [Object]
152
+ def initialize(parent, article, value)
153
+ @parent = parent
154
+ @parent.add_result(self)
155
+ @article = article
156
+ @value = value
157
+ end
158
+
159
+ # @return [Symbol]
160
+ def art_id
161
+ @article.id
162
+ end
163
+ end
164
+
165
+ # Represents a Result Walker.
166
+ class Walker
167
+ # Constructs a new Walker.
168
+ def initialize
169
+ @anonymous_parameter_hook = nil
170
+ @anonymous_article_hook = nil
171
+ @parameter_hooks = {}
172
+ @finished_hook = nil
173
+ end
174
+
175
+ # Registers a parameter hook.
176
+ def parameter(param_id = nil, &block)
177
+ unless param_id
178
+ # Anonymous parameter hook
179
+ @anonymous_parameter_hook = block
180
+ return
181
+ end
182
+
183
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
184
+ @parameter_hooks[param_id].hook = block
185
+ end
186
+
187
+ # Registers an article hook.
188
+ def article(param_id = nil, art_id = nil, &block)
189
+ unless param_id
190
+ # Anonymous article hook
191
+ @anonymous_article_hook = block
192
+ return
193
+ end
194
+
195
+ unless art_id
196
+ # Anonymous article hook
197
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
198
+ @parameter_hooks[param_id].anonymous_article_hook = block
199
+ return
200
+ end
201
+
202
+ @parameter_hooks[param_id] = TermUtils::AP::ParameterWalkerHooks.new unless @parameter_hooks.key?(param_id)
203
+ @parameter_hooks[param_id].article_hooks ||= {}
204
+ @parameter_hooks[param_id].article_hooks[art_id] = block
205
+ end
206
+
207
+ # Registers a walk finished hook.
208
+ def finished(&block)
209
+ @finished_hook = block
210
+ end
211
+
212
+ # Calls parameter hooks.
213
+ def notify_parameter(parameter)
214
+ # (1of2) ID parameter hook
215
+ param_hooks = @parameter_hooks[parameter.param_id]
216
+ param_hooks.hook.call(parameter) if param_hooks && param_hooks.hook
217
+ # (2of2) Anonymous parameter hook
218
+ @anonymous_parameter_hook.call(parameter) if @anonymous_parameter_hook
219
+ end
220
+
221
+ # Calls article hooks.
222
+ def notify_article(article)
223
+ # (1of2) ID article hook
224
+ param_hooks = @parameter_hooks[article.parent.param_id]
225
+ if param_hooks
226
+ # ID article hook
227
+ param_hooks.article_hooks[article.art_id].call(article) if param_hooks.article_hooks && param_hooks.article_hooks.key?(article.art_id)
228
+ # Anonymous article hook
229
+ param_hooks.anonymous_article_hook.call(article) if param_hooks.anonymous_article_hook
230
+ end
231
+ # (2of2) Anonymous article hook
232
+ @anonymous_article_hook.call(article) if @anonymous_article_hook
233
+ end
234
+
235
+ # Calls finished hook.
236
+ def notify_finished(remaining_arguments)
237
+ @finished_hook.call(remaining_arguments) if @finished_hook
238
+ end
239
+ end
240
+
241
+ # Parameter hooks for Walker.
242
+ ParameterWalkerHooks = Struct.new('ParameterWalkerHooks', :hook, :anonymous_article_hook, :article_hooks)
243
+ end
244
+ end