]> gitweb.factorcode.org Git - factor.git/blob - basis/tools/scaffold/scaffold.factor
tools.scaffold: Support unit tests with more than one output with run-string helper...
[factor.git] / basis / tools / scaffold / scaffold.factor
1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien arrays assocs byte-arrays calendar
4 classes classes.error combinators combinators.short-circuit
5 continuations eval hashtables help.markup interpolate io
6 io.directories io.encodings.utf8 io.files io.pathnames
7 io.streams.string kernel math math.parser namespaces prettyprint
8 quotations sequences sets sorting splitting strings system
9 timers unicode urls vocabs vocabs.loader vocabs.metadata words
10 words.symbol ;
11 IN: tools.scaffold
12
13 SYMBOL: developer-name
14 SYMBOL: using
15
16 ERROR: not-a-vocab-root string ;
17
18 ERROR: vocab-must-not-exist string ;
19
20 <PRIVATE
21
22 : vocab-root? ( string -- ? )
23     trim-tail-separators vocab-roots get member? ;
24
25 : ensure-vocab-exists ( string -- string )
26     dup lookup-vocab [ no-vocab ] unless ;
27
28 : check-vocab-root ( string -- string )
29     dup vocab-root? [ not-a-vocab-root ] unless ;
30
31 : check-vocab-root/name ( vocab-root string -- vocab-root string )
32     [ check-vocab-root ] [ check-vocab-name ] bi* ;
33
34 : replace-vocab-separators ( vocab -- path )
35     path-separator first CHAR: . associate substitute ;
36
37 : vocab-root/vocab>path ( vocab-root vocab -- path )
38     check-vocab-root/name
39     [ ] [ replace-vocab-separators ] bi* append-path ;
40
41 : vocab>path ( vocab -- path )
42     check-vocab [ find-vocab-root ] keep vocab-root/vocab>path ;
43
44 : vocab-root/vocab/file>path ( vocab-root vocab file -- path )
45     [ vocab-root/vocab>path ] dip append-path ;
46
47 : vocab-root/vocab/suffix>path ( vocab-root vocab suffix -- path )
48     [ vocab-root/vocab>path dup file-name append-path ] dip append ;
49
50 : vocab/file>path ( vocab file -- path )
51     [ vocab>path ] dip append-path ;
52
53 : vocab/suffix>path ( vocab suffix -- path )
54     [ vocab>path dup file-name append-path ] dip append ;
55
56 : directory-exists ( path -- )
57     "Not creating a directory, it already exists: " write print ;
58
59 : scaffold-directory ( vocab-root vocab -- )
60     vocab-root/vocab>path
61     dup file-exists? [ directory-exists ] [ make-directories ] if ;
62
63 : not-scaffolding ( path -- path )
64     "Not creating scaffolding for " write dup <pathname> . ;
65
66 : scaffolding ( path -- path )
67     "Creating scaffolding for " write dup <pathname> . ;
68
69 : scaffolding? ( path -- path ? )
70     dup file-exists? [ not-scaffolding f ] [ scaffolding t ] if ;
71
72 : scaffold-copyright ( -- )
73     "! Copyright (C) " write now year>> number>string write
74     developer-name get [ "Your name" ] unless* bl write "." print
75     "! See http://factorcode.org/license.txt for BSD license." print ;
76
77 : main-file-string ( vocab -- string )
78     [
79         scaffold-copyright
80         "USING: ;" print
81         "IN: " write print
82     ] with-string-writer ;
83
84 : set-scaffold-main-file ( vocab path -- )
85     [ main-file-string 1array ] dip utf8 set-file-lines ;
86
87 : scaffold-main ( vocab-root vocab -- )
88     [ ".factor" vocab-root/vocab/suffix>path ] keep swap scaffolding? [
89         set-scaffold-main-file
90     ] [
91         2drop
92     ] if ;
93
94 : scaffold-metadata ( vocab file contents -- )
95     [ ensure-vocab-exists ] 2dip
96     [
97         [ vocab/file>path ] dip 1array swap scaffolding? [
98             utf8 set-file-lines
99         ] [
100             2drop
101         ] if
102     ] [
103         2drop
104     ] if* ;
105
106 : lookup-type ( string -- object/string ? )
107     "/f" ?tail swap
108     "new" ?head drop [ { [ CHAR: ' = ] [ digit? ] } 1|| ] trim-tail
109     H{
110         { "object" object }
111         { "obj" object }
112         { "quot" quotation }
113         { "string" string }
114         { "str" string }
115         { "hash" hashtable }
116         { "hashtable" hashtable }
117         { "?" boolean }
118         { "ch" "a character" }
119         { "word" word }
120         { "array" array }
121         { "byte-array" byte-array }
122         { "timer" timer }
123         { "duration" duration }
124         { "path" "a pathname string" }
125         { "vocab" "a vocabulary specifier" }
126         { "vocab-root" "a vocabulary root string" }
127         { "c-ptr" c-ptr }
128         { "sequence" sequence }
129         { "slice" slice }
130         { "from" integer }
131         { "to" integer }
132         { "i" integer }
133         { "n" integer }
134         { "seq" sequence }
135         { "exemplar" object }
136         { "assoc" assoc }
137         { "alist" "an array of key/value pairs" }
138         { "keys" sequence }
139         { "values" sequence }
140         { "class" class }
141         { "tuple" tuple }
142         { "url" url }
143     } at* [ swap [ \ $maybe swap 2array ] when ] dip ;
144
145 GENERIC: add-using ( object -- )
146
147 M: array add-using [ add-using ] each ;
148
149 M: string add-using drop ;
150
151 M: object add-using
152     vocabulary>> using get [ adjoin ] [ drop ] if* ;
153
154 : ($values.) ( array -- )
155     [
156         "    " write
157         [ bl ] [
158             "{ " write
159             dup array? [ first ] when
160             dup lookup-type [
161                 [ unparse write bl ]
162                 [ [ pprint ] [ add-using ] bi ] bi*
163             ] [
164                 drop unparse write bl object pprint
165                 object add-using
166             ] if
167             " }" write
168         ] interleave
169     ] unless-empty ;
170
171 : ?print-nl ( seq1 seq2 -- )
172     [ empty? ] either? [ nl ] unless ;
173
174 : $values. ( word -- )
175     "declared-effect" word-prop [
176         [ in>> ] [ out>> ] bi
177         2dup [ empty? ] both? [
178             2drop
179         ] [
180             [ members ] dip over diff
181             "{ $values" print
182             [ drop ($values.) ]
183             [ ?print-nl ]
184             [ nip ($values.) ] 2tri
185             nl "}" print
186         ] if
187     ] when* ;
188
189 : error-description. ( word -- )
190     [ $values. ] [
191         "{ $description \"Throws " write
192         name>> dup a/an write " \" { $link " write
193         write " } \" error.\" }" print
194     ] bi "{ $error-description \"\" } ;" print ;
195
196 : class-description. ( word -- )
197     drop "{ $class-description \"\" } ;" print ;
198
199 : symbol-description. ( word -- )
200     drop "{ $var-description \"\" } ;" print ;
201
202 : $description. ( word -- )
203     drop "{ $description \"\" } ;" print ;
204
205 : docs-body. ( word/symbol -- )
206     {
207         { [ dup error-class? ] [ error-description. ] }
208         { [ dup class? ] [ class-description. ] }
209         { [ dup symbol? ] [ symbol-description. ] }
210         [ [ $values. ] [ $description. ] bi ]
211     } cond ;
212
213 : docs-header. ( word -- )
214     "HELP: " write name>> print ;
215
216 : (help.) ( word -- )
217     [ docs-header. ] [ docs-body. ] bi ;
218
219 : interesting-words ( vocab -- array )
220     vocab-words
221     [ { [ "help" word-prop ] [ predicate? ] } 1|| ] reject
222     natural-sort ;
223
224 : interesting-words. ( vocab -- )
225     interesting-words [ (help.) nl ] each ;
226
227 : docs-file-string ( vocab -- str2 )
228     [
229         {
230             [ "IN: " write print nl ]
231             [ interesting-words. ]
232             [
233                 [ "ARTICLE: " write unparse dup write bl print ]
234                 [ "{ $vocab-link " write pprint " }" print ] bi
235                 ";" print nl
236             ]
237             [ "ABOUT: " write unparse print ]
238         } cleave
239     ] with-string-writer ;
240
241 : write-using ( vocab -- )
242     "USING:" write
243     using get members
244     { "help.markup" "help.syntax" } append natural-sort remove
245     [ bl write ] each
246     " ;" print ;
247
248 : set-scaffold-docs-file ( vocab path -- )
249     utf8 <file-writer> [
250         scaffold-copyright
251         [ docs-file-string ] [ write-using ] bi
252         write
253     ] with-output-stream ;
254
255 : with-scaffold ( quot -- )
256     [ HS{ } clone using ] dip with-variable ; inline
257
258 : link-vocab ( vocab -- )
259     ".private" ?tail drop
260     check-vocab
261     "Edit documentation: " write
262     "-docs.factor" vocab/suffix>path <pathname> . ;
263
264 PRIVATE>
265
266 : help. ( word -- )
267     [ (help.) ] [ nl vocabulary>> link-vocab ] bi ;
268
269 : scaffold-docs ( vocab -- )
270     ensure-vocab-exists
271     [
272         dup "-docs.factor" vocab/suffix>path scaffolding? [
273             set-scaffold-docs-file
274         ] [
275             2drop
276         ] if
277     ] with-scaffold ;
278
279 : scaffold-undocumented ( string -- )
280     [ interesting-words. ] [ link-vocab ] bi ;
281
282 : scaffold-authors ( vocab -- )
283     "authors.txt" developer-name get scaffold-metadata ;
284
285 : scaffold-tags ( vocab tags -- )
286     [ "tags.txt" ] dip scaffold-metadata ;
287
288 : scaffold-summary ( vocab summary -- )
289     [ "summary.txt" ] dip scaffold-metadata ;
290
291 : scaffold-platforms ( vocab platforms -- )
292     [ "platforms.txt" ] dip scaffold-metadata ;
293
294 : delete-from-root-cache ( string -- )
295     root-cache get delete-at ;
296
297 : scaffold-vocab-in ( vocab-root string -- )
298     dup delete-from-root-cache
299     {
300         [ scaffold-directory ]
301         [ scaffold-main ]
302         [ nip require ]
303         [ nip scaffold-authors ]
304     } 2cleave ;
305
306 : scaffold-core ( string -- )
307     "resource:core" swap scaffold-vocab-in ;
308
309 : scaffold-basis ( string -- )
310     "resource:basis" swap scaffold-vocab-in ;
311
312 : scaffold-extra ( string -- )
313     "resource:extra" swap scaffold-vocab-in ;
314
315 : scaffold-work ( string -- )
316     "resource:work" swap scaffold-vocab-in  ;
317
318 : scaffold-vocab ( string -- )
319     "Choose a vocabulary root:" vocab-roots get
320     '[ [ "Use " prepend ] keep ] { } map>assoc throw-restarts
321     swap scaffold-vocab-in ;
322
323 <PRIVATE
324
325 : tests-file-string ( vocab -- string )
326     [
327         scaffold-copyright
328         "USING: tools.test " write dup write " ;" print
329         "IN: " write write ".tests" print
330     ] with-string-writer ;
331
332 : set-scaffold-tests-file ( vocab path -- )
333     [ tests-file-string ] dip utf8 set-file-contents ;
334
335 PRIVATE>
336
337 : scaffold-tests ( vocab -- )
338     ensure-vocab-exists
339     dup "-tests.factor" vocab/suffix>path
340     scaffolding? [
341         set-scaffold-tests-file
342     ] [
343         2drop
344     ] if ;
345
346 SYMBOL: nested-examples
347
348 : example-using ( using -- )
349     join-words "example-using" [
350         nested-examples get 4 0 ? CHAR: \s <string> "example-indent" [
351             "${example-indent}\"Example:\"
352 ${example-indent}{ $example \"USING: ${example-using} ;\"
353 ${example-indent}    \"\"
354 ${example-indent}    \"\"
355 ${example-indent}}
356 "
357             interpolate
358         ] with-variable
359     ] with-variable ;
360
361 : n-examples-using ( n using -- )
362     '[ _ example-using ] times ;
363
364 : scaffold-n-examples ( n word -- )
365     vocabulary>> "prettyprint" 2array
366     [ t nested-examples ] 2dip
367     '[
368         "{ $examples" print
369         _ _ n-examples-using
370         "}" print
371     ] with-variable ;
372
373 : scaffold-examples ( word -- )
374     2 swap scaffold-n-examples ;
375
376 : scaffold-file ( path -- )
377     [ touch-file ]
378     [ "Click to edit: " write <pathname> . ] bi ;
379
380 : scaffold-rc ( path -- )
381     home prepend-path scaffold-file ;
382
383 : scaffold-factor-boot-rc ( -- )
384     ".factor-boot-rc" scaffold-rc ;
385
386 : scaffold-factor-rc ( -- )
387     ".factor-rc" scaffold-rc ;
388
389 : scaffold-mason-rc ( -- )
390     ".factor-mason-rc" scaffold-rc ;
391
392 : scaffold-factor-roots ( -- )
393     ".factor-roots" scaffold-rc ;
394
395 : make-unit-test ( answer code -- str )
396     split-lines [ "    " prepend ] map "\n" join
397     "[\n" "\n] unit-test\n" surround
398     " " glue ;
399
400 : run-string ( string -- datastack )
401     parse-string V{ } clone swap with-datastack ; inline
402
403 : scaffold-unit-test ( -- str/f )
404     read-contents dup "" = [
405         drop f
406     ] [
407         [ run-string unparse ] keep
408         make-unit-test
409     ] if ;
410
411 : scaffold-unit-tests ( -- str )
412     [ scaffold-unit-test dup ] [ ] produce nip "\n\n" join ;
413
414 HOOK: scaffold-emacs os ( -- )
415
416 M: unix scaffold-emacs ".emacs" scaffold-rc ;