flor 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/CREDITS.md +21 -0
- data/LICENSE.txt +4 -1
- data/Makefile +4 -0
- data/README.md +8 -0
- data/flor.gemspec +10 -10
- data/lib/flor.rb +2 -2
- data/lib/flor/changes.rb +3 -3
- data/lib/flor/colours.rb +14 -8
- data/lib/flor/conf.rb +63 -58
- data/lib/flor/core.rb +4 -4
- data/lib/flor/core/executor.rb +65 -29
- data/lib/flor/core/node.rb +37 -20
- data/lib/flor/core/procedure.rb +182 -40
- data/lib/flor/core/texecutor.rb +125 -52
- data/lib/flor/djan.rb +111 -82
- data/lib/flor/dollar.rb +31 -30
- data/lib/flor/flor.rb +314 -237
- data/lib/flor/id.rb +7 -2
- data/lib/flor/log.rb +250 -245
- data/lib/flor/parser.rb +72 -38
- data/lib/flor/pcore/_arr.rb +10 -10
- data/lib/flor/pcore/_att.rb +49 -14
- data/lib/flor/pcore/_coll.rb +18 -0
- data/lib/flor/pcore/_obj.rb +23 -7
- data/lib/flor/pcore/_pat_.rb +1 -1
- data/lib/flor/pcore/_pat_guard.rb +8 -0
- data/lib/flor/pcore/_pat_obj.rb +3 -3
- data/lib/flor/pcore/_pat_or.rb +4 -0
- data/lib/flor/pcore/_pat_regex.rb +24 -0
- data/lib/flor/pcore/_skip.rb +4 -0
- data/lib/flor/pcore/_val.rb +0 -1
- data/lib/flor/pcore/all.rb +111 -0
- data/lib/flor/pcore/any.rb +83 -0
- data/lib/flor/pcore/arith.rb +35 -6
- data/lib/flor/pcore/break.rb +39 -1
- data/lib/flor/pcore/case.rb +82 -4
- data/lib/flor/pcore/cmp.rb +7 -7
- data/lib/flor/pcore/collect.rb +50 -0
- data/lib/flor/pcore/cond.rb +17 -3
- data/lib/flor/pcore/cursor.rb +8 -2
- data/lib/flor/pcore/detect.rb +45 -0
- data/lib/flor/pcore/each.rb +52 -0
- data/lib/flor/pcore/empty.rb +60 -0
- data/lib/flor/pcore/filter.rb +94 -0
- data/lib/flor/pcore/find.rb +67 -0
- data/lib/flor/pcore/for_each.rb +65 -0
- data/lib/flor/pcore/includes.rb +32 -0
- data/lib/flor/pcore/inject.rb +55 -0
- data/lib/flor/pcore/iterator.rb +151 -0
- data/lib/flor/pcore/keys.rb +60 -0
- data/lib/flor/pcore/length.rb +34 -7
- data/lib/flor/pcore/logo.rb +18 -0
- data/lib/flor/pcore/loop.rb +4 -0
- data/lib/flor/pcore/map.rb +77 -46
- data/lib/flor/pcore/match.rb +8 -2
- data/lib/flor/pcore/matchr.rb +4 -5
- data/lib/flor/pcore/move.rb +3 -3
- data/lib/flor/pcore/noeval.rb +13 -0
- data/lib/flor/pcore/not.rb +16 -0
- data/lib/flor/pcore/on.rb +172 -0
- data/lib/flor/pcore/on_cancel.rb +54 -0
- data/lib/flor/pcore/on_error.rb +68 -0
- data/lib/flor/pcore/rand.rb +2 -2
- data/lib/flor/pcore/range.rb +2 -1
- data/lib/flor/pcore/reduce.rb +124 -0
- data/lib/flor/pcore/reverse.rb +46 -0
- data/lib/flor/pcore/select.rb +72 -0
- data/lib/flor/pcore/set.rb +8 -0
- data/lib/flor/pcore/stall.rb +10 -0
- data/lib/flor/pcore/to_array.rb +61 -0
- data/lib/flor/pcore/until.rb +34 -0
- data/lib/flor/punit/cancel.rb +30 -5
- data/lib/flor/punit/ccollect.rb +11 -0
- data/lib/flor/punit/cmap.rb +10 -5
- data/lib/flor/punit/concurrence.rb +42 -51
- data/lib/flor/punit/cron.rb +33 -0
- data/lib/flor/punit/do_trap.rb +42 -0
- data/lib/flor/punit/every.rb +48 -13
- data/lib/flor/punit/graft.rb +3 -3
- data/lib/flor/punit/on_timeout.rb +38 -0
- data/lib/flor/punit/schedule.rb +69 -6
- data/lib/flor/punit/signal.rb +54 -0
- data/lib/flor/punit/sleep.rb +1 -1
- data/lib/flor/punit/task.rb +4 -1
- data/lib/flor/punit/trap.rb +188 -13
- data/lib/flor/tools/shell.rb +408 -62
- data/lib/flor/tools/shell_out.rb +31 -0
- data/lib/flor/unit.rb +1 -1
- data/lib/flor/unit/caller.rb +177 -0
- data/lib/flor/unit/executor.rb +1 -0
- data/lib/flor/unit/ganger.rb +15 -21
- data/lib/flor/unit/hook.rb +1 -1
- data/lib/flor/unit/hooker.rb +22 -10
- data/lib/flor/unit/loader.rb +22 -22
- data/lib/flor/unit/logger.rb +63 -36
- data/lib/flor/unit/models.rb +6 -1
- data/lib/flor/unit/models/execution.rb +12 -1
- data/lib/flor/unit/models/message.rb +7 -0
- data/lib/flor/unit/models/trap.rb +31 -17
- data/lib/flor/unit/scheduler.rb +18 -10
- data/lib/flor/unit/storage.rb +83 -23
- data/lib/flor/unit/waiter.rb +1 -2
- metadata +96 -52
- data/lib/flor/deep.rb +0 -144
- data/lib/flor/punit/on.rb +0 -57
- data/lib/flor/unit/runner.rb +0 -84
- data/match.md +0 -22
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::OnError < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Counterpart to the on_error: attribute.
|
5
|
+
#
|
6
|
+
# Takes a function definition (or a variable pointing to one of them)
|
7
|
+
# and makes sure the function is called when an error occurs at the
|
8
|
+
# current level or below.
|
9
|
+
#
|
10
|
+
# ```
|
11
|
+
# sequence
|
12
|
+
# set f.l []
|
13
|
+
# on_error (def err \ push f.l err.error.msg)
|
14
|
+
# push f.l 0
|
15
|
+
# push f.l x # <-- will fail because `x` is unknown
|
16
|
+
# push f.l 1
|
17
|
+
# ```
|
18
|
+
# Where the field `l` ends up containing
|
19
|
+
# `[ 0, "don't know how to apply \"x\"" ]`.
|
20
|
+
#
|
21
|
+
# ```
|
22
|
+
# set f.l []
|
23
|
+
#
|
24
|
+
# define error_handler err
|
25
|
+
# push f.l err.error.msg
|
26
|
+
#
|
27
|
+
# sequence
|
28
|
+
# on_error error_handler
|
29
|
+
# # ...
|
30
|
+
# ```
|
31
|
+
#
|
32
|
+
# ## on and on_error
|
33
|
+
#
|
34
|
+
# "on_error" is made to allow for `on error`, so that:
|
35
|
+
# ```
|
36
|
+
# sequence
|
37
|
+
# on error
|
38
|
+
# push f.l err.msg # a block with an `err` variable
|
39
|
+
# # ...
|
40
|
+
# ```
|
41
|
+
# gets turned into:
|
42
|
+
# ```
|
43
|
+
# sequence
|
44
|
+
# on_error
|
45
|
+
# def err # a anonymous function definition with an `err` argument
|
46
|
+
# push f.l err.msg
|
47
|
+
# # ...
|
48
|
+
# ```
|
49
|
+
#
|
50
|
+
# ## see also
|
51
|
+
#
|
52
|
+
# On, on_cancel.
|
53
|
+
|
54
|
+
name 'on_error'
|
55
|
+
|
56
|
+
def pre_execute
|
57
|
+
|
58
|
+
unatt_unkeyed_children
|
59
|
+
end
|
60
|
+
|
61
|
+
def receive_non_att
|
62
|
+
|
63
|
+
store_on(:error)
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/lib/flor/pcore/rand.rb
CHANGED
@@ -30,8 +30,8 @@ class Flor::Pro::Rand < Flor::Procedure
|
|
30
30
|
|
31
31
|
a, b = determine_bounds
|
32
32
|
|
33
|
-
fail
|
34
|
-
"'
|
33
|
+
fail Flor::FlorError.new(
|
34
|
+
"'#{tree[0]}' expects an integer or a float", self
|
35
35
|
) unless is_number?(a) && is_number?(b)
|
36
36
|
|
37
37
|
payload['ret'] = Random.rand(a...b)
|
data/lib/flor/pcore/range.rb
CHANGED
@@ -49,7 +49,8 @@ class Flor::Pro::Range < Flor::Procedure
|
|
49
49
|
edn = aedn if aedn
|
50
50
|
ste = aste if aste
|
51
51
|
|
52
|
-
fail
|
52
|
+
fail Flor::FlorError.new("#{@node['heat0']} step is 0", self) \
|
53
|
+
if ste == 0
|
53
54
|
|
54
55
|
#payload['ret'] = (sta..edn - 1).step(ste).to_a
|
55
56
|
# doesn't accept negative steps
|
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Reduce < Flor::Pro::Iterator
|
3
|
+
#
|
4
|
+
# Reduce takes a collection and a function. It reduces the collection
|
5
|
+
# to a single result thanks to the function.
|
6
|
+
#
|
7
|
+
# ```
|
8
|
+
# reduce [ '0', 1, 'b', 3 ]
|
9
|
+
# def result element
|
10
|
+
# result + element
|
11
|
+
# # --> "01b3"
|
12
|
+
# ```
|
13
|
+
#
|
14
|
+
# An initial value is accepted (generally after the collection)
|
15
|
+
#
|
16
|
+
# ```
|
17
|
+
# reduce [ 0, 1, 2, 3, 4 ] 10
|
18
|
+
# def result i \ result + i
|
19
|
+
# # --> 20
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
# Passing a proc is OK too, but, in the case of a mathematical expression
|
23
|
+
# prefixing it with `v.` prevents premature rewriting...
|
24
|
+
#
|
25
|
+
# ```
|
26
|
+
# reduce [ 0, 1, 2, 3, 4 ] 10 v.+
|
27
|
+
# # --> 20
|
28
|
+
# ```
|
29
|
+
#
|
30
|
+
# ## iterating and functions
|
31
|
+
#
|
32
|
+
# Iterating functions accept 0 to 3 arguments when iterating over an
|
33
|
+
# array and 0 to 4 arguments when iterating over an object.
|
34
|
+
#
|
35
|
+
# Those arguments are `[ result, value, index, length ]` for arrays.
|
36
|
+
# They are `[ result, key, value, index, length ]` for objects.
|
37
|
+
#
|
38
|
+
# The corresponding `res`, `key`, `val`, `idx` and `len` variables are also
|
39
|
+
# set in the closure for the function call.
|
40
|
+
#
|
41
|
+
# ## see also
|
42
|
+
#
|
43
|
+
# Inject.
|
44
|
+
|
45
|
+
name 'reduce'
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def prepare_iterations
|
50
|
+
|
51
|
+
@node['args']
|
52
|
+
.each { |a|
|
53
|
+
if Flor.is_func_tree?(a)
|
54
|
+
@node['fun'] ||= a
|
55
|
+
elsif Flor.is_proc_tree?(a)
|
56
|
+
@node['fun'] ||= proc_to_fun(a)
|
57
|
+
elsif a.is_a?(Array) || a.is_a?(Hash)
|
58
|
+
@node['ocol'] ||= a
|
59
|
+
else
|
60
|
+
@node['res'] ||= a
|
61
|
+
end }
|
62
|
+
|
63
|
+
@node['ocol'] ||= node_payload_ret
|
64
|
+
ocol = @node['ocol']
|
65
|
+
|
66
|
+
fail Flor::FlorError.new(
|
67
|
+
"function not given to #{heap.inspect}", self
|
68
|
+
) unless @node['fun']
|
69
|
+
fail Flor::FlorError.new(
|
70
|
+
"collection not given to #{heap.inspect}", self
|
71
|
+
) unless ocol.is_a?(Array) || ocol.is_a?(Hash)
|
72
|
+
|
73
|
+
@node['col'] = Flor.to_coll(@node['ocol'])
|
74
|
+
|
75
|
+
@node['res'] ||= @node['col'].shift
|
76
|
+
|
77
|
+
@node['args'] = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def determine_iteration_vars
|
81
|
+
|
82
|
+
res = @node['res']
|
83
|
+
idx = @node['idx']
|
84
|
+
elt = @node['col'][idx]
|
85
|
+
len = @node['col'].length
|
86
|
+
|
87
|
+
if @node['ocol'].is_a?(Array)
|
88
|
+
{ 'res' => res, 'elt' => elt,
|
89
|
+
'idx' => idx, 'len' => len }
|
90
|
+
else
|
91
|
+
{ 'res' => res, 'key' => elt[0], 'val' => elt[1],
|
92
|
+
'idx' => idx, 'len' => len }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def receive_iteration
|
97
|
+
|
98
|
+
@node['res'] = payload['ret']
|
99
|
+
end
|
100
|
+
|
101
|
+
def iterator_result
|
102
|
+
|
103
|
+
@node['res']
|
104
|
+
end
|
105
|
+
|
106
|
+
def proc_to_fun(prc)
|
107
|
+
|
108
|
+
h = prc[1]['proc']
|
109
|
+
l = tree[2]
|
110
|
+
|
111
|
+
[ '_func',
|
112
|
+
{ 'nid' => "#{nid}_0_1",
|
113
|
+
'tree' =>
|
114
|
+
[ 'def', [
|
115
|
+
[ '_att', [ [ 'r', [], l ] ], l ],
|
116
|
+
[ '_att', [ [ 'x', [], l ] ], l ],
|
117
|
+
[ h, [ [ 'r', [], l ], [ 'x', [], l ] ], l ]
|
118
|
+
], l ],
|
119
|
+
'cnid' => '0', #
|
120
|
+
'fun' => 0 }, # TODO really?
|
121
|
+
l ]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Reverse < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Reverses an array or a string.
|
5
|
+
#
|
6
|
+
# ```
|
7
|
+
# reverse [ 0, 2, 4 ]
|
8
|
+
# # --> sets f.ret to [ 4, 2, 0 ]
|
9
|
+
# reverse "melimelo"
|
10
|
+
# # --> sets f.ret to "olemilem"
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# Reverses f.ret if there are no arguments
|
14
|
+
# ```
|
15
|
+
# [ 5, 6, 4 ] # sets f.ret to [ 5, 6, 4 ]
|
16
|
+
# reverse _ # sets f.ret to [ 4, 6, 5 ]
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# Will fail if it finds nothing reversable.
|
20
|
+
|
21
|
+
name 'reverse'
|
22
|
+
|
23
|
+
def pre_execute
|
24
|
+
|
25
|
+
@node['ret'] = receive_payload_ret
|
26
|
+
|
27
|
+
unatt_unkeyed_children
|
28
|
+
end
|
29
|
+
|
30
|
+
def receive_payload_ret
|
31
|
+
|
32
|
+
r = payload['ret']
|
33
|
+
r.respond_to?(:reverse) ? r.reverse : false
|
34
|
+
end
|
35
|
+
|
36
|
+
def receive_last
|
37
|
+
|
38
|
+
r =
|
39
|
+
@node['ret'] ||
|
40
|
+
fail(
|
41
|
+
Flor::FlorError.new('found no argument that could be reversed', self))
|
42
|
+
|
43
|
+
wrap_reply('ret' => r)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
require 'flor/pcore/iterator'
|
3
|
+
|
4
|
+
|
5
|
+
class Flor::Pro::Select < Flor::Macro::Iterator
|
6
|
+
#
|
7
|
+
# Filters a collection
|
8
|
+
#
|
9
|
+
# "select" and "reject" are the 'block-oriented' children of
|
10
|
+
# "filter" and "filter-out" respectively.
|
11
|
+
#
|
12
|
+
# ```
|
13
|
+
# select [ 1, 2, 3, 4, 5 ]
|
14
|
+
# = (elt % 2) 1
|
15
|
+
#
|
16
|
+
# # f.ret --> [ 1, 3, 5 ]
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# Note that the equivalent "filter" is:
|
20
|
+
# ```
|
21
|
+
# filter [ 1, 2, 3, 4, 5 ]
|
22
|
+
# def x
|
23
|
+
# = (x % 2) 1
|
24
|
+
# ```
|
25
|
+
#
|
26
|
+
# The blocks understand `elt` (the current element), `idx` (the current
|
27
|
+
# zero-based index), and `key` (the current key for an object/hash).
|
28
|
+
#
|
29
|
+
# ## with objects (hashes)
|
30
|
+
#
|
31
|
+
# ```
|
32
|
+
# select { a: 'A', b: 'B', c: 'C', d: 'D' }
|
33
|
+
# key == 'a' or val == 'C' or idx == 3
|
34
|
+
#
|
35
|
+
# # f.ret --> { 'a' => 'A', 'c' => 'C', 'd' => 'D' }
|
36
|
+
# ```
|
37
|
+
#
|
38
|
+
# ## reject
|
39
|
+
#
|
40
|
+
# "reject" is the negative of "select".
|
41
|
+
#
|
42
|
+
# ```
|
43
|
+
# reject [ 1, 2, 3, 4, 5 ]
|
44
|
+
# (elt % 2) == 0
|
45
|
+
#
|
46
|
+
# # f.ret --> [ 1, 3, 5 ]
|
47
|
+
# ```
|
48
|
+
#
|
49
|
+
# ## iterating blocks
|
50
|
+
#
|
51
|
+
# Iterating blocks are given 3 to 4 local variables.
|
52
|
+
#
|
53
|
+
# A block iterating over an array will receive `elt` (the current element
|
54
|
+
# of the iteration), `idx` (the zero-based index of the current element),
|
55
|
+
# and `len` (the length of the array).
|
56
|
+
#
|
57
|
+
# A block iterating over an object will receive `key` (the current string
|
58
|
+
# key), `val` (the current value), `idx` (the zero-based index of the
|
59
|
+
# current key/val), and `len` (the length of the object).
|
60
|
+
#
|
61
|
+
# ## see also
|
62
|
+
#
|
63
|
+
# filter, map, reject, and collect.
|
64
|
+
|
65
|
+
names %w[ select reject ]
|
66
|
+
|
67
|
+
def rewrite_tree
|
68
|
+
|
69
|
+
rewrite_iterator_tree(heap == 'select' ? 'filter' : 'filter-out')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
data/lib/flor/pcore/set.rb
CHANGED
@@ -77,6 +77,14 @@ class Flor::Pro::Set < Flor::Procedure
|
|
77
77
|
@node['refs'] = []
|
78
78
|
end
|
79
79
|
|
80
|
+
def execute_child(index=0, sub=nil, h=nil)
|
81
|
+
|
82
|
+
payload['ret'] = node_payload_ret \
|
83
|
+
if children[index]
|
84
|
+
|
85
|
+
super(index, sub, h)
|
86
|
+
end
|
87
|
+
|
80
88
|
def receive_non_att
|
81
89
|
|
82
90
|
ret = payload['ret']
|
data/lib/flor/pcore/stall.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Stall < Flor::Procedure
|
3
|
+
#
|
4
|
+
# "stall" is mostly used in flor tests. It simply dead ends.
|
5
|
+
#
|
6
|
+
# It receives its execution message, executes all its attributes but
|
7
|
+
# does not answer to its parent procedure, effectively stalling
|
8
|
+
# its branch of the execution.
|
9
|
+
#
|
10
|
+
# ## see also
|
11
|
+
#
|
12
|
+
# _skip
|
3
13
|
|
4
14
|
name 'stall'
|
5
15
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::ToArray < Flor::Procedure
|
3
|
+
#
|
4
|
+
# "to-array", turns an argument into an array, "to-object" turns it into
|
5
|
+
# an object.
|
6
|
+
#
|
7
|
+
# ```
|
8
|
+
# to-array [ 0 1 2 ]
|
9
|
+
# # --> [ 0 1 2 ] # (left intact)
|
10
|
+
#
|
11
|
+
# to-array 123
|
12
|
+
# # --> [ 123 ]
|
13
|
+
#
|
14
|
+
# to-array { a: 'A', b: 'B' }
|
15
|
+
# # --> [ [ 'a', 'A' ], [ 'b', 'B' ] ]
|
16
|
+
# ```
|
17
|
+
#
|
18
|
+
# and
|
19
|
+
#
|
20
|
+
# ```
|
21
|
+
# to-object [ 'a' 'A' 'b' 'B' 'c' 'C' ]
|
22
|
+
# # --> { 'a': 'A', b: 'B', c: 'C' }
|
23
|
+
# ```
|
24
|
+
|
25
|
+
names %w[ to-array to-object ]
|
26
|
+
|
27
|
+
def pre_execute
|
28
|
+
|
29
|
+
@node['ret'] = receive_payload_ret
|
30
|
+
|
31
|
+
unatt_unkeyed_children
|
32
|
+
end
|
33
|
+
|
34
|
+
def receive_last
|
35
|
+
|
36
|
+
wrap_reply('ret' => (heap == 'to-object') ? to_object : to_array)
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def to_array
|
42
|
+
|
43
|
+
Flor.to_coll(@node['ret'])
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_object
|
47
|
+
|
48
|
+
r = @node['ret']
|
49
|
+
|
50
|
+
fail Flor::FlorError.new('to-object wants an array (or an object)', self) \
|
51
|
+
unless r.is_a?(Array) || r.is_a?(Hash)
|
52
|
+
|
53
|
+
fail Flor::FlorError.new('to-object expects array with even length', self) \
|
54
|
+
if r.is_a?(Array) && r.length.odd?
|
55
|
+
|
56
|
+
r = r.each_slice(2).to_a if r.find { |e| ! e.is_a?(Array) }
|
57
|
+
|
58
|
+
Hash[r]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/lib/flor/pcore/until.rb
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Until < Flor::Procedure
|
3
|
+
#
|
4
|
+
# `until` loops until a condition evaluates to true.
|
5
|
+
# `while` loops while a condition evaluates to true.
|
6
|
+
#
|
7
|
+
# ```
|
8
|
+
# set i 0
|
9
|
+
# until i == 7
|
10
|
+
# task 'bob' "verify counter ($(i))"
|
11
|
+
# set i (i + 1)
|
12
|
+
# ```
|
13
|
+
#
|
14
|
+
# ```
|
15
|
+
# set i 0
|
16
|
+
# while i < 7
|
17
|
+
# task 'bob' "verify counter ($(i))"
|
18
|
+
# set i (i + 1)
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# `until` and `while` understand `break` and `continue`, like `cursor` and
|
22
|
+
# `loop` do.
|
23
|
+
#
|
24
|
+
# ```
|
25
|
+
# until
|
26
|
+
# false
|
27
|
+
# push f.l 0
|
28
|
+
# set outer-break break # alias local break to "outer-break"
|
29
|
+
# until false
|
30
|
+
# push f.l 'a'
|
31
|
+
# outer-break 'x'
|
32
|
+
# ```
|
33
|
+
#
|
34
|
+
# ## see also
|
35
|
+
#
|
36
|
+
# Break, continue, cursor, loop.
|
3
37
|
|
4
38
|
names 'until', 'while'
|
5
39
|
|