squared 0.6.1 → 0.6.3

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.
@@ -1,638 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
-
5
3
  module Squared
6
4
  module Workspace
7
5
  module Project
8
6
  module Support
9
- class OptionPartition
10
- include Common::Shell
11
- extend Forwardable
12
-
13
- OPT_NAME = /\A(?:(--)|-)((?(1)[^\[\]=\s-][^\[\]=\s]*|[^\[\]=\s-]))\z/
14
- OPT_VALUE = /\A-{0,2}([^\[\]=\s-][^\[\]=\s]*)(?:\s*=\s*|\s+)(.+)\z/
15
- OPT_SINGLE = /\A-([^\[\]=\s-])(.+)\z/
16
- private_constant :OPT_NAME, :OPT_VALUE, :OPT_SINGLE
17
-
18
- class << self
19
- include Common::Format
20
- include Shell
21
- include Prompt
22
-
23
- def append(target, *args, delim: false, escape: false, quote: true, strip: nil, force: true, double: false,
24
- filter: nil, **)
25
- return if (ret = args.flatten).empty?
26
-
27
- target << '--' if delim && !target.include?('--')
28
- if strip
29
- pat, s = Array(strip)
30
- ret.map! { |val| val.gsub(pat, s || '') }
31
- end
32
- ret, err = ret.partition { |val| filter.match?(val) } if filter
33
- if block_given?
34
- out = []
35
- err ||= []
36
- ret.each do |val|
37
- case (s = yield val)
38
- when String
39
- out << s
40
- when NilClass, FalseClass
41
- err << val
42
- else
43
- out << val
44
- end
45
- end
46
- ret = out
47
- end
48
- if escape || quote
49
- ret.map! do |val|
50
- if opt?(val)
51
- val
52
- elsif escape
53
- shell_escape(val, quote: quote, double: double)
54
- else
55
- shell_quote(val, force: force, double: double)
56
- end
57
- end
58
- end
59
- if target.is_a?(Set)
60
- target.merge(ret)
61
- else
62
- target.concat(ret)
63
- end
64
- err || ret
65
- end
66
-
67
- def clear(target, opts, pass: true, styles: nil, **kwargs)
68
- return if opts.empty?
69
-
70
- kwargs[:subject] ||= target.first.stripext
71
- kwargs[:hint] ||= 'unrecognized'
72
- append(target, opts, delim: true) if kwargs.delete(:append)
73
- warn log_message(Logger::WARN, opts.join(', '), pass: true, **kwargs)
74
- exit 1 unless pass || confirm("Run? [#{sub_style(target, styles: styles)}]", 'N')
75
- end
76
-
77
- def delete_key(target, *args, value: false, reverse: false, count: -1)
78
- ret = []
79
- args.each do |val|
80
- next if (opts = target.grep(matchopt(val, value))).empty?
81
-
82
- opts = opts.first(count) if count >= 0
83
- opts.send(reverse ? :reverse_each : :each) { |key| target.delete(key) }
84
- ret.concat(opts)
85
- end
86
- ret
87
- end
88
-
89
- def strip(val)
90
- return [] unless val
91
-
92
- val = shell_split val if val.is_a?(String)
93
- val.map { |s| s.sub(OPT_SINGLE, '\1=\2').sub(OPT_VALUE, '\1=\2').sub(OPT_NAME, '\2') }.reject(&:empty?)
94
- end
95
-
96
- def select(list, bare: true, no: true, single: false, double: false)
97
- ret = bare ? list.grep_v(/=/) : list.grep(/=/).map! { |val| val.split('=', 2).first }
98
- ret.map! { |val| val.split('|', 2).last }
99
- ret = ret.grep_v(/\Ano-/) unless no
100
- return ret if single == double
101
-
102
- ret.select { |val| single ? val.size == 1 : val.size > 1 }
103
- end
7
+ autoload :OptionPartition, File.expand_path('optionpartition', __dir__)
104
8
 
