-
Notifications
You must be signed in to change notification settings - Fork 6
/
common.reb
309 lines (266 loc) · 7.78 KB
/
common.reb
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
rebol [
Title: "Common Draem Routines"
Description: {
Common definitions included by all the Draem modules.
}
Home: http://draem.hostilefork.com
License: 'mit
Date: 20-Oct-2010
Version: 0.3.0.4
; Header conventions: http://www.rebol.org/one-click-submission-help.r
File: %common.reb
Type: 'dialect
Level: 'intermediate
]
;---
; Bridge TO behavior for which there are pull requests, but not integrated
; into the mainline yet.
;---
if unset? $old-to [
old-to: :to
]
to: func [type value] [
if any [text! == type] [
if any-word? value [
return replace replace replace old-to text! value {:} {} {'} {} {/} {}
]
]
return old-to type value
]
;---
; If keys are mapped to blocks in a map, return a block variant of that
; map sorted in order of the longest blocks first
;---
make-sorted-block-from-map: function [map [map!]] [
blk: split make block! map 2
sort:compare blk func [a b] [(length? second a) > (length? second b)]
pos: head blk
while [not tail? pos] [
pos: change:part pos first pos 1
]
return blk
]
;--
; Small helper for getting an object field as a block; if the
; field does not exist then it will be an empty block, and if
; it isn't a block it will be put into one.
;--
in-as-block: func [obj [object!] key [word!]] [
either in obj key [
either block? obj.(key) [obj.(key)] [reduce [obj.(key)]]
][
[]
]
]
;--
; We want to trim head and tail lines from code, but not tabs or spaces
;--
trim-head-tail-lines: function [code [text!]] [
;-- Trim empty lines on top or bottom
;-- (they might make the source easier to read)
code-lines: split code lf
while ["" = trim copy first code-lines] [
take code-lines
]
while ["" = trim copy last code-lines] [
take:last code-lines
]
for-each line code-lines [
append line lf
]
change:part code (combine code-lines) tail code
return code
]
;---
; Delete directory function from:
; http://en.wikibooks.org/wiki/REBOL_Programming/delete-dir
;---
delete-dir: func [
{Deletes a directory including all files and subdirectories.}
dir [file! url!]
/local files
][
if all [
dir? dir
dir: dirize dir
not sys.util/rescue [files: load dir]
][
for-each file files [delete-dir dir/(file)]
]
sys.util/rescue [delete dir]
]
prompt-delete-dir-if-exists: function [
dir [file!]
][
if exists? dir [
print [{Directory} dir {currently exists.}]
either "Y" = uppercase ask "Delete it [Y/N]?" [
delete-dir dir
][
quit 1
]
]
]
;---
; Helper routines for generating templates etc.
;---
; converts slug, character, or tag to a string with option to have dashes
; or spaces (default)
stringify: func [word [set-word! word! file! text!] /dashes] [
if text? word [
print ["already stringified:" word]
quit
]
either dashes [
to text! word
][
replace to text! word "-" space
]
]
url-for-entry: function [entry [object!]] [
;-- Required hook: produce URL from header
assert [action? draem.config.url-from-header/]
draem.config/url-from-header entry.header
]
link-to-entry: function [entry [object!]] [
combine [
{<a href="} url-for-entry entry {">}
entry.header.title
</a>
space {:} space to text! replace copy to text! entry.header.date.date {0:00} {}
<br />
]
]
flatten: func [
data
/local rule
][
local: make block! length? data
rule: [
into [some rule]
|
value: one (append local value)
]
parse data [some rule]
return local
]
; The COMBINE dialect is intended to assist with the common task of creating
; a merged string series out of component Rebol values. Its
; goal is to be friendlier than REJOIN, and to hopefully become the
; behavior backing PRINT.
;
; Currently in a proposal period, and there are questions about whether the
; same dialect can be meaningful for blocks or not.
;
; http://blog.hostilefork.com/combine-alternative-rebol-red-rejoin/
;
combine: func [
block [block!]
/with "Add delimiter between values (will be COMBINEd if a block)"
delimiter [block! any-string! char! action!]
/into
out [any-string!]
/local
needs-delimiter pre-delimit value temp
; hidden way of passing depth after /local, review...
/level depth
][
;-- No good heuristic for string size yet
if not into [
out: make text! 10
]
if not action? get:any $delimiter [
if not block? delimiter [
delimiter: compose [(maybe delimiter)]
]
delimiter: func [depth [integer!]] compose:only:deep [
combine (delimiter)
]
]
if not depth [
depth: 1
]
needs-delimiter: false
pre-delimit: does [
either needs-delimiter [
temp: delimiter depth ; no APPLY in bootstrap (!)
if all [
value? 'temp
(not null? temp) or (block? out)
][
out: append out temp
]
][
needs-delimiter: did with
]
]
;-- Do evaluation of the block until a non-null evaluation result
;-- is found... or the end of the input is reached.
while [not tail? block] [
block: eval:step3 block 'value
; Blocks are substituted in evaluation, like the recursive nature
; of parse rules.
case [
unset? 'value [
fail "Evaluation produced nothing"
]
void? value [
; Skip all voids
]
null? value [
fail "Evaluation produced NULL in COMBINE"
]
action? :value [
fail "Evaluation in COMBINE gave action"
]
block? value [
pre-delimit
out: combine:with:into:level value :delimiter out depth + 1
]
; This is an idea that was not met with much enthusiasm, which was
; to allow COMBINE ['X] to mean the same as COMBINE [MOLD X]
;any [
; word? value
; path? value
;] [
; pre-delimit ;-- overwrites temp!
; temp: get value
; out: append out (mold :temp)
;]
refinement? value [
case [
value = /+ [
needs-delimiter: false
]
true [
fail "COMBINE refinement other than /+ used"
]
]
]
any-list? value [
;
; all other block types as *results* of evaluations throw
; errors for the moment. (It's legal to use PAREN! in the
; COMBINE, but a function invocation that returns a PAREN!
; will not recursively iterate the way BLOCK! does)
;
fail "Evaluation in COMBINE gave non-block! or path! block"
]
any-word? value [
;
; currently we throw errors on words if that's what an
; evaluation produces. Theoretically these could be
; given behaviors in the dialect, but the potential for
; bugs probably outweighs the value (of converting implicitly
; to a string or trying to run an evaluation of a non-block)
;
fail "Evaluation in COMBINE gave symbolic word"
]
true [
pre-delimit
out: append out (form :value)
]
]
]
either into [out] [head out]
]