1
+ """
2
+ LoopStateMachine
3
+
4
+ Utilities for returning state from functions that run inside a loop.
5
+
6
+ This is used in e.g clipping, where we may need to break or transition states.
7
+
8
+ The main entry point is to return an [`Action`](@ref) from a function that
9
+ is wrapped in a `@controlflow f(...)` macro in a loop. When a known `Action`
10
+ (currently, `:continue`, `:break`, `:return`, or `:full_return` actions) is returned,
11
+ it is processed by the `@controlflow` macro, which allows the function to break out of the loop
12
+ early, continue to the next iteration, or return a value, basically a way to provoke syntactic
13
+ behaviour from a function called from a inside a loop, where you do not have access to that loop.
14
+
15
+ ## Example
16
+
17
+ ```julia
18
+ ```
19
+ """
20
+ module LoopStateMachine
21
+
22
+ export Action, @controlflow
23
+
24
+ import .. GeometryOps as GO
25
+
26
+ const ALL_ACTION_DESCRIPTIONS = """
27
+ - `:continue`: continue to the next iteration of the loop.
28
+ This is the `continue` keyword in Julia. The contents of the action are not used.
29
+ - `:break`: break out of the loop.
30
+ This is the `break` keyword in Julia. The contents of the action are not used.
31
+ - `:return`: cause the function executing the loop to return with the wrapped value.
32
+ - `:full_return`: cause the function executing the loop to return `Action(:full_return, x)`.
33
+ This is very useful to terminate recursive funtions, like tree queries terminating after you
34
+ have found a single intersecting segment.
35
+ """
36
+
37
+ """
38
+ Action(name::Symbol, [x])
39
+
40
+ Create an `Action` with the name `name` and optional contents `x`.
41
+
42
+ `Action`s are returned from functions wrapped in a `@controlflow` macro, which
43
+ does something based on the return value of that function if it is an `Action`.
44
+
45
+ ## Available actions
46
+
47
+ $ALL_ACTION_DESCRIPTIONS
48
+ """
49
+ struct Action{T}
50
+ name:: Symbol
51
+ x:: T
52
+ end
53
+
54
+ Action () = Action {Nothing} (:unnamed , nothing )
55
+ Action (x:: T ) where T = Action {:unnamed, T} (:unnamed , x)
56
+ Action (x:: Symbol ) = Action (x, nothing )
57
+
58
+ function Base. show (io:: IO , action:: Action{T} ) where T
59
+ print (io, " Action " , action. name)
60
+ if isnothing (action. x)
61
+ print (io, " ()" )
62
+ else
63
+ print (io, " (" , action. x, " )" )
64
+ end
65
+ end
66
+
67
+ """
68
+ @controlflow f(...)
69
+
70
+ Process the result of `f(...)` and return the result if it's not an `Action`(@ref LoopStateMachine.Action).
71
+
72
+ If it is an `Action`, then process it according to the following rules, and throw an error if it's not recognized.
73
+ `:continue`, `:break`, `:return`, or `:full_return` are valid actions.
74
+
75
+ $ALL_ACTION_DESCRIPTIONS
76
+
77
+ !!! warning
78
+ Only use this inside a loop, otherwise you'll get a syntax error, especially if you use `:continue` or `:break`.
79
+
80
+ ## Examples
81
+ """
82
+ macro controlflow (expr)
83
+ varname = gensym (" loop-state-machine-returned-value" )
84
+ return quote
85
+ $ varname = $ (esc (expr))
86
+ if $ varname isa Action
87
+ if $ varname. name == :continue
88
+ continue
89
+ elseif $ varname. name == :break
90
+ break
91
+ elseif $ varname. name == :return
92
+ return $ varname. x
93
+ elseif $ varname. name == :full_return
94
+ return $ varname
95
+ else
96
+ throw (ArgumentError (" Unknown action: $($ varname. name) " ))
97
+ end
98
+ else
99
+ $ varname
100
+ end
101
+ end
102
+ end
103
+
104
+ # You can define more actions as you desire.
105
+
106
+ end
0 commit comments