105
- def uniq!(list, pass = [])
106
- keys = {}
107
- list.each_with_index do |val, i|
108
- j = val =~ OPT_VALUE ? $1 : val
109
- (keys[j] ||= []) << i unless pass.include?(j)
110
- end
111
- data = keys.map { |item| item[1].size > 1 ? item[1][0..-2] : [] }.reject(&:empty?)
112
- return if data.empty?
113
-
114
- data.each { |key| key.each { |i| list[i] = nil } }
115
- list.compact!
116
- list
117
- end
118
-
119
- def arg?(target, *args, value: false, **)
120
- r, s = args.partition { |val| val.is_a?(Regexp) }
121
- r << matchopts(s, value) unless s.empty?
122
- s = target.to_a.compact
123
- r.any? { |pat| s.any?(pat) }
124
- end
125
-
126
- def opt?(val)
127
- val.start_with?('-') && (OPT_NAME.match?(val) || OPT_VALUE.match?(val) || OPT_SINGLE.match?(val))
128
- end
129
-
130
- def pattern?(val)
131
- val.match?(/(?:\A\^|\$\z)/) || val.match?(/(?:\.[*+]|\(\?:|\\[dsw]|\[.+\]|\{\d+,?\d*\})/)
132
- end
133
-
134
- private
135
-
136
- def matchopt(val, value = false)
137
- /\A#{val.size == 1 ? shortopt(val) : longopt(val, value)}/
138
- end
139
-
140
- def matchopts(list, value = false)
141
- a, b = Array(list).partition { |val| val.size == 1 || val.match?(OPT_SINGLE) }
142
- return /\A#{shortopt(*a)}}/ if b.empty?
143
- return /\A#{longopt(*b, value)}/ if a.empty?
144
-
145
- /\A(?:#{shortopt(*a)}|#{longopt(*b, value)})/
146
- end
147
-
148
- def shortopt(*group)
149
- group.map! { |s| s.delete_prefix('-') }
150
- "-(?:#{Regexp.escape(group.join('|'))})(?:\\z|[^ =]| +[^ -])"
151
- end
152
-
153
- def longopt(*group, value)
154
- group.map! { |s| s.delete_prefix('--') }
155
- "--(?:#{Regexp.escape(group.join('|'))})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
156
- end
9
+ class JoinSet < Set
10
+ def self.to_s
11
+ super[/[^:]+\z/, 0]
157
12
  end
158
13
 
159
- attr_reader :target, :extras, :found, :errors, :values, :project, :path, :sep
14
+ alias to_ary to_a
160
15
 
161
- def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
162
- :merge, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
163
- def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :shift, :unshift,
164
- :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size
165
-
166
- def_delegator :@extras, :delete, :remove
167
- def_delegator :@extras, :delete_at, :remove_at
168
- def_delegator :@extras, :delete_if, :remove_if
169
- def_delegator :@extras, :find_all, :detect_all
170
- def_delegator :@extras, :find_index, :detect_index
171
-
172
- def initialize(opts, list, target = Set.new, project: nil, path: nil, sep: '=', **kwargs, &blk)
173
- @target = target.is_a?(Set) ? target : target.to_set
174
- @project = project
175
- @path = path || project&.path
176
- @sep = sep
177
- @errors = []
178
- @found = []
179
- parse(list, opts, **kwargs, &blk)
180
- end
16
+ attr_reader :delim, :extras, :multiple
181
17
 
182
- def parse(list, opts = extras, no: nil, single: nil, args: false, first: nil, underscore: nil, &blk)
18
+ def initialize(data = [], delim: ' ', partition: '--', uniq: /\A--?[^=\s-]/, multiple: [])
19
+ @delim = delim
20
+ @partition = partition
21
+ @uniq = uniq
22
+ @multiple = multiple
183
23
  @extras = []
184
- @values = []
185
- bare = []
186
- e = []
187
- b = []
188
- m = []
189
- p = []
190
- q = []
191
- qq = []
192
- i = []
193
- f = []
194
- si = []
195
- bl = []
196
- list.flat_map do |val|
197
- x, y = val.split('|', 2)
198
- if y
199
- if (n = val.index('='))
200
- x += val[n..-1]
201
- end
202
- [x, y]
203
- else
204
- x
205
- end
206
- end
207
- .each do |val|
208
- if (n = val.index('='))
209
- flag = val[0, n]
210
- case val[n.succ]
211
- when 'e'
212
- e << flag
213
- when 'b'
214
- b << flag
215
- when 'm'
216
- m << flag
217
- when 'q'
218
- qq << flag if val[n + 2] == 'q'
219
- q << flag
220
- when 'p'
221
- p << flag
222
- when 'i'
223
- i << flag
224
- when 'f'
225
- f << flag
226
- when 'n'
227
- si << flag
228
- when 'v'
229
- @values << Regexp.escape(flag)
230
- when '!'
231
- bl << flag
232
- else
233
- next
234
- end
235
- m << flag if val[n + 2] == 'm'
236
- bare << flag if val.end_with?('?')
237
- else
238
- bare << val
239
- end
240
- end
241
- no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
242
- bare.concat(no)
243
- if underscore
244
- tr = ->(a) { a.map { |val| val.tr('-', '_') } }
245
- @values.concat(tr.call(@values))
246
- bare.concat(tr.call(bare))
247
- e.concat(tr.call(e))
248
- b.concat(tr.call(b))
249
- m.concat(tr.call(m))
250
- p.concat(tr.call(p))
251
- q.concat(tr.call(q))
252
- qq.concat(tr.call(qq))
253
- i.concat(tr.call(i))
254
- f.concat(tr.call(f))
255
- si.concat(tr.call(si))
256
- bl.concat(tr.call(bl))
257
- no.concat(tr.call(no))
258
- end
259
- numtype = [
260
- [i, /\A\d+\z/],
261
- [f, /\A\d*(?:\.\d+)?\z/],
262
- [si, /\A-?\d+\z/]
263
- ].freeze
264
- numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && v.match?(pat) } }
265
- skip = false
266
- opts.each do |opt|
267
- next skip = true if opt == '--'
268
- next push opt if skip
269
-
270
- if single&.match?(opt)
271
- add "-#{opt}"
272
- elsif bare.include?(opt)
273
- add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
274
- elsif opt.start_with?(/no[-_]/) && no.include?(name = opt[3..-1])
275
- add "--no-#{name}"
276
- else
277
- if opt =~ OPT_VALUE
278
- key = $1
279
- val = $2
280
- merge = m.include?(key)
281
- if e.include?(key)
282
- add shell_option(key, val, merge: merge, sep: sep)
283
- elsif q.include?(key)
284
- add quote_option(key, val, double: qq.include?(key), merge: merge, sep: sep)
285
- elsif p.include?(key)
286
- if val.match?(/\A(["']).+\1\z/)
287
- add shell_option(key, val, escape: false, merge: merge, sep: sep)
288
- elsif path
289
- add quote_option(key, path + val, merge: merge, sep: sep)
290
- else
291
- push opt
292
- end
293
- elsif b.include?(key) || (bl.include?(key) && %w[true false].include?(val)) || numcheck.call(key, val)
294
- add basic_option(key, val, merge: merge, sep: sep)
295
- elsif merge
296
- add basic_option(key, val, merge: true, sep: sep)
297
- else
298
- push opt
299
- end
300
- opt = key
301
- else
302
- push opt
303
- skip = true if args
304
- end
305
- skip = true if first&.any? { |s| s.is_a?(Regexp) ? opt.match?(s) : !opt.include?(s) }
306
- end
307
- end
308
- @values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})#{sep}(.+)\z/m
309
- @extras.each_with_index(&blk) if block_given?
310
- self
311
- end
312
-
313
- def swap(opts = nil, &blk)
314
- unless opts
315
- opts = found
316
- @found = []
317
- end
318
- opts.sort!(&blk) if block_given?
319
- @extras = opts
320
- self
321
- end
322
-
323
- def append(*args, **kwargs, &blk)
324
- args = extras if args.empty?
325
- out = OptionPartition.append(target, *args, **kwargs, &blk)
326
- errors.concat(out) if out && (block_given? || kwargs[:filter])
327
- self
328
- end
329
-
330
- def append_any(*args, quote: true, **kwargs)
331
- (args.empty? ? extras : args.flatten).each do |val|
332
- val = yield val if block_given?
333
- next unless val.is_a?(String)
334
-
335
- if exist?(val)
336
- add_path(val, **kwargs)
337
- elsif quote
338
- add_quote(val, **kwargs)
339
- else
340
- add val
341
- end
342
- end
343
- self
344
- end
345
-
346
- def delete_key(*args, **kwargs)
347
- OptionPartition.delete_key(target, *args, **kwargs)
348
- self
349
- end
350
-
351
- def values_of(*args, strict: true, first: false, last: false)
352
- eq, s = strict ? [sep, '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
353
- g = ["\"((?:[^\"]|(?<=\\\\)\"(?!$#{windows? ? '| ' : ''}))*)\""]
354
- g << "'((?:[^']|'\\\\'')*)'" unless windows?
355
- g << "(#{s})"
356
- args.map! do |opt|
357
- if opt.size == 1
358
- /(?:\A| )-#{opt} ?([^ ]+)/
359
- else
360
- /(?:\A| )--#{opt + eq}(?:#{g.join('|')})/
361
- end
362
- end
363
- ret = []
364
- target.each do |opt|
365
- args.each do |pat|
366
- next unless opt =~ pat
367
-
368
- ret << ($1 || $2 || $3)
369
- break
370
- end
371
- end
372
- return ret unless first || last
373
-
374
- if last.is_a?(Numeric)
375
- ret.last(last)
376
- elsif last
377
- ret.last
378
- else
379
- first.is_a?(Numeric) ? ret.first(first) : ret.first
380
- end
381
- end
382
-
383
- def uniq(list)
384
- ignore = map { |val| nameonly(val) }
385
- list.reject { |val| ignore.include?(s = nameonly(val)) || any?(OptionPartition.send(:matchopt, s)) }
386
- end
387
-
388
- def clear(opts = nil, errors: false, **kwargs)
389
- styles = project.theme[:inline] if project
390
- if errors
391
- OptionPartition.clear(target, @errors, styles: styles, **kwargs)
392
- @errors.clear
393
- return self unless opts
394
- end
395
- opts ||= extras
396
- OptionPartition.clear(target, opts - found, styles: styles, **kwargs)
397
- opts.clear
398
- self
399
- end
400
-
401
- def adjoin(*args, with: nil, start: false)
402
- index = -1
403
- temp = to_a
404
- if with
405
- pat = case with
406
- when String, Symbol
407
- /\A#{Regexp.escape(with)}\z/
408
- when Array
409
- OptionPartition.send(:matchopts, with)
410
- else
411
- with
412
- end
413
- temp.each_with_index do |val, i|
414
- if val.to_s.match?(pat)
415
- index = i + (start.is_a?(Numeric) ? start : 1)
416
- break
417
- end
418
- end
419
- else
420
- temp.each_with_index do |val, i|
421
- if index == 0
422
- next unless val.start_with?('-')
423
-
424
- index = i
425
- break
426
- elsif i > 0 && !val.start_with?('-')
427
- if start
428
- index = i + (start.is_a?(Numeric) ? start : 1)
429
- break
430
- end
431
- index = 0
432
- end
433
- end
434
- end
435
- if index > 0
436
- if args.empty?
437
- args = dup
438
- reset
439
- else
440
- args.each { |val| remove val }
441
- end
442
- args = temp[0...index] + args + temp[index..-1]
443
- target.clear
444
- end
445
- merge args
446
- self
447
- end
448
-
449
- def add_path(*args, force: true, double: false, **kwargs)
450
- if args.empty?
451
- args = select { |val| val.is_a?(String) }
452
- args.map! { |val| path + val } if path
453
- append(args, force: force, **kwargs)
454
- else
455
- add shell_quote(path ? path.join(*args) : File.join(*args), option: false, force: force, double: double)
456
- end
457
- self
458
- end
459
-
460
- def add_quote(*args, **kwargs)
461
- args.compact!
462
- merge(args.map! { |val| val == '--' || OptionPartition.opt?(val) ? val : shell_quote(val, **kwargs) })
463
- self
464
- end
465
-
466
- def add_option(flag, val = nil, **kwargs)
467
- add shell_option(flag, val, **kwargs)
468
- self
469
- end
470
-
471
- def add_first(fallback = nil, prefix: nil, path: false, quote: false, reverse: false, expect: false, **kwargs)
472
- val = (reverse ? pop : shift) || fallback
473
- if val
474
- val.delete_prefix!(prefix) if prefix
475
- return self if block_given? && !(val = yield val).is_a?(String)
476
-
477
- if path
478
- add_path(val, **kwargs)
479
- elsif quote
480
- add_quote(val, **kwargs)
481
- else
482
- add val
483
- end
484
- elsif expect
485
- raise(expect.is_a?(String) ? expect : 'no value provided')
486
- end
487
- self
488
- end
489
-
490
- def last(val = nil, &blk)
491
- unless block_given?
492
- case val
493
- when NilClass
494
- return extras.last
495
- when Numeric
496
- return extras.last(val)
497
- when String, Array, Regexp
498
- val = OptionPartition.send(:matchopts, val) unless val.is_a?(Regexp)
499
- blk = proc { |s| s&.match?(val) }
500
- else
501
- raise TypeError, "unknown: #{val}"
502
- end
503
- end
504
- ret = find_all(&blk)
505
- unless ret.empty?
506
- ret = case val
507
- when NilClass
508
- ret.first(1)
509
- when Numeric
510
- ret.first(val)
511
- else
512
- ret
513
- end
514
- ret.each do |opt|
515
- delete opt
516
- add opt
517
- end
518
- end
519
- val.nil? ? ret.first : ret
520
- end
521
-
522
- def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
523
- found, other = if block_given?
524
- partition(&blk)
525
- elsif exclude.first.is_a?(Symbol)
526
- partition(&exclude.first)
527
- else
528
- partition do |val|
529
- next false if pattern && OptionPartition.pattern?(val)
530
-
531
- exclude.none? { |pat| val.match?(Regexp.new(pat)) }
532
- end
533
- end
534
- unless found.empty?
535
- add '--' if delim
536
- extras.clear
537
- concat other
538
- if path
539
- found.each { |val| add_path(val) }
540
- else
541
- found.map! { |val| shell_quote(val) } if quote
542
- merge found
543
- end
544
- end
545
- self
546
- end
547
-
548
- def reset(errors: false)
549
- extras.clear
550
- found.clear
551
- clear(errors: true) if errors
552
- self
553
- end
554
-
555
- def append?(key, val = nil, type: nil, force: false, sep: '=', **kwargs)
556
- return false unless force || !arg?(key)
557
-
558
- val = yield self if block_given?
559
- return false unless val
560
-
561
- type ||= :quote if kwargs.empty?
562
- add case type
563
- when :quote
564
- quote_option(key, val, sep: sep)
565
- when :basic
566
- basic_option(key, val, sep: sep)
567
- else
568
- shell_option(key, val, sep: sep, **kwargs)
569
- end
570
- true
571
- end
572
-
573
- def arg?(*args, **kwargs)
574
- OptionPartition.arg?(target, *args, **kwargs)
24
+ super(data.compact)
575
25
  end
576
26
 
577
- def exist?(*args, add: false, first: false, last: false, glob: false)
578
- return with_glob?(File.join(*args), glob) unless args.empty?
579
-
580
- if first || last
581
- return false unless (val = first ? self.first : self.last)
582
-
583
- with_glob?(val, glob).tap do |ret|
584
- next unless add && ret
585
-
586
- add_first(path: true, reverse: !first)
587
- end
588
- else
589
- each_with_index do |val, i|
590
- next unless with_glob?(val, glob)
591
-
592
- if add
593
- remove_at i
594
- add_path val
595
- end
596
- return true
597
- end
598
- false
27
+ def multiple=(val)
28
+ case val
29
+ when Enumerable
30
+ @multiple.concat(val.to_a.map(&:to_s))
31
+ when String, Symbol, Pathname
32
+ @multiple << val.to_s
33
+ when NilClass, FalseClass
34
+ @multiple.clear
599
35
  end
600
36
  end
601
37
 
602
- def uniq!(list)
603
- unless (list = uniq(list)).empty?
604
- concat list
605
- self
606
- end
38
+ def insert(*args)
39
+ replace Set.new(compact.insert(*args))
607
40
  end
608
41
 
609
- private
610
-
611
- def nameonly(val)
612
- val[OPT_VALUE, 1] || val
42
+ def slice(*args)
43
+ compact.slice(*args)
613
44
  end
614
45
 
615
- def with_glob?(val, glob = true)
616
- return false unless path && val.is_a?(String) && !val.empty?
617
-
618
- path.join(val).exist? || (glob && !path.glob(val).empty?)
619
- end
620
-
621
- def windows?
622
- Rake::Win32.windows?
623
- end
624
- end
625
-
626
- class JoinSet < Set
627
- def self.to_s
628
- super[/[^:]+\z/, 0]
46
+ def slice!(*args)
47
+ data = compact
48
+ data.slice!(*args).tap { replace Set.new(data) }
629
49
  end
630
50
 
631
- attr_reader :delim
632
-
633
- def initialize(data = [], delim: ' ')
634
- super(data.compact)
635
- @delim = delim
51
+ def compact
52
+ to_ary.map!(&:to_s).reject(&:empty?)
636
53
  end
637
54
 
638
55
  def last(val, pat)
@@ -641,7 +58,7 @@ module Squared
641
58
  end
642
59
 
643
60
  def pass(&blk)
644
- ret = to_a.map!(&:to_s).reject(&:empty?)
61
+ ret = compact
645
62
  @last&.each do |val, pat, key|
646
63
  items = []
647
64
  index = nil
@@ -662,6 +79,7 @@ module Squared
662
79
  end
663
80
  ret[items.last] = val
664
81
  end
82
+ ret.concat(extras.map(&:to_s).reject(&:empty?)) unless extras.empty?
665
83
  return ret unless block_given?
666
84
 
667
85
  ret.reject(&blk)
@@ -683,18 +101,72 @@ module Squared
683
101
 
684
102
  def temp(*args, &blk)
685
103
  args.compact!
686
- ret = pass(&blk)
687
- ret = Set.new(ret.concat(args)).to_a unless args.empty?
688
- ret.join(@delim)
104
+ pass(&blk)
105
+ .concat(args)
106
+ .join(@delim)
689
107
  end
690
108
 
691
109
  def done
692
110
  to_s.tap { clear }
693
111
  end
694
112
 
113
+ def merge(enum)
114
+ if !extras.empty?
115
+ extras.concat(enum.to_a)
116
+ self
117
+ elsif (n = enum.find_index { |val| extras?(val) })
118
+ data = enum.to_a
119
+ @extras = if n == 0
120
+ data
121
+ else
122
+ super(data[0...n])
123
+ data[n..-1]
124
+ end
125
+ self
126
+ else
127
+ super
128
+ end
129
+ end
130
+
131
+ def <<(obj)
132
+ return super if extras.empty? && !extras?(obj)
133
+
134
+ unless !extras.include?(@partition) && include?(obj) && @uniq.match?(s = obj.to_s) && !multiple.include?(s)
135
+ extras << obj
136
+ end
137
+ self
138
+ end
139
+
140
+ def to_a
141
+ pass
142
+ end
143
+
695
144
  def to_s
696
145
  pass.join(@delim)
697
146
  end
147
+
148
+ def to_enum(*args)
149
+ pass.to_enum(*args)
150
+ end
151
+
152
+ def to_json(*args)
153
+ pass.to_json(*args)
154
+ end
155
+
156
+ def to_yaml(*args)
157
+ pass.to_yaml(*args)
158
+ end
159
+
160
+ alias add :<<
161
+ alias add? :<<
162
+ alias push :<<
163
+ alias concat merge
164
+
165
+ private
166
+
167
+ def extras?(obj)
168
+ obj == @partition || (include?(obj) && (!@uniq.match?(s = obj.to_s) || multiple.include?(s)))
169
+ end
698
170
  end
699
171
  end
700
172
  end