term_utils 0.1.1 → 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 (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