-
Notifications
You must be signed in to change notification settings - Fork 0
/
letUtils.nim
240 lines (194 loc) · 5.02 KB
/
letUtils.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
## A collection of small templates and macros that aid in limiting the scope of mutable `var`\iables
## as much as practical.
import std/macros
when (NimMajor, NimMinor) >= (1, 2):
import std/with
func unreachable {.noReturn, inline.} = discard
template scope*(body: untyped): untyped =
##[
Open a new lexical scope (variables declared in the body are not accessible outside). Similar
to the built-in `block` `language construct`_ but does not interfere with `break`. All credits
for the implementation go to **@markspanbroek**.
Unlabeled `break` inside a `block` has been deprecated in Nim 2.0; `see details and rationale
<https://github.com/nim-lang/Nim/pull/20785>`_.
.. _language construct: https://nim-lang.org/docs/manual.html#statements-and-expressions-block-expression
]##
if true:
body
else:
discard # Required to work around a bug in the compiler.
unreachable()
template asLet*(val, name, body: untyped): untyped =
##[
Equivalent to:
.. code-block:: nim
let name = val
body
name
**Example:**
.. code-block:: nim
processObj:
getObj().asLet obj:
obj.field = 1
]##
scope:
let name = val
body
name
when declared with:
template asLet*(val, body: untyped): untyped =
##[
Equivalent to:
.. code-block:: nim
let tmp = val
with tmp:
body
tmp
**Example:**
.. code-block:: nim
processObj:
getObj().asLet:
field = 1
**Since:** Nim 1.2.
**See also:**
* `std/with <https://nim-lang.org/docs/with.html>`_
]##
scope:
let tmp = val
with tmp:
body
tmp
template asVar*(val, name, body: untyped): untyped =
##[
Equivalent to:
.. code-block:: nim
var name = val
body
name
**Example:**
.. code-block:: nim
from std/strbasics import strip
processStr:
getStr().asVar s:
s.strip
]##
scope:
var name = val
body
name
when declared with:
template asVar*(val, body: untyped): untyped =
##[
Equivalent to:
.. code-block:: nim
var tmp = val
with tmp:
body
tmp
**Example:**
.. code-block:: nim
from std/strbasics import strip
assert " test\n".asVar(strip) == "test"
**Since:** Nim 1.2.
**See also:**
* `std/with <https://nim-lang.org/docs/with.html>`_
]##
scope:
var tmp = val
with tmp:
body
tmp
template viaVar*[T](t: type[T]; name, body: untyped): T =
##[
Equivalent to:
.. code-block:: nim
var name: T
body
name
**Example:**
.. code-block:: nim
processSeq:
seq[string].viaVar data:
for i in 0 ..< 5:
data &= $i
**See also:**
* `std/sugar: collect <https://nim-lang.org/docs/sugar.html#collect.m%2Cuntyped>`_
* `iterrr <https://github.com/hamidb80/iterrr>`_
]##
scope:
var name: T
body
name
when declared with:
template viaVar*[T](t: type[T]; body: untyped): T =
##[
Equivalent to:
.. code-block:: nim
var tmp: T
with tmp:
body
tmp
**Example:**
.. code-block:: nim
run:
Config.viaVar:
logFile = "prog.log"
verbosity = 1
merge loadFromFile "config.kdl"
**Since:** Nim 1.2.
**See also:**
* `std/with <https://nim-lang.org/docs/with.html>`_
]##
scope:
var tmp: T
with tmp:
body
tmp
macro freezeVars*(body: untyped): untyped =
##[
Create a `let` binding for each top-level `var` in `body`; make other declarations inaccessible
from outside.
**Example:**
.. code-block:: nim
freezeVars:
var fib = @[0, 1]
for i in 2 ..< 13:
fib &= fib[^1] + fib[^2]
# From now on, you cannot modify `fib`.
echo fib
assert not compiles (fib[0] = 123)
**Example:**
.. code-block:: nim
proc process(id: int): bool =
freezeVars:
var thing = findById id
if thing == nil:
if id < 0:
return false
thing = insert id
validate thing
]##
let letTuple = nnkVarTuple.newNimNode # To be assigned outside `body`.
let varTupleConstr = nnkTupleConstr.newNimNode # Forwarded from inside.
func recurse(body: NimNode) =
for node in body:
case node.kind:
of nnkVarSection:
for defs in node:
for i in 0 ..< defs.len - 2:
let varIdent = defs[i]
letTuple.add varIdent
varTupleConstr.add varIdent
of nnkStmtList: # `body` can have nested statement lists if was generated by a macro.
node.recurse
else:
discard
body.expectKind nnkStmtList
body.recurse
if letTuple.len == 0:
body
else:
nnkLetSection.newNimNode.add letTuple.add(
newEmptyNode(),
bindSym"scope".newCall body.add varTupleConstr,
)