-
-
Notifications
You must be signed in to change notification settings - Fork 28
/
lang-zuo.scrbl
1426 lines (1048 loc) · 53.8 KB
/
lang-zuo.scrbl
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#lang scribble/manual
@(require (for-label zuo-doc/fake-zuo)
"real-racket.rkt")
@title[#:tag "zuo-base"]{Zuo Base Language}
@defmodule[#:multi (zuo zuo/base) #:no-declare #:lang #:packages ()]
@declare-exporting[zuo zuo/base #:packages () #:use-sources (zuo-doc/fake-zuo)]
The @racketmodname[zuo] language is Zuo's default language. It's meant
to be familiar to Racket programmers, and the description here leans
heavily on comparisons and the Racket documentation, for now. Zuo
forms and functions tend use traditional Racket names, even when a
different choice might be made in a fresh design, and even when the
Zuo construct is not exactly the same. Filesystem operations, however,
tend to use the names of Unix programs, which are much shorter than
Racket's long names.
The @racketmodname[zuo/base] language includes most of the bindings
from @racketmodname[zuo], but not the ones from @racketmodname[zuo/cmdline],
@racketmodname[zuo/build], @racketmodname[zuo/shell], @racketmodname[zuo/thread],
@racketmodname[zuo/glob], or @racketmodname[zuo/config].
When using @racket[module->hash] on @racketmodname[zuo/base],
@racketmodname[zuo], or a module implemented with one of those
languages, the resulting hash table includes @racket['dynamic-require]
mapped to the @racket[dynamic-require] function. Getting
@racket[dynamic-require] that way provides a path from the primitive
@seclink["module-protocol"]{Zuo kernel module protocol} to
@racketmodname[zuo/base] module exports.
@history[#:changed "1.2" @elem{Added the @racket['dynamic-require] key
for @racketmodname[zuo] and related languages.}]
@section{Syntax and Evaluation Model}
A @racketmodname[zuo] module consists of a sequence of definitions
(e.g., @racket[define]), macro definitions (e.g.,
@racket[define-syntax]), imports (e.g., @racket[require]), exports
(e.g., @racket[provide]), and expressions (e.g., @racket[5]). Loading
the module first @deftech{expands} it, and then @deftech{evaluates}
it. A module is loaded only once, so if a module is demanded more than
once, the result of the first load is used.
The expansion process expands macro uses, loads imported modules, and
evaluates macro definitions as such forms are encountered for the
module body. Expansion creates a binding for each definition as
encountered, but does not expand or evaluate the definition, yet.
Expansion of definitions and expressions is deferred until all forms
in the module body have been processed. Some expression forms have
local definition contexts, which can include further imports and macro
definitions, so expansion at those points nests the same two-step
process as used for the module body.
Evaluation of a module evaluates its definitions and expressions (some
of which may have been introduced by macro expansion) in order.
Definitions bind mutually recursively within the enclosing module or
definition context, and referencing a defined variable before its
evaluation is an error. The value of each expression in a module body
is printed using @racket[alert] compiled with @racket[~v].
A module's provided variables and macros are made available to other
modules that import it. Variables and macros that are not provided are
completely inaccessible outside of the module.
There are no @defterm{phases} in the sense of Racket. When
@racketmodname[zuo] macro expansion encounters an import, it makes
all of the imported module's exports immediately available for use in
macro implementations, both variables and macros. For example, an
imported macro might be used both to implement a macro body and in
nearby run-time code or even run-time code generated by the macro's
expansion. The absence of a phase separation is related to the way that
each module is evaluated only once, and it's made workable in part by
the absence of mutable data structures in Zuo, and in part because
there is no support for compiling a @racketmodname[zuo] module and
saving it separate from its instantiation in a Zuo process or saved
image.
Zuo macros consume a representation of syntax that uses plain pairs,
numbers, strings, etc., but with an identifier @tech{syntax object}
potentially in place of a symbol. Even for symbols, using a syntax
object is optional; by using @racket[quote-syntax] to create a syntax
object, a macro can generate a term with identifiers bound at the
macro's definition site, instead of a use site's, but the macro
expander does not impose or automate that binding. See
@racket[quote-syntax] for more information.
@; ----------------------------------------
@section{Binding and Control Forms}
A @racketmodname[zuo] syntactic form is either a @deftech{definition}
form or an @deftech{expression} form. Expressions can appear in
definition contexts, but not vice versa. In descriptions of syntactic
forms @racket[_body ...+] refers to a context that allows definition
forms, but the last form in the expansion of the definition context
must be an expression form.
@subsection{Expression Forms}
@defform[(lambda formals body ...+)
#:grammar ([formals (id ... [id expr] ...)
id
(id ... [id expr] ... . id)])]{
Analogous to @realracket[lambda] in @racketmodname[racket], but
without keyword arguments.}
@defform[#:link-target? #f #:id not-expr (expr expr ...)]{
A function call, where the initial @racket[expr] is not an identifier
bound to a macro.}
@deftogether[(
@defform*[[(let ([id val-expr] ...) body ...+)
(let proc-id ([id init-expr] ...) body ...+)]]
@defform[(let* ([id val-expr] ...) body ...+)]
@defform[(letrec ([id val-expr] ...) body ...+)]
)]{
Just like @realracket*[let let* letrec] in @racketmodname[racket].}
@deftogether[(
@defform[(if test-expr then-expr else-expr)]
@defform[(and expr ...)]
@defform[(or expr ...)]
@defform[(when test-expr body ...+)]
@defform[(unless test-expr body ...+)]
@defform[#:literals (else)
(cond cond-clause ...)
#:grammar ([cond-clause [test-expr then-body ...+]
[else then-body ...+]])]
@defform[#:id else else]
@defform[(begin expr ...+)]
)]{
Just like @realracket*[if and or when unless cond else begin] in
@racketmodname[racket], except that @racket[cond] is more limited.}
@deftogether[(
@defform[(quote datum)]
@defform[(quasiquote datum)]
@defform[#:id unquote unquote]
@defform[#:id unquote-splicing unquote-splicing]
)]{
Just like @realracket*[quote quasiquote unquote unquote-splicing] from
@racketmodname[racket].}
@defform[(quote-syntax datum)]{
Analogous to @realracket[quote-syntax] from @racketmodname[racket],
but only identifiers have a specialized syntax-object representation
in place of symbols. Tree structure in @racket[datum] is represented
using plain pairs, and non-identifier elements are
represented with plain numbers, strings, etc.
A Zuo module's representation starts with plain pairs and symbols, a
macro procedure can receive terms containing plain symbols, and it can
return a term with plain symbols. A symbol non-hygienically acquires a
@tech{scope} at the point where its binding is resolved or where it
creates a binding.
A @deftech{scope} corresponds to a particular binding context. It can
be a module context, an internal definition context, or a binding site
for an expression form like the formals of a @racket[lambda] or the
right-hand side of a @racket[letrec].
An identifier @tech{syntax object} created by @racket[quote-syntax] closes
over a binding at the point where it is created, closing over the
enclosing module scope if the identifier is not (yet) bound. The
closure does not change if the identifier is nested in a later
@racket[quote-syntax] form. Identifiers that are introduced by macros
are not automatically given a scope or otherwise distinguished from
identifiers that appeared as input to a macro, and a plain symbol is
implicitly coerced to a syntax object only at the point where it binds
or where its binding is resolved as a reference.
There is no @realracket[quasisyntax], @realracket[unsyntax], or
@realracket[unsyntax-splicing] analog, since @racket[quasiquote],
@racket[unquote], and @racket[unquote-splicing] are already convenient
enough for most purposes. To generate a fresh symbol for the output of
a macro expansion, use @racket[string->uninterned-symbol].}
@defform[(quote-module-path)]{
Produces the module path of the enclosing module.}
@subsection{Definition Forms}
@defform*[[(define id expr)
(define (id . formals) body ...++)]]{
Like @realracket*[define] from @racketmodname[racket], but without
keyword arguments and header nesting for curried functions.}
@defform*[[(define-syntax id expr)
(define-syntax (id . formals) body ...++)]]{
Analogous to @realracket*[define-syntax] from @racketmodname[racket],
binds @racket[id] as a macro. The value of @racket[expr] must be
either a procedure (of one argument) or a value constructed by
@racket[context-consumer].
If @racket[expr] produces a @racket[context-consumer] wrapper, then
when @racket[id] is used for a macro invocation, the wrapped procedure
receives three arguments: the macro use as syntax, a function that
acts like @realracket[free-identifier=?], and either @racket[#f] or an
inferred-name string. (In @racketmodname[racket],
@realracket[free-identifier=?] and @realracket[syntax-local-name] are
implicitly parameterized over the context of a macro invocation.
Explicitly providing a comparison procedure and name string to a macro
implementation, instead, avoids the implicit parameterization.)
See @racket[quote-syntax] for more information about the
representation of syntax that a macro function consumes and produces.}
@defform[(struct id (field-id ...))]{
Analogous to @realracket*[struct] from @racketmodname[racket], but
defining only @racket[id] as a constructor,
@racket[id]@racketidfont{?} as a predicate,
@racket[id]@racketidfont{-}@racket[field-id] as an accessor for each
@racket[field-id], and
@racket[id]@racketidfont{-set-}@racket[field-id] as a
functional-update operation (along the lines of
@realracket[struct-copy]) for each @racket[field-id].}
@defform[(include module-path)]{
Splices the content of the module identified by @racket[module-path],
assuming that @racket[module-path] is implemented in a language like
@racketmodname[zuo/datum].}
@deftogether[(
@defform[#:literals (only-in rename-in)
(require spec ...)
#:grammar ([spec module-path
(only-in module-path
maybe-renamed-id ...)
(rename-in module-path
renamed-id ...)]
[maybe-renamed-id id
renamed-id]
[renamed-id [provided-id id]])]
@defform[#:literals (rename-out all-from-out)
(provide spec ...)
#:grammar ([spec id
(rename-out renamed-id ...)
(all-from-out module-path)]
[maybe-renamed-id id
renamed-id]
[renamed-id [id provided-id]])]
)]{
Like @realracket*[require provide] from @racketmodname[racket], but a
@racket[require] can appear in any definition context, while
@racket[provide] is not allowed in @tech{submodules}.}
@defform[(module+ id defn-or-expr ...)]{
Declares a kind of @deftech{submodule}, roughly analogous to
@realracket[module+] from @racketmodname[racket], but without allowing
submodules nested in submodules.
A submodule becomes a procedure of zero arguments that is a mapped
from the symbol form of @racket[id] in the enclosing module's
representation as a hash table (see @secref["module-protocol"]).
Calling the procedure evaluates the @racket[defn-or-expr] content of
the submodule, where expression results are printed and the procedure's
result is @racket[(void)].
When Zuo loads a starting module (see @secref["running"]), it checks
for a @racketidfont{main} submodule and runs it if one is found.}
@; ----------------------------------------
@section{Booleans}
Zuo booleans are written @racket[#t] or @racket[#true] and @racket[#f]
or @racket[#false]. Any value other than @racket[#f] counts as true
for conditionals.
@deftogether[(
@defproc[(boolean? [v any/c]) boolean?]
@defproc[(not [v any/c]) boolean?]
)]{
Just like @realracket*[boolean? not] from @racketmodname[racket].}
@defproc[(eq? [v1 any/c] [v2 any/c]) boolean?]{
Analogous to @realracket[eq?] from @racketmodname[racket], but even small Zuo
numbers are not necessarily @racket[eq?] when they are @racket[=].}
@defproc[(equal? [v1 any/c] [v2 any/c]) boolean?]{
Analogous to @realracket[equal?] from @racketmodname[racket].}
@section{Numbers}
A Zuo number corresponds to a 64-bit two's complement representation
with modular arithmetic (i.e., wraparound on overflow). It is always
written in decimal form with a leading @litchar{-} for negative
numbers.
@defproc[(integer? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is an integer, @racket[#f] otherwise.}
@deftogether[(
@defproc[(+ [z integer?] ...) integer?]
@defproc*[([(- [z integer?]) integer?]
[(- [z integer?] [w integer?] ...+) integer?])]
@defproc[(* [z integer?] ...) integer?]
@defproc[(quotient [n integer?] [m integer?]) integer?]
@defproc[(remainder [n integer?] [m integer?]) integer?]
@defproc[(modulo [n integer?] [m integer?]) integer?]
@defproc[(= [z integer?] [w integer?]) boolean?]
@defproc[(< [x integer?] [y integer?]) boolean?]
@defproc[(<= [x integer?] [y integer?]) boolean?]
@defproc[(> [x integer?] [y integer?]) boolean?]
@defproc[(>= [x integer?] [y integer?] ...) boolean?]
@defproc[(bitwise-ior [n integer?] [m integer?]) integer?]
@defproc[(bitwise-and [n integer?] [m integer?]) integer?]
@defproc[(bitwise-xor [n integer?] [m integer?]) integer?]
@defproc[(bitwise-not [n integer?]) integer?]
)]{
Analogous to @realracket*[+ - * quotient remainder modulo = < <= > >=
bitwise-ior bitwise-and bitwise-xor bitwise-not] from
@racketmodname[racket], but on Zuo integers and sometimes constrained
to two arguments.
@history[#:changed "1.9" @elem{Added @racket[remainder] and changed @racket[modulo]
to match Racket.}]}
@section{Pairs and Lists}
Zuo pairs and lists work the same as in Racket with the same textual
representation.
@deftogether[(
@defproc[(pair? [v any/c])
boolean?]
@defproc[(null? [v any/c])
boolean?]
@defproc[(list? [v any/c])
boolean?]
@defproc[(cons [a any/c] [d any/c])
pair?]
@defproc[(car [p pair?])
any/c]
@defproc[(cdr [p pair?])
any/c]
@defproc[(list [v any/c] ...)
list?]
@defproc[(list* [v any/c] ... [tail any/c])
any/c]
@defproc*[([(append [lst list?] ...) list?]
[(append [lst list?] ... [v any/c]) any/c])]
@defproc[(reverse [lst list?]) list?]
@defproc[(length [lst list?]) integer?]
@defproc[(list-ref [lst pair?] [pos integer?]) any/c]
@defproc[(list-set [lst pair?] [pos integer?] [v any/c]) any/c]
@defproc[(list-tail [lst any/c] [pos integer?]) any/c]
)]{
Just like @realracket*[pair? null? cons car cdr list? list* append
reverse list-ref list-set list-tail] from @racketmodname[racket], except that
@racket[list?] takes time proportional to the length of the list.}
@deftogether[(
@defproc[(caar [p pair?]) any/c]
@defproc[(cadr [p pair?]) any/c]
@defproc[(cdar [p pair?]) any/c]
@defproc[(cddr [p pair?]) any/c]
)]{
Just like @realracket*[caar cadr cdar cddr] from @racketmodname[racket].}
@deftogether[(
@defproc[(map [proc procedure?] [lst list?] ...+)
list?]
@defproc[(for-each [proc (any/c . -> . any/c)] [lst list?])
void?]
@defproc[(foldl [proc (any/c any/c . -> . any/c)] [init any/c] [lst list?])
any/c]
@defproc[(andmap [proc (any/c . -> . any/c)] [lst list?])
any/c]
@defproc[(ormap [proc (any/c . -> . any/c)] [lst list?])
any/c]
@defproc[(filter [proc (any/c . -> . any/c)] [lst list?])
list?]
@defproc[(sort [lst list?] [less-than? (any/c any/c . -> . any/c)])
list?]
)]{
Like @realracket*[map for-each foldl andmap ormap filter] from
@racketmodname[racket], but mostly restricted to a single list.}
@deftogether[(
@defproc[(member [v any/c] [lst list?])
(or/c pair? #f)]
@defproc[(assoc [v any/c] [lst list?])
(or/c pair? #f)]
@defproc[(remove [v any/c] [lst list?])
(or/c pair? #f)]
)]{
Like @realracket*[member assoc remove] from @racketmodname[racket].}
@section{Strings}
Zuo @deftech{strings} are sequences of bytes.
@deftogether[(
@defproc[(string? [v any/c]) boolean?]
@defproc[(string [char integer?] ...) string?]
@defproc[(string-length [str string?]) integer?]
@defproc[(string-ref [str string?] [k integer?]) integer?]
@defproc[(substring [str string?]
[start integer?]
[end integer? (string-length str)]) string?]
@defproc[(string=? [str1 string?] [str2 string?]) boolean?]
@defproc[(string-ci=? [str1 string?] [str2 string?]) boolean?]
@defproc[(string<? [str1 string?] [str2 string?]) boolean?]
)]{
Analogous to @realracket*[string? string string-length string-ref substring
string=? string-ci=? string<?] from @racketmodname[racket], or more precisely analogous to
@realracket*[bytes? bytes bytes-length bytes-ref subbytes bytes=? bytes<?]
(and, in principle, @racket[bytes-ci=?]) from @racketmodname[racket].}
@defproc[(string-u32-ref [str string?] [k integer?]) integer?]{
Returns the two's complement interpretation of four bytes in
@racket[str] starting at index @racket[k] using the host machine's
endianness.}
@defproc[(string->integer [str string?]) (or/c integer? #f)]{
Tries to parse @racket[str] as an integer returning @racket[#f] if
that fails.}
@defproc[(string-sha256 [str string?]) string?]{
Returns the SHA-256 hash of @racket[str] as a 64-digit hexadecimal string.
See also @racket[file-sha256] and @racket[sha256-length].}
@defform[(char str)]{
Expands to @racket[(string-ref str 0)], where @racket[str] must be a
literal string of length 1.}
@defproc*[([(string-split [str string?]) (listof string?)]
[(string-split [str string?] [sep string?]) (listof string?)])]{
Breaks @racket[str] into a sequence of substrings that have a
non-empty separator string in between. When @racket[sep] is not
provided, @racket[" "] is used as the separator, and empty strings are
filtered from the result list. When @racket[sep] is provided, empty
strings are @emph{not} filtered from the result list.}
@defproc[(string-join [strs list?] [sep string? " "]) string?]{
Concatenates the strings in @racket[strs] with @racket[sep] between
each pair of strings.}
@defproc[(string-trim [str string?] [edge-str string? " "]) string?]{
Removes any number of repetitions of @racket[edge-str] from the start
and end of @racket[str].}
@defproc[(string-tree? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a string or if it is a list where
@racket[string-tree?] returns @racket[#t] for each element,
@racket[#f] otherwise. The flattened form of a string tree is a list
of its strings in order. See also @racket[process] and
@racket[build-shell].}
@section{Symbols}
Zuo symbols are @deftech{interned} by the reader, where two interned
symbols are @racket[eq?] when they have the same string content. An
@deftech{uninterned} symbol is @racket[eq?] only to itself. Zuo
symbols are the only kind of value that can be used as a key for a Zuo
@tech{hash table}.
The textual representation of symbols does not include escapes for
special character, unlike the way @litchar{|} works in Racket.
Symbols with those characters will print in a way that cannot be read
back into Zuo.
@deftogether[(
@defproc[(symbol? [v any/c]) boolean?]
@defproc[(symbol->string [sym symbol?]) string?]
@defproc[(string->symbol [str string?]) symbol?]
@defproc[(string->uninterned-symbol [str string?]) symbol?]
)]{
Analogous to @realracket*[symbol? symbol->string string->symbol
string->uninterned-symbol] from @racketmodname[racket], but
@racket[string->symbol] accepts only strings that do not contain the
null character.}
@section{Hash Tables (Persistent Maps)}
Zuo @tech{hash tables} do not actually have anything to do with
hashing, but they're called that for similarity to Racket. A hash table
maps symbols to other values, and updating a hash table produces a new
hash table (which, internally, may share with the original).
Hash tables print in a way analogous to Racket, but there is no reader
support to convert the textual form back into a hash table value.
@deftogether[(
@defproc[(hash? [v any/c]) boolean?]
@defproc[(hash [key symbol?] [val any/c] ... ...) hash?]
@defproc*[([(hash-ref [hash hash?]
[key symbol?])
any/c]
[(hash-ref [hash hash?]
[key symbol?]
[failure-value any/c])
any/c])]
@defproc[(hash-set [hash hash?]
[key symbol?]
[v any/c])
hash?]
@defproc[(hash-remove [hash hash?]
[key symbol?])
hash?]
@defproc[(hash-keys [hash hash?]) (listof symbol?)]
@defproc[(hash-count [hash hash?]) integer?]
@defproc[(hash-keys-subset? [hash1 hash?] [hash2 hash?])
boolean?]
)]{
Analogous to @realracket*[hash? hash hash-ref hash-set hash-remove
hash-keys hash-count hash-keys-subset?] from @racketmodname[racket].
Besides being constrained to symbol keys, there are two additional
differences:
@itemlist[
@item{the third argument to @racket[hash-ref], when supplied, is
always used as a value to return if a key is missing, as
opposed to a failure thunk; and}
@item{the @racket[hash-keys] function returns interned keys sorted
alphabetically.}
]}
@section{Procedures}
@deftogether[(
@defproc[(procedure? [v any/c]) any/c]
@defproc[(apply [proc procedure?] [lst list?]) any/c]
@defproc[(call/cc [proc (any/c . -> . any/c)]) any/c]
@defproc[(call/prompt [proc (-> any/c)] [tag symbol?]) any/c]
@defproc[(continuation-prompt-available? [tag symbol?]) boolean?]
)]{
Like @realracket*[procedure? apply call/cc
call-with-continuation-prompt continuation-prompt-available?] from
@racketmodname[racket], but @racket[apply] accepts only two arguments,
@racket[call/cc] has no prompt-tag argument and captures up to the
nearest enclosing prompt of any tag, @racket[call/prompt] expects a
symbol for a prompt tag, and @racket[continuation-prompt-available?]
checks only whether the immediately enclosing prompt has the given tag.}
@section{Paths}
A @deftech{path string} is a @tech{string} that is not empty
and contains no null bytes.
@defproc[(path-string? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a path string, @racket[#f] otherwise.}
@defproc[(relative-path? [path path-string?]) boolean?]{
Returns @racket[#t] if @racket[path] is a relative path, @racket[#f] otherwise.}
@defproc[(build-raw-path [base path-string?] [rel path-string?] ...) path-string?]{
Combines @racket[base] path (absolute or relative) with relative
@racket[rel] paths, adding path separators as needed.}
@defproc[(build-path [base path-string?] [rel path-string?] ...) path-string?]{
Similar to @racket[build-raw-path], but any @filepath{.} or
@filepath{..} element in a @racket[rel] is syntactically eliminated,
and separators in @racket[rel] are normalized. Removing @filepath{..}
elements may involve syntactically resolving elements at the end of
@racket[base]. Furthermore, if base is at some point reduced to
@racket["."], it will not be prefixed on the result.}
@defproc[(split-path [path path-string?]) pair?]{
Splits @racket[path] into its directory (if any) and a final element
components. If @racket[path] has only a single element, the
@racket[car] of the result is @racket[#f], and the @racket[cdr] is
@racket[path] unchanged; otherwise, the final element is returned
without trailing separators.}
@defproc[(explode-path [path path-string?]) (listof path-string?)]{
Split @racket[path] into a list of individual path elements by
repeatedly applying @racket[split-path].}
@defproc[(simple-form-path [path path-string?]) path-string?]{
Syntactically normalizes @racket[path] by eliminating @filepath{.} and
@filepath{..} elements (except for @filepath{..} at the start that
cannot be eliminated), removing redundant path separators, and making
all path separators the platform default (on Windows).}
@defproc[(find-relative-path [base path-string?] [path path-string?]) path-string?]{
Attempts to find a path relative to @racket[base] that accesses the
same file or directory as @racket[path]. Both @racket[base] and
@racket[path] must be normalized in the sense of
@racket[simple-form-path], otherwise @filepath{.} and @filepath{..}
elements are treated as normal path elements. Assuming that @racket[base]
and @racket[path] are normalized, the result is always normalized.
The result path depends on whether @racket[base] and @racket[path] are
relative or absolute:
@itemlist[
@item{If both are relative, the result is always a relative path. If
@racket[base] starts with @filepath{..} elements that are not
matched by @racket[path], then elements are drawn from
@racket[(hash-ref (runtime-env) 'dir)].}
@item{If both are absolute, the result is absolute if @racket[base]
and @racket[path] do not share a root element, otherwise the
result is relative.}
@item{If @racket[path] is absolute and @racket[base] is relative,
@racket[path] is returned as-is. The intent of this mode is to
preserve the ``absoluteness'' of @racket[path] in a setting
that otherwise works in terms of relative paths.}
@item{If @racket[base] is absolute and @racket[path] is relative,
@racket[path] is converted to absolute via
@racket[path->complete-path], and the result is as when both
are absolute (so, the result may still be absolute).}
]}
@defproc[(path-only [path path-string?]) path-string?]{
Returns @racket[path] without its final path element in the case that
@racket[path] is not syntactically a directory. If @racket[path] has
only a single, non-directory path element, @racket["."] is returned.
If @racket[path] is syntactically a directory, then @racket[path] is
returned unchanged.}
@defproc[(file-name-from-path [path path-string?]) (or/c path-string? #f)]{
Returns the last element of @racket[path] in the case that
@racket[path] is not syntactically a directory, @racket[#f]
otherwise.}
@defproc[(path->complete-path [path path-string?]) path-string?]{
Returns @racket[path] if it is absolute, otherwise returns
@racket[(build-path (hash-ref (runtime-env) 'dir) path)].}
@defproc[(path-replace-extension [path path-string?] [suffix string?]) path-string?]{
Removes any @litchar{.} suffix from the last element of @racket[path],
and then appends @racket[suffix] to the end of the path. A @litchar{.}
at the start of a path element does not count as a file suffix.}
@defidform[at-source]{
Expands to a function that acts like @racket[build-path] starting from
the enclosing module's directory. That is, expands to a function
roughly equivalent to
@racketblock[(lambda args
(apply build-path (cons (path-only (quote-module-path))
args)))]
If the argument to the function is an absolute path, however, the
enclosing module's directory is ignored, and the function acts simply
like @racket[build-path].}
@section{Opaque Records}
@defproc[(opaque [key any/c] [val any/c]) any/c]{
Returns an opaque record that encapsulates @racket[val] with access
allowed via @racket[key].}
@defproc[(opaque-ref [key any/c] [v any/c] [failure-val any/c]) any/c]{
Returns the value encapsulated in @racket[v] if it is an opaque
object with access allowed via @racket[key], @racket[failure-val] otherwise.}
@section{Variables}
A @tech{variable} is a value with a name that contains another
value. The contained value is initially undefined, and attempting to
access the contained value before it's set results in an error where
the variable's name is used in the error message. A variable's
contained value can be set only once.
@defproc[(variable? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a variable, @racket[#f] otherwise.}
@defproc[(variable [name symbol?]) variable?]{
Creates a variable named by @racket[name] and without a value until
one is installed with @racket[variable-set!].}
@defproc[(variable-set! [var variable?] [val any/c]) void?]{
Sets the value contained by @racket[var] to @racket[val] or errors if
@racket[var] already has a contained value.}
@defproc[(variable-ref [var variable?]) any/c]{
Returns the value contained by @racket[var] or errors if @racket[var]
does not yet have a contained value.}
@section{Modules and Evaluation}
A @deftech{module path} is a path string or a symbol, where a symbol
must contain only the letters @litchar{A}-@litchar{Z},
@litchar{a}-@litchar{z}, @litchar{0}-@litchar{9}, @litchar{-}, @litchar{+}, @litchar{_}, or
@litchar{/}. Furthermore, @litchar{/} in a symbol module path cannot
be at the start, end, or adjacent to another @litchar{/}.
@defproc[(module-path? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @tech{module path}, @racket[#f]
otherwise.}
@defproc[(build-module-path [base module-path?] [rel-path path-string?]) module-path?]{
Analogous to @racket[build-path], but for @tech{module paths}. The
@racket[rel-path] string must end with @litchar{.zuo}, and the
characters of @racket[rel-path] must be allowable in a symbol module
paths, except for a @litchar{.} in @filepath{.} and @filepath{..}
elements or a @litchar{.zuo} suffix.}
@defproc[(module->hash [mod-path module-path?]) hash?]{
Loads @racket[mod-path] if it has not been loaded already, and returns
the @tech{hash table} representation of the loaded module. See also
@secref["module-protocol"].}
@defproc[(dynamic-require [mod-path module-path?] [export symbol?]) any/c]{
Like @racket[module->hash], but extracts an exported value. The module
referenced by @racket[mod-path] must be implemented in
@racketmodname[zuo], @racketmodname[zuo/hygienic], or a derived
compatible language.}
@defproc[(kernel-eval [s-exp any/c]) any/c]{
Evaluates a term as if it appeared in a @racketmodname[zuo/kernel] module
(but the result does not have to be a @tech{hash table}).}
@defproc[(kernel-env) hash?]{
Returns a @tech{hash table} that maps each primitive and constant name
available in the body of a @racketmodname[zuo/kernel] module to its value.}
@section{Void}
@defproc[(void? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is the unique @deftech{void} value,
@racket[#f] otherwise.}
@defproc[(void [v any/c] ...) void?]{
Accepts any number of arguments and ignores them, returning the void
value.}
@section{Reading and Writing Objects}
@defproc[(string-read [str string?] [start integer? 0] [where any/c #f]) list?]{
Reads all S-expressions in @racket[str], starting at index
@racket[start] and returning a list of the S-expressions (in order as
they appeared in the string). The @racket[where] argument, if not
@racket[#f], is used to report the source of errors.
See also @secref["reader"].}
@deftogether[(
@defproc[(~v [v any/c] ...) string?]
@defproc[(~a [v any/c] ...) string?]
@defproc[(~s [v any/c] ...) string?]
)]{
Like @realracket*[~v ~a ~s], but with no formatting options. These
three format options corresponds to @realracket[print] style,
@realracket[display] style, and @realracket[write] style,
respectively.
Unlike uninterned symbols in @racketmodname[racket], Zuo uninterned
symbols format in @realracket[print] and @realracket[write] styles with
@litchar{#<symbol:}...@litchar{>}. @tech{Opaque objects},
@tech{handles}, and @tech{variables} print with
@litchar{#<:}...@litchar{>} notation in all styles.}
@deftogether[(
@defproc[(display [v any/c]) void?]
@defproc[(displayln [v any/c]) void?]
)]{
Convenience output functions that are analogous to @realracket*[display
displayln] from @racketmodname[racket]. They use @racket[~a] and
@racket[(fd-open-output 'stdout)].}
@defproc[(error [v any/c] ...) void?]{
Errors (and exits) after printing the @racket[v]s to standard error,
using an error color if standard error is a terminal.
If the first @racket[v] is a string, its characters are printed
followed by @litchar{: }. All
other @racket[v]s (including the first one if it's not a string) are
combined using @racket[~v], and the resulting string's characters are
printed.}
@defproc[(alert [v any/c] ...) void?]{
Prints to standard output using the same formatting rules as
@racket[error], but in an alert color for terminals. This function is
useful for simple logging and debugging tasks.}
@defproc[(arity-error [name (or/c string? #f)] [args list?]) void?]{
Errors (and exits) after printing an error about @racket[name]
receiving the wrong number of arguments, where @racket[args] are the
arguments that were supplied.}
@defproc[(arg-error [who symbol?] [what string?] [v any/c]) void?]{
Errors (and exits) after printing an error from @racket[who] about a
@racket[what] expected in place of @racket[v].}
@section{Syntax Objects}
A @deftech{syntax object} combines a symbol with a binding scope,
where the two are used to determine a binding when the identifier is
used in a macro expansion.
@deftogether[(
@defproc[(identifier? [v any/c]) boolean?]
@defproc[(syntax-e [v identifier?]) symbol?]
@defproc[(syntax->datum [v any/c]) any/c]
@defproc[(datum->syntax [ctx identifier?] [v any/c]) any/c]
@defproc[(bound-identifier=? [id1 identifier?]
[id2 identifier?]) boolean?]
)]{
Analogous to @realracket*[identifier? syntax-e syntax->datum
datum->syntax bound-identifier=?] from @racketmodname[racket]. Plain
symbols count as an identifier, however, and for
@racket[bound-identifier=?], a symbol is equal only to itself. The
@racket[datum->syntax] function always just returns its second
argument.}
@defproc[(syntax-error [message string?] [stx any/c]) void?]{
Exits as an error after printing @racket[message], @litchar{: }, and
@racket[(~s (syntax->datum stx))].}
@deftogether[(
@defproc[(bad-syntax [stx any/c]) void?]
@defproc[(misplaced-syntax [stx any/c]) void?]
@defproc[(duplicate-identifier [stx any/c]) void?]
)]{
Calls @racket[syntax-error] with a suitable error message and @racket[stx].}
@deftogether[(
@defproc[(context-consumer [proc procedure]) context-consumer?]
@defproc[(context-consumer? [v any/c]) boolean?]
)]{
The @racket[context-consumer] constructor wraps a procedure to
indicate that it expects three arguments as a macro transformer; see
@racket[define-syntax] for more information. The
@racket[context-consumer?] predicate recognizes values produced by
@racket[context-consumer].}
@section{Files, Streams, and Processes}
Files, input and output streams, and processes are all
represented as @tech{handles}.
@defproc[(handle? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a @tech{handle}, @racket[#f]
otherwise.}
@defproc[(fd-open-input [filename (or/c path-string? 'stdin integer?)]
[options hash? (hash)])
handle?]{
Opens a file for reading, obtains a reference to standard input when
@racket[filename] is @racket['stdin], or (on Unix) obtains a
reference to an existing file descriptor when @racket[filename]
is an integer. The result handle can be used
with @racket[fd-read] and closed with @racket[fd-close].
No keys are currently recognized for @racket[options], so it must be
an empty hash table.}
@deftogether[(
@defproc[(fd-open-output [filename (or/c path-string? 'stdout 'stderr integer?)]
[options hash? (hash)]) handle?]
@defthing[:error hash?]
@defthing[:truncate hash?]
@defthing[:must-truncate hash?]
@defthing[:append hash?]
@defthing[:update hash?]
@defthing[:can-update hash?]
)]{
The @racket[fd-open-output] procedure opens a file for writing,
obtains a reference to standard output when @racket[filename] is
@racket['stdout], obtains a reference to standard error when
@racket[filename] is @racket['stderr], or (on Unix) obtains a
reference to an existing file descriptor when @racket[filename]
is an integer. When opening a file,
@racket[options] specifies options as described below, but
@racket[options] must be empty for @racket['stdout] or
@racket['stderr]. The result handle can be used with @racket[fd-write]
and closed with @racket[fd-close].
In @racket[options], a single key is currently recognized:
@racket['exists]. The mapping for @racket['exists] must be one of the
symbols accepted for @racket[#:exists] by
@realracket[open-output-file] from @racketmodname[racket], but not
@racket['replace] or @racket['truncate/replace], and the default
mapping is @racket['error]. Any other key in @racket[options] is an
error.
The @racket[:error], @racket[:truncate], @racket[:must-truncate],
@racket[:append], @racket[:update], and @racket[:can-update] hash
tables each map @racket['exists] to the corresponding mode.}
@defproc[(fd-close [handle handle?]) void?]{
Closes the file or stream associated with @racket[handle], if it
refers to an open input or output stream. Any other kind of
@racket[handle] triggers an error.}
@defproc[(fd-read [handle handle?] [amount (or/c integer? eof 'avail)]) (or/c string? eof)]{
Reads from the input file or input stream associated with
@racket[handle], erroring for any other kind of @racket[handle]. The
@racket[amount] argument can be a non-negative integer to read up to
that many bytes, @racket[eof] to read all content up to an