flor 0.18.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +29 -0
- data/CREDITS.md +1 -0
- data/LICENSE.txt +1 -1
- data/Makefile +1 -1
- data/README.md +16 -2
- data/flor.gemspec +1 -2
- data/lib/flor.rb +3 -2
- data/lib/flor/colours.rb +5 -3
- data/lib/flor/conf.rb +1 -0
- data/lib/flor/core.rb +9 -8
- data/lib/flor/core/executor.rb +20 -16
- data/lib/flor/core/node.rb +1 -13
- data/lib/flor/core/procedure.rb +10 -1
- data/lib/flor/core/texecutor.rb +35 -3
- data/lib/flor/djan.rb +1 -0
- data/lib/flor/errors.rb +1 -0
- data/lib/flor/flor.rb +172 -26
- data/lib/flor/id.rb +5 -1
- data/lib/flor/log.rb +14 -8
- data/lib/flor/migrations/0001_tables.rb +1 -0
- data/lib/flor/migrations/0002_cunit_and_munit.rb +1 -0
- data/lib/flor/migrations/0003_timer_onid_bnid.rb +1 -0
- data/lib/flor/migrations/0004_trap_bnid.rb +1 -0
- data/lib/flor/migrations/0005_pointer_content.rb +1 -0
- data/lib/flor/parser.rb +19 -11
- data/lib/flor/pcore/_apply.rb +1 -0
- data/lib/flor/pcore/_arr.rb +1 -0
- data/lib/flor/pcore/_atom.rb +1 -0
- data/lib/flor/pcore/_att.rb +1 -0
- data/lib/flor/pcore/_coll.rb +1 -0
- data/lib/flor/pcore/_dmute.rb +1 -0
- data/lib/flor/pcore/_dol.rb +1 -0
- data/lib/flor/pcore/_dqs.rb +1 -0
- data/lib/flor/pcore/_dump.rb +1 -0
- data/lib/flor/pcore/_err.rb +1 -0
- data/lib/flor/pcore/_head.rb +1 -0
- data/lib/flor/pcore/_obj.rb +1 -0
- data/lib/flor/pcore/_pat_.rb +1 -0
- data/lib/flor/pcore/_pat_arr.rb +1 -0
- data/lib/flor/pcore/_pat_guard.rb +1 -0
- data/lib/flor/pcore/_pat_obj.rb +1 -0
- data/lib/flor/pcore/_pat_or.rb +1 -0
- data/lib/flor/pcore/_pat_regex.rb +1 -0
- data/lib/flor/pcore/_ref.rb +1 -0
- data/lib/flor/pcore/_rxs.rb +1 -0
- data/lib/flor/pcore/_skip.rb +1 -0
- data/lib/flor/pcore/_val.rb +1 -0
- data/lib/flor/pcore/all.rb +1 -0
- data/lib/flor/pcore/andor.rb +1 -0
- data/lib/flor/pcore/any.rb +1 -0
- data/lib/flor/pcore/apply.rb +1 -0
- data/lib/flor/pcore/arith.rb +1 -0
- data/lib/flor/pcore/array_qmark.rb +1 -0
- data/lib/flor/pcore/break.rb +1 -0
- data/lib/flor/pcore/case.rb +1 -0
- data/lib/flor/pcore/cmp.rb +1 -0
- data/lib/flor/pcore/collect.rb +2 -1
- data/lib/flor/pcore/cond.rb +1 -0
- data/lib/flor/pcore/cursor.rb +45 -3
- data/lib/flor/pcore/define.rb +1 -0
- data/lib/flor/pcore/detect.rb +1 -0
- data/lib/flor/pcore/do_return.rb +1 -0
- data/lib/flor/pcore/each.rb +1 -0
- data/lib/flor/pcore/echo.rb +1 -0
- data/lib/flor/pcore/empty.rb +1 -0
- data/lib/flor/pcore/fail.rb +1 -0
- data/lib/flor/pcore/filter.rb +1 -0
- data/lib/flor/pcore/find.rb +1 -0
- data/lib/flor/pcore/flatten.rb +1 -0
- data/lib/flor/pcore/for_each.rb +1 -0
- data/lib/flor/pcore/if.rb +1 -0
- data/lib/flor/pcore/includes.rb +1 -0
- data/lib/flor/pcore/inject.rb +1 -0
- data/lib/flor/pcore/iterator.rb +1 -0
- data/lib/flor/pcore/keys.rb +1 -0
- data/lib/flor/pcore/length.rb +1 -0
- data/lib/flor/pcore/loop.rb +1 -0
- data/lib/flor/pcore/map.rb +1 -0
- data/lib/flor/pcore/match.rb +1 -0
- data/lib/flor/pcore/matchr.rb +1 -0
- data/lib/flor/pcore/max.rb +1 -0
- data/lib/flor/pcore/merge.rb +1 -0
- data/lib/flor/pcore/move.rb +1 -0
- data/lib/flor/pcore/noeval.rb +1 -0
- data/lib/flor/pcore/noret.rb +1 -0
- data/lib/flor/pcore/not.rb +1 -0
- data/lib/flor/pcore/on.rb +4 -3
- data/lib/flor/pcore/on_cancel.rb +1 -0
- data/lib/flor/pcore/on_error.rb +1 -0
- data/lib/flor/pcore/push.rb +1 -0
- data/lib/flor/pcore/rand.rb +1 -0
- data/lib/flor/pcore/range.rb +1 -0
- data/lib/flor/pcore/reduce.rb +1 -0
- data/lib/flor/pcore/return.rb +1 -0
- data/lib/flor/pcore/reverse.rb +1 -0
- data/lib/flor/pcore/select.rb +1 -0
- data/lib/flor/pcore/sequence.rb +1 -0
- data/lib/flor/pcore/set.rb +1 -0
- data/lib/flor/pcore/shuffle.rb +1 -0
- data/lib/flor/pcore/slice.rb +1 -0
- data/lib/flor/pcore/sort.rb +1 -0
- data/lib/flor/pcore/sort_by.rb +1 -0
- data/lib/flor/pcore/split.rb +1 -0
- data/lib/flor/pcore/stall.rb +1 -0
- data/lib/flor/pcore/strings.rb +1 -0
- data/lib/flor/pcore/timestamp.rb +1 -0
- data/lib/flor/pcore/to_array.rb +1 -0
- data/lib/flor/pcore/twig.rb +1 -0
- data/lib/flor/pcore/type_of.rb +1 -0
- data/lib/flor/pcore/until.rb +1 -0
- data/lib/flor/punit/abort.rb +50 -0
- data/lib/flor/punit/c_collect.rb +1 -0
- data/lib/flor/punit/c_each.rb +19 -0
- data/lib/flor/punit/c_for_each.rb +2 -1
- data/lib/flor/punit/c_iterator.rb +2 -1
- data/lib/flor/punit/c_map.rb +1 -0
- data/lib/flor/punit/cancel.rb +1 -0
- data/lib/flor/punit/concurrence.rb +7 -5
- data/lib/flor/punit/cron.rb +1 -0
- data/lib/flor/punit/do_trap.rb +1 -0
- data/lib/flor/punit/every.rb +1 -0
- data/lib/flor/punit/graft.rb +1 -0
- data/lib/flor/punit/{m_ram.rb → m_receive_and_merge.rb} +5 -4
- data/lib/flor/punit/on_timeout.rb +1 -0
- data/lib/flor/punit/part.rb +1 -0
- data/lib/flor/punit/schedule.rb +1 -0
- data/lib/flor/punit/signal.rb +1 -0
- data/lib/flor/punit/sleep.rb +1 -0
- data/lib/flor/punit/task.rb +1 -0
- data/lib/flor/punit/trace.rb +1 -0
- data/lib/flor/punit/trap.rb +10 -1
- data/lib/flor/to_string.rb +1 -0
- data/lib/flor/tools/env.rb +1 -0
- data/lib/flor/tools/firb.rb +33 -0
- data/lib/flor/tools/shell.rb +13 -3
- data/lib/flor/tools/shell_out.rb +1 -0
- data/lib/flor/tt.rb +98 -0
- data/lib/flor/unit.rb +3 -0
- data/lib/flor/unit/caller.rb +158 -23
- data/lib/flor/unit/caller_jruby.rb +133 -0
- data/lib/flor/unit/dump.rb +36 -0
- data/lib/flor/unit/executor.rb +19 -13
- data/lib/flor/unit/ganger.rb +9 -12
- data/lib/flor/unit/gangers.rb +125 -0
- data/lib/flor/unit/hloader.rb +34 -7
- data/lib/flor/unit/hook.rb +3 -0
- data/lib/flor/unit/hooker.rb +32 -15
- data/lib/flor/unit/journal.rb +23 -0
- data/lib/flor/unit/loader.rb +142 -15
- data/lib/flor/unit/logger.rb +35 -7
- data/lib/flor/unit/models.rb +8 -1
- data/lib/flor/unit/models/execution.rb +51 -0
- data/lib/flor/unit/models/message.rb +6 -0
- data/lib/flor/unit/models/pointer.rb +21 -1
- data/lib/flor/unit/models/timer.rb +1 -0
- data/lib/flor/unit/models/trace.rb +1 -0
- data/lib/flor/unit/models/trap.rb +3 -2
- data/lib/flor/unit/scheduler.rb +51 -36
- data/lib/flor/unit/spooler.rb +1 -0
- data/lib/flor/unit/storage.rb +113 -84
- data/lib/flor/unit/taskers.rb +70 -1
- data/lib/flor/unit/waiter.rb +22 -17
- data/lib/flor/unit/wlist.rb +19 -8
- metadata +16 -11
data/lib/flor/id.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
|
5
|
+
NID_REX = /\A[0-9]+(?:_[0-9]+)*(?:-[0-9]+)?\z/.freeze
|
6
|
+
START_NID_REX = /\A[0-9]+(?:_[0-9]+)*(?:-[0-9]+)?/.freeze
|
7
|
+
|
4
8
|
class << self
|
5
9
|
|
6
10
|
#
|
@@ -98,7 +102,7 @@ module Flor
|
|
98
102
|
|
99
103
|
def is_nid?(s)
|
100
104
|
|
101
|
-
!! (s.is_a?(String) && s.match(
|
105
|
+
!! (s.is_a?(String) && s.match(NID_REX))
|
102
106
|
end
|
103
107
|
|
104
108
|
def split_exid(s)
|
data/lib/flor/log.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
class << self
|
@@ -75,7 +76,7 @@ module Flor
|
|
75
76
|
a << hp
|
76
77
|
|
77
78
|
msr = " #{_c.dg}m#{m['m']}s#{m['sm'] || '_'}"
|
78
|
-
msr
|
79
|
+
msr += "r#{m['er']}>#{m['pr']}" if m['er'] && m['er'] > -1
|
79
80
|
a << msr
|
80
81
|
|
81
82
|
a << (m['from'] ? " from #{m['from']}" : '')
|
@@ -323,6 +324,7 @@ module Flor
|
|
323
324
|
def msg_to_detail_s(executor, m, opts={})
|
324
325
|
|
325
326
|
return if m['_detail_msg_flag']
|
327
|
+
#
|
326
328
|
m['_detail_msg_flag'] = true if opts[:flag]
|
327
329
|
|
328
330
|
o = StringIO.new
|
@@ -332,18 +334,22 @@ module Flor
|
|
332
334
|
n = executor.execution['nodes'][nid]
|
333
335
|
node = n ? Flor::Node.new(executor, n, m) : nil
|
334
336
|
|
335
|
-
o.puts "#{_c.
|
336
|
-
|
337
|
-
o.puts "#{_c.dg}
|
338
|
-
o.puts
|
339
|
-
|
337
|
+
o.puts "#{_c.rs}#{_c.dg}<Flor.msg_to_detail_s>"
|
338
|
+
|
339
|
+
o.puts "#{_c.dg}message:#{_c.yl}"
|
340
|
+
o.puts YAML.dump(m)
|
341
|
+
|
342
|
+
o.puts "#{_c.dg}tree:#{_c.yl}"
|
340
343
|
o.puts(tree_to_s(node.lookup_tree(nid), nid, out: o)) if node
|
344
|
+
|
341
345
|
o.puts "#{_c.dg}node:#{_c.yl}"
|
342
|
-
o.puts(
|
343
|
-
|
346
|
+
o.puts YAML.dump(n.merge('tree' => '(above)'))
|
347
|
+
|
348
|
+
o.puts "#{_c.dg}nodes:#{_c.yl}"
|
344
349
|
o.puts nods_to_s(executor, m, opts)
|
345
350
|
z = executor.execution['nodes'].size
|
346
351
|
o.puts "#{_c.yl}#{z} node#{z == 1 ? '' : 's'}."
|
352
|
+
|
347
353
|
o.puts "#{_c.dg}</Flor.msg_to_detail_s>#{_c.rs}"
|
348
354
|
|
349
355
|
o.string
|
data/lib/flor/parser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Flor
|
3
3
|
|
4
4
|
def self.parse(input, fname=nil, opts={})
|
@@ -26,7 +26,10 @@ module Flor
|
|
26
26
|
@line, @column, @offset, @msg, @visual = error_array
|
27
27
|
@fname = fname
|
28
28
|
|
29
|
-
|
29
|
+
m = "syntax error at line #{@line} column #{@column}"
|
30
|
+
m += " in #{fname}" if fname
|
31
|
+
|
32
|
+
super(m)
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
@@ -35,8 +38,11 @@ module Flor
|
|
35
38
|
# parsing
|
36
39
|
|
37
40
|
def wstar(i); rex(nil, i, /[ \t]*/); end
|
41
|
+
def wplus(i); rex(nil, i, /[ \t]+/); end
|
42
|
+
|
43
|
+
def rnstar(i); rex(nil, i, /[\r\n]*/); end
|
44
|
+
def rnplus(i); rex(nil, i, /[\r\n]+/); end
|
38
45
|
|
39
|
-
def retnew(i); rex(nil, i, /[\r\n]*/); end
|
40
46
|
def dot(i); str(nil, i, '.'); end
|
41
47
|
def colon(i); str(nil, i, ':'); end
|
42
48
|
def semicolon(i); str(nil, i, ';'); end
|
@@ -160,8 +166,9 @@ module Flor
|
|
160
166
|
|
161
167
|
def comment(i); rex(nil, i, /#[^\r\n]*/); end
|
162
168
|
|
163
|
-
def eol(i); seq(nil, i, :wstar, :comment, '?', :
|
164
|
-
def eol_wstar(i); seq(nil, i, :wstar, :comment, '?', :
|
169
|
+
def eol(i); seq(nil, i, :wstar, :comment, '?', :rnstar); end
|
170
|
+
def eol_wstar(i); seq(nil, i, :wstar, :comment, '?', :rnstar, :wstar); end
|
171
|
+
def eol_plus(i); seq(nil, i, :wstar, :comment, '?', :rnplus); end
|
165
172
|
def postval(i); rep(nil, i, :eol, 0); end
|
166
173
|
|
167
174
|
def comma_eol(i); seq(nil, i, :comma, :eol, :wstar); end
|
@@ -170,6 +177,8 @@ module Flor
|
|
170
177
|
def comma_qmark_eol(i); seq(nil, i, :comma, '?', :eol); end
|
171
178
|
def coll_sep(i); alt(nil, i, :comma_qmark_eol, :wstar); end
|
172
179
|
|
180
|
+
def woreol(i); alt(:woreol, i, :wplus, :eol_plus); end
|
181
|
+
|
173
182
|
def ent(i)
|
174
183
|
seq(:ent, i, :key, :postval, :colon, :postval, :exp, :postval)
|
175
184
|
end
|
@@ -212,8 +221,8 @@ module Flor
|
|
212
221
|
def ssum(i); seq(nil, i, :sssum, :eol, '?'); end
|
213
222
|
def slgt(i); seq(nil, i, :sslgt, :eol, '?'); end
|
214
223
|
def sequ(i); seq(nil, i, :ssequ, :eol, '?'); end
|
215
|
-
def sand(i); seq(nil, i, :ssand, :
|
216
|
-
def sor(i); seq(nil, i, :ssor, :
|
224
|
+
def sand(i); seq(nil, i, :ssand, :woreol); end # space or eol
|
225
|
+
def sor(i); seq(nil, i, :ssor, :woreol); end # space or eol
|
217
226
|
|
218
227
|
def emod(i); jseq(:exp, i, :val_ws, :smod); end
|
219
228
|
def eprd(i); jseq(:exp, i, :emod, :sprd); end
|
@@ -230,7 +239,7 @@ module Flor
|
|
230
239
|
|
231
240
|
def att(i); seq(:att, i, :sep, :keycol, '?', :exp); end
|
232
241
|
def riou(i); rex(:iou, i, /(if|unless)/); end
|
233
|
-
def iou(i); seq(nil, i, :sep, :riou); end
|
242
|
+
def iou(i); seq(nil, i, :sep, :riou); end # If Or Unless
|
234
243
|
def natt(i); alt(nil, i, :iou, :att); end
|
235
244
|
|
236
245
|
def head(i); seq(:head, i, :exp); end
|
@@ -238,8 +247,8 @@ module Flor
|
|
238
247
|
def node(i); seq(:node, i, :indent, :head, :natt, '*'); end
|
239
248
|
|
240
249
|
def linjoin(i); rex(nil, i, /[ \t]*(\\|\|(?!\|)|;)[ \t]*/); end
|
241
|
-
def outjnl(i); seq(nil, i, :linjoin, :comment, '?', :
|
242
|
-
def outnlj(i); seq(nil, i, :wstar, :comment, '?', :
|
250
|
+
def outjnl(i); seq(nil, i, :linjoin, :comment, '?', :rnstar); end
|
251
|
+
def outnlj(i); seq(nil, i, :wstar, :comment, '?', :rnstar, :linjoin); end
|
243
252
|
def outdent(i); alt(:outdent, i, :outjnl, :outnlj, :eol); end
|
244
253
|
|
245
254
|
def line(i)
|
@@ -718,4 +727,3 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
|
|
718
727
|
sio.string
|
719
728
|
end
|
720
729
|
end
|
721
|
-
|
data/lib/flor/pcore/_apply.rb
CHANGED
data/lib/flor/pcore/_arr.rb
CHANGED
data/lib/flor/pcore/_atom.rb
CHANGED
data/lib/flor/pcore/_att.rb
CHANGED
data/lib/flor/pcore/_coll.rb
CHANGED
data/lib/flor/pcore/_dmute.rb
CHANGED
data/lib/flor/pcore/_dol.rb
CHANGED
data/lib/flor/pcore/_dqs.rb
CHANGED
data/lib/flor/pcore/_dump.rb
CHANGED
data/lib/flor/pcore/_err.rb
CHANGED
data/lib/flor/pcore/_head.rb
CHANGED
data/lib/flor/pcore/_obj.rb
CHANGED
data/lib/flor/pcore/_pat_.rb
CHANGED
data/lib/flor/pcore/_pat_arr.rb
CHANGED
data/lib/flor/pcore/_pat_obj.rb
CHANGED
data/lib/flor/pcore/_pat_or.rb
CHANGED
data/lib/flor/pcore/_ref.rb
CHANGED
data/lib/flor/pcore/_rxs.rb
CHANGED
data/lib/flor/pcore/_skip.rb
CHANGED
data/lib/flor/pcore/_val.rb
CHANGED
data/lib/flor/pcore/all.rb
CHANGED
data/lib/flor/pcore/andor.rb
CHANGED
data/lib/flor/pcore/any.rb
CHANGED
data/lib/flor/pcore/apply.rb
CHANGED
data/lib/flor/pcore/arith.rb
CHANGED
data/lib/flor/pcore/break.rb
CHANGED
data/lib/flor/pcore/case.rb
CHANGED
data/lib/flor/pcore/cmp.rb
CHANGED
data/lib/flor/pcore/collect.rb
CHANGED
data/lib/flor/pcore/cond.rb
CHANGED
data/lib/flor/pcore/cursor.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
class Flor::Pro::Cursor < Flor::Procedure
|
3
4
|
#
|
@@ -10,7 +11,9 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
10
11
|
# task 'charly'
|
11
12
|
# ```
|
12
13
|
#
|
13
|
-
# ##
|
14
|
+
# ## "orders" understood by cursors
|
15
|
+
#
|
16
|
+
# ### break
|
14
17
|
#
|
15
18
|
# Cursor understands `break`. For example, this execution will go from
|
16
19
|
# "alpha" to "charly", task "bravo" will not be visited.
|
@@ -22,7 +25,7 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
22
25
|
# task 'charly'
|
23
26
|
# ```
|
24
27
|
#
|
25
|
-
#
|
28
|
+
# ### continue
|
26
29
|
#
|
27
30
|
# Cursor also understands `continue`. It's useful to rewind a cursor:
|
28
31
|
# ```
|
@@ -33,7 +36,7 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
33
36
|
# create_account _
|
34
37
|
# ```
|
35
38
|
#
|
36
|
-
#
|
39
|
+
# ### move
|
37
40
|
#
|
38
41
|
# Cursor accepts move orders, as in:
|
39
42
|
# ```
|
@@ -44,6 +47,30 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
44
47
|
# do-that-other-thing _
|
45
48
|
# ```
|
46
49
|
#
|
50
|
+
#
|
51
|
+
# ## cursor and tags
|
52
|
+
#
|
53
|
+
# ```
|
54
|
+
# cursor 'main'
|
55
|
+
# # is equivalent to
|
56
|
+
# cursor tag: 'main'
|
57
|
+
# ```
|
58
|
+
#
|
59
|
+
# Tags on cursors are useful for "break" and "continue" (as well as "cancel"),
|
60
|
+
# letting them act on other cursors.
|
61
|
+
#
|
62
|
+
#
|
63
|
+
# ## cursor and start: / initial: attribute
|
64
|
+
#
|
65
|
+
# ```
|
66
|
+
# task 'create mandate'
|
67
|
+
# cursor start: 'approve mandate'
|
68
|
+
# task 'amend mandate'
|
69
|
+
# task 'approve mandate' # <-- first "cycle" will start here
|
70
|
+
# continue _ if f.outcome == 'reject' # <-- will go to "amend mandate"
|
71
|
+
# task 'activate mandate'
|
72
|
+
# ```
|
73
|
+
#
|
47
74
|
# ## see also
|
48
75
|
#
|
49
76
|
# Break, continue, loop.
|
@@ -52,6 +79,7 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
52
79
|
|
53
80
|
def pre_execute
|
54
81
|
|
82
|
+
@node['atts'] = []
|
55
83
|
@node['subs'] = []
|
56
84
|
end
|
57
85
|
|
@@ -90,6 +118,15 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
90
118
|
end
|
91
119
|
end
|
92
120
|
|
121
|
+
def receive_last_att
|
122
|
+
|
123
|
+
start = att('start', 'initial')
|
124
|
+
cid = start && find_child_id(start)
|
125
|
+
@ncid = cid if cid
|
126
|
+
|
127
|
+
super
|
128
|
+
end
|
129
|
+
|
93
130
|
def cancel_when_closed
|
94
131
|
|
95
132
|
return cancel if node_status_flavour == 'on-error'
|
@@ -131,6 +168,11 @@ class Flor::Pro::Cursor < Flor::Procedure
|
|
131
168
|
"move target #{to.inspect} is not a string", self
|
132
169
|
) unless to.is_a?(String)
|
133
170
|
|
171
|
+
find_child_id(to)
|
172
|
+
end
|
173
|
+
|
174
|
+
def find_child_id(to)
|
175
|
+
|
134
176
|
find_tag_target(to) ||
|
135
177
|
find_string_arg_target(to) ||
|
136
178
|
find_string_target(to) ||
|