doc.txt (24022B)
1 (The rest of the Scribble documentation is now a separate manual.) 2 3 The Scribble Reader 4 ------------------- 5 6 The Scribble @-reader is designed to be a convenient facility for 7 using free-form text in Scheme code, where "@" is chosen as one of 8 the least-used characters in Scheme code. 9 10 You can use the reader via Racket's `#reader' form: 11 12 #reader(lib "reader.ss" "scribble")@{This is free-form text!} 13 14 Note that the reader will only read @-forms as S-expressions. The 15 meaning of these S-expressions depends on the rest of your own code. 16 17 A PLT Scheme manual more likely starts with 18 19 #reader(lib "docreader.ss" "scribble") 20 21 which installs a reader, wraps the file content afterward into a 22 Racket module, and parses the body into a document using 23 "decode.ss". 24 25 Another way to use the reader is to use the `use-at-readtable' 26 function to switch the current readtable to a readtable that parses 27 @-forms. You can do this in a single command line: 28 29 mzscheme -ile scribble/reader "(use-at-readtable)" 30 31 *** Concrete Syntax 32 33 Informally, the concrete syntax of @-forms is 34 35 "@" <cmd> "[" <datum> ... "]" "{" <text-body> ... "}" 36 37 where all three parts after "@" are optional, but at least one should 38 be present. (Note that spaces are not allowed between the three 39 parts.) "@" is set as a non-terminating reader macro, so it can be 40 used as usual in Scheme identifiers unless you want to use it as a 41 first character of an identifier; in this case you need to quote with 42 a backslash (`\@foo') or quote the whole identifier with bars 43 (`|@foo|'). 44 45 (define |@foo| '\@bar@baz) 46 47 Of course, "@" is not treated specially in Scheme strings, character 48 constants, etc. 49 50 Roughly, a form matching the above grammar is read as 51 52 (<cmd> <datum> ... <parsed-body> ...) 53 54 where <parsed-body> is the translation of each <text-body> in the 55 input. Thus, the initial <cmd> determines the Scheme code that the 56 input is translated into. The common case is when <cmd> is a Scheme 57 identifier, which generates a plain Scheme form. 58 59 A <text-body> is made of text, newlines, and nested @-forms. Note 60 that the syntax for @-forms is the same in a <text-body> context as in 61 a Scheme context. A <text-body> that isn't an @-form is converted to 62 a string expression for its <parsed-body>, and newlines are converted 63 to "\n" expressions. 64 65 @foo{bar baz 66 blah} 67 --reads-as--> 68 (foo "bar baz" "\n" "blah") 69 70 @foo{bar @baz[3] 71 blah} 72 --reads-as--> 73 (foo "bar " (baz 3) "\n" "blah") 74 75 @foo{bar @baz{3} 76 blah} 77 --reads-as--> 78 (foo "bar " (baz "3") "\n" "blah") 79 80 @foo{bar @baz[2 3]{4 5} 81 blah} 82 --reads-as--> 83 (foo "bar " (baz 2 3 "4 5") "\n" "blah") 84 85 Note that spaces are not allowed before a "[" or a "{", or they will 86 be part of the following text (or Scheme code). (More on using braces 87 in body texts below.) 88 89 @foo{bar @baz[2 3] {4 5}} 90 --reads-as--> 91 (foo "bar " (baz 2 3) " {4 5}") 92 93 When the above @-forms appear in a Scheme expression context, the 94 lexical environment must provide bindings for `foo' (as a procedure or 95 a macro). 96 97 (let* ([formatter (lambda (fmt) 98 (lambda args (format fmt (apply string-append args))))] 99 [bf (formatter "*~a*")] 100 [it (formatter "/~a/")] 101 [ul (formatter "_~a_")] 102 [text string-append]) 103 @text{@it{Note}: @bf{This is @ul{not} a pipe}.}) 104 --> "/Note/: *This is _not_ a pipe*." 105 106 If you want to see the expression that is actually being read, you can 107 use Scheme's `quote'. 108 109 '@foo{bar} 110 111 ** The Command Part 112 113 Besides being a Scheme identifier, the <cmd> part of an @-form can 114 have Scheme punctuation prefixes, which will end up wrapping the 115 *whole* expression. 116 117 @`',@foo{blah} --reads-as--> `',@(foo "blah") 118 119 When writing Scheme code, this means that @`',@foo{blah} is exactly 120 the same as `@',@foo{blah} and `',@@foo{blah}, but unlike the latter 121 two, the first construct can appear in body texts with the same 122 meaning, whereas the other two would not work (see below). 123 124 After the optional punctuation prefix, the <cmd> itself is not limited 125 to identifiers; it can be *any* Scheme expression. 126 127 @(lambda (x) x){blah} --reads-as--> ((lambda (x) x) "blah") 128 @`(unquote foo){blah} --reads-as--> `(,foo "blah") 129 130 In addition, the command can be omitted altogether, which will omit it 131 from the translation, resulting in an S-expression that usually 132 contains, say, just strings: 133 134 @{foo bar --reads-as--> ("foo bar" "\n" "baz") 135 baz} 136 137 @'{foo bar --reads-as--> '("foo bar" "\n" "baz") 138 baz} 139 140 If the command part begins with a ";" (with no newline between the "@" 141 and the ";"), then the construct is a comment. There are two comment 142 forms, one for arbitrary-text and possibly nested comments, and 143 another one for line comments: 144 145 @;{<anything> ...} 146 147 @;<anything-else-without-newline> 148 149 In the first form, the commented body must still parse correctly; see 150 the description of the body syntax below. In the second form, all 151 text from the "@;" to the end of the line *and* all following spaces 152 (or tabs) are part of the comment (similar to "%" comments in TeX). 153 154 @foo{bar @; comment --reads-as--> (foo "bar bazblah") 155 baz@; 156 blah} 157 158 Tip: if you're editing in a Scheme-aware editor (like DrRacket or 159 Emacs), it is useful to comment out blocks like this: 160 161 @;{ 162 ... 163 ;} 164 165 so the editor does not treat the file as having unbalanced 166 parenthesis. 167 168 If only the <cmd> part of an @-form is specified, then the result is 169 the command part only, without an extra set of parenthesis. This 170 makes it suitable for Scheme escapes in body texts. (More on this 171 below, in the description of the body part.) 172 173 @foo{x @y z} --reads-as--> (foo "x " y " z") 174 @foo{x @(* y 2) z} --reads-as--> (foo "x " (* y 2) " z") 175 @{@foo bar} --reads-as--> (foo " bar") 176 177 Finally, note that there are currently no special rules for using "@" 178 in the command itself, which can lead to things like: 179 180 @@foo{bar}{baz} --reads-as--> ((foo "bar") "baz") 181 182 ** The Datum Part 183 184 The datum part can contain arbitrary Scheme expressions, which are 185 simply stacked before the body text arguments: 186 187 @foo[1 (* 2 3)]{bar} --reads-as--> (foo 1 (* 2 3) "bar") 188 @foo[@bar{...}]{blah} --reads-as--> (foo (bar "...") "blah") 189 190 The body part can still be omitted, which is essentially an 191 alternative syntax for plain (non-textual) S-expressions: 192 193 @foo[bar] --reads-as--> (foo bar) 194 @foo{bar @f[x] baz} --reads-as--> (foo "bar " (f x) " baz") 195 196 The datum part can be empty, which makes no difference, except when 197 the body is omitted. It is more common, however, to use an empty body 198 for the same purpose. 199 200 @foo[]{bar} --reads-as--> (foo "bar") 201 @foo[] --reads-as--> (foo) 202 @foo --reads-as--> foo 203 @foo{} --reads-as--> (foo) 204 205 The most common use of the datum part is for Scheme forms that expect 206 keyword-value arguments that precede the body of text arguments. 207 208 @foo[#:style 'big]{bar} --reads-as--> (foo #:style 'big "bar") 209 210 ** The Body Part 211 212 The syntax of the body part is intended to be as convenient as 213 possible for free text. It can contain almost any text -- the only 214 characters with special meaning is "@" for sub-@-forms, and "}" for 215 the end of the text. In addition, a "{" is allowed as part of the 216 text, and it makes the matching "}" be part of the text too -- so 217 balanced braces are valid text. 218 219 @foo{f{o}o} --reads-as--> (foo "f{o}o") 220 @foo{{{}}{}} --reads-as--> (foo "{{}}{}") 221 222 As described above, the text turns to a sequence of string arguments 223 for the resulting form. Spaces at the beginning and end of lines are 224 discarded, and newlines turn to individual "\n" strings (i.e., they 225 are not merged with other body parts). (See also the information 226 about newlines and indentation below.) Spaces are *not* discarded if 227 they appear after the open "{" (before the closing "}") when there is 228 also text that follows (precedes) it; specifically, they are preserved 229 in a single-line body. 230 231 @foo{bar} --reads-as--> (foo "bar") 232 @foo{ bar } --reads-as--> (foo " bar ") 233 @foo[1]{ bar } --reads-as--> (foo 1 " bar ") 234 235 If "@" appears in a body, then it is interpreted as Scheme code, which 236 means that the @-reader is applied recursively, and the resulting 237 syntax appears as part of the S-expression, among other string 238 contents. 239 240 @foo{a @bar{b} c} --reads-as--> (foo "a " (bar "b") " c") 241 242 If the nested "@" construct has only a command -- no body or datum 243 parts -- it will not appear in a subform. Given that the command part 244 can be any Scheme expression, this makes "@" a general escape to 245 arbitrary Scheme code. 246 247 @foo{a @bar c} --reads-as--> (foo "a " bar " c") 248 @foo{a @(bar 2) c} --reads-as--> (foo "a " (bar 2) " c") 249 250 This is particularly useful with strings, which can be used to include 251 arbitrary text. 252 253 @foo{A @"}" marks the end} 254 --reads-as--> 255 (foo "A } marks the end") 256 257 Note that the escaped string is (intentionally) merged with the rest 258 of the text. This works for "@" too: 259 260 @foo{The prefix: @"@".} 261 --reads-as--> 262 (foo "The prefix: @.") 263 264 @foo{@"@x{y}" --> (x "y")} 265 --reads-as--> 266 (foo "@x{y} --> (x \"y\")") 267 268 * Alternative Body Syntax 269 270 In addition to the above, there is an alternative syntax for the body, 271 one that specifies a new marker for its end: use "|{" for the opening 272 marker to have the text terminated by a "}|". 273 274 @foo|{...}| 275 --reads-as--> 276 (foo "...") 277 278 @foo|{"}" follows "{"}| 279 --reads-as--> 280 (foo "\"}\" follows \"{\"") 281 282 @foo|{Nesting |{is}| ok}| 283 --reads-as--> 284 (foo "Nesting |{is}| ok") 285 286 This applies to sub-@-forms too -- the "@" must be prefixed with a 287 "|": 288 289 @foo|{Maze 290 |@bar{is} 291 Life!}| 292 --reads-as--> 293 (foo "Maze" "\n" (bar "is") "Life!") 294 295 @t|{In |@i|{sub|@"@"s}| too}| 296 --reads-as--> 297 (t "In " (i "sub@s") " too") 298 299 Note that the subform uses its own delimiters, "{...}" or "|{...}|". 300 This means that you can copy and paste Scribble text with @-forms 301 freely, just prefix the "@" if the immediate surrounding text has a 302 prefix. 303 304 For even better control, you can add characters in the opening 305 delimiter, between the "|" and the "{". Characters that are put there 306 (non alphanumeric ASCII characters only, excluding "{" and "@") should 307 also be used for sub-@-forms, and the end-of-body marker should have 308 these characters in reverse order with paren-like characters ("(", 309 "[", "<") mirrored. 310 311 @foo|<<<{@x{foo} |@{bar}|.}>>>| 312 --reads-as--> 313 (foo "@x{foo} |@{bar}|.") 314 315 @foo|!!{X |!!@b{Y}...}!!| 316 --reads-as--> 317 (foo "X " (b "Y") "...") 318 319 Finally, remember that you can use an expression escape with a Scheme 320 string for confusing situations. This works well when you only need 321 to quote short pieces, and the above works well when you have larger 322 multi-line body texts. 323 324 * Scheme Expression Escapes 325 326 In some cases, you may want to use a Scheme identifier (or a number or 327 a boolean etc.) in a position that touches the following text; in 328 these situations you should surround the escaped Scheme expression by 329 a pair of "|" characters. The text inside the bars is parsed as a 330 Scheme expression. 331 332 @foo{foo@bar.} --reads-as--> (foo "foo" bar.) 333 @foo{foo@|bar|.} --reads-as--> (foo "foo" bar ".") 334 @foo{foo@3.} --reads-as--> (foo "foo" 3.0) 335 @foo{foo@|3|.} --reads-as--> (foo "foo" 3 ".") 336 337 This form is a generic Scheme expression escape, there is no body text 338 or datum part when you use this form. 339 340 @foo{foo@|(f 1)|{bar}} --reads-as--> (foo "foo" (f 1) "{bar}") 341 @foo{foo@|bar|[1]{baz}} --reads-as--> (foo "foo" bar "[1]{baz}") 342 343 This works for string expressions too, but note that unlike the above, 344 the string is (intentionally) not merged with the rest of the text: 345 346 @foo{x@"y"z} --reads-as--> (foo "xyz") 347 @foo{x@|"y"|z} --reads-as--> (foo "x" "y" "z") 348 349 Expression escapes also work with *any* number of expressions, 350 351 @foo{x@|1 (+ 2 3) 4|y} --reads-as--> (foo "x" 1 (+ 2 3) 4 "y") 352 353 @foo{x@|* --reads-as--> (foo "x" * * "y") 354 *|y} 355 356 It seems that "@||" has no purpose -- but remember that these escapes 357 are never merged with the surrounding text, which can be useful when 358 you want to control the sub expressions in the form. 359 360 @foo{Alice@||Bob@| --reads-as--> (foo "Alice" "Bob" "Carol") 361 |Carol} 362 363 Note that "@|{...}|" can be parsed as either an escape expression or 364 as a no-command @-form. The latter is used in this case (since there 365 is little point in Scheme code that uses braces. 366 367 @|{blah}| --reads-as--> ("blah") 368 369 * Comments 370 371 As noted above, there are two kinds of Scribble comments: "@;{...}" is 372 a (nestable) comment for a whole body of text (following the same 373 rules for @-forms), and "@;..." is a line-comment. 374 375 @foo{First line@;{there is still a 376 newline here;} 377 Second line} 378 --reads-as--> 379 (foo "First line" "\n" "Second line") 380 381 One useful property of line-comments is that they continue to the end 382 of the line *and* all following spaces (or tabs). Using this, you can 383 get further control of the subforms. 384 385 @foo{A long @; 386 single-@; 387 string arg.} 388 --reads-as--> 389 (foo "A long single-string arg.") 390 391 Note how this is different from using "@||"s in that strings around it 392 are not merged. 393 394 * Spaces, Newlines, and Indentation 395 396 The Scribble syntax treats spaces and newlines in a special way is 397 meant to be sensible for dealing with text. As mentioned above, 398 spaces at the beginning and end of body lines are discarded, except 399 for spaces between a "{" and text, or between text and a "}". 400 401 @foo{bar} --reads-as--> (foo "bar") 402 403 @foo{ bar } --reads-as--> (foo " bar ") 404 405 @foo{ bar --reads-as--> (foo " bar" "\n" "baz ") 406 baz } 407 408 A single newline that follows an open brace or precedes a closing 409 brace is discarded, unless there are only newlines in the body; other 410 newlines are read as a "\n" string 411 412 @foo{bar --reads-as--> (foo "bar") 413 } 414 415 @foo{ 416 bar --reads-as--> (foo "bar") 417 } 418 419 @foo{ 420 421 bar --reads-as--> (foo "\n" "bar" "\n") 422 423 } 424 425 @foo{ 426 bar 427 --reads-as--> (foo "bar" "\n" "\n" "baz") 428 baz 429 } 430 431 @foo{ --reads-as--> (foo "\n") 432 } 433 434 @foo{ 435 --reads-as--> (foo "\n" "\n") 436 } 437 438 @foo{ bar --reads-as--> (foo " bar" "\n" "baz ") 439 baz } 440 441 In the parsed S-expression syntax, a single newline string is used for 442 all newlines; you can use `eq?' to identify this line. This can be 443 used to identify newlines in the original <text-body>. 444 445 (let ([nl (car @'{ 446 })]) 447 (for-each (lambda (x) (display (if (eq? x nl) "\n... " x))) 448 @`{foo 449 @,@(list "bar" "\n" "baz") 450 blah}) 451 (newline)) 452 --prints--> 453 foo 454 ... bar 455 baz 456 ... blah 457 458 Spaces at the beginning of body lines do not appear in the resulting 459 S-expressions, but the column of each line is noticed, and all-space 460 indentation strings are added so the result has the same indentation. 461 A indentation string is added to each line according to its distance 462 from the leftmost syntax object (except for empty lines). (Note: if 463 you try these examples on a mzscheme REPL, you should be aware that 464 the reader does not know about the "> " prompt.) 465 466 @foo{ --reads-as--> (foo "bar" "\n" 467 bar "baz" "\n" 468 baz "blah") 469 blah 470 } 471 472 @foo{ --reads-as--> (foo "begin" "\n" 473 begin " " "x++;" "\n" 474 x++; "end") 475 end} 476 477 @foo{ --reads-as--> (foo " " "a" "\n" 478 a " " "b" "\n" 479 b "c") 480 c} 481 482 If the first string came from the openning "{" line, it is not 483 prepended with an indentation (but it can affect the leftmost syntax 484 object used for indentation). This makes sense when formatting 485 structured code as well as text (see the last example in the following 486 block). 487 488 @foo{bar --reads-as--> (foo "bar" "\n" 489 baz " " "baz" "\n" 490 bbb} "bbb") 491 492 @foo{ bar --reads-as--> (foo " bar" "\n" 493 baz " " "baz" "\n" 494 bbb} " " "bbb") 495 496 @foo{bar --reads-as--> (foo "bar" "\n" 497 baz "baz" "\n" 498 bbb} "bbb") 499 500 @foo{ bar --reads-as--> (foo " bar" "\n" 501 baz "baz" "\n" 502 bbb} "bbb") 503 504 @foo{ bar --reads-as--> (foo " bar" "\n" 505 baz "baz" "\n" 506 bbb} " " "bbb") 507 508 @text{Some @b{bold 509 text}, and 510 more text.} 511 --reads-as--> 512 (text "Some " (b "bold" "\n" "text") ", and" "\n" "more text.") 513 514 Note that each @-form is parsed to an S-expression that has its own 515 indentation. This means that Scribble source can be indented like 516 code, but if indentation matters then you may need to apply 517 indentation of the outer item to all lines of the inner one. For 518 example, in 519 520 @code{ 521 begin 522 i = 1, r = 1 523 @bold{while i < n do 524 r *= i++ 525 done} 526 end 527 } 528 --reads-as--> 529 (code "begin" "\n" 530 " " "i = 1, r = 1" "\n" 531 " " (bold "while i < n do" "\n" 532 " " "r *= i++" "\n" 533 "done") "\n" 534 "end") 535 536 a formatter will need to apply the 2-space indentation to the 537 rendering of the `bold' body. 538 539 Note that to get a first-line text to be counted as a leftmost line, 540 line and column accounting should be on for the input port 541 (`use-at-readtable' turns them on for the current input port). 542 Without this, 543 544 @foo{x1 545 x2 546 x3} 547 548 will not have 2-space indentations in the parsed S-expression if 549 source accounting is not on, but 550 551 @foo{x1 552 x2 553 x3} 554 555 will (due to the last line). Pay attention to this, as it can be a 556 problem with Scheme code, for example: 557 558 @code{(define (foo x) 559 (+ x 1))} 560 561 For rare situations where spaces at the beginning (or end) of lines 562 matter, you can begin (or end) a line with a "@||". 563 564 @foo{ 565 @|| bar @|| --reads-as--> (foo " bar " "\n" " baz") 566 @|| baz} 567 568 *** Syntax Properties 569 570 The Scribble reader attaches properties to syntax objects. These 571 properties might be useful in rare situations. 572 573 Forms that Scribble reads is marked with a 'scribble property, and a 574 value of a list of three elements: the first is 'form, the second is 575 the number of items that were read from the datum part, and the third 576 is the number of items in the body part (strings, sub-forms, and 577 escapes). In both cases, a 0 means an empty datum/body part, and #f 578 means that the corresponding part was omitted. If the form has 579 neither parts, the property is not attached to the result. This 580 property can be used to give different meanings to expressions from 581 the datum and the body parts, for example, implicitly quoted keywords: 582 583 (define-syntax (foo stx) 584 (let ([p (syntax-property stx 'scribble)]) 585 (syntax-case stx () 586 [(_ x ...) 587 (and (pair? p) (eq? (car p) 'form) (even? (cadr p))) 588 (let loop ([n (/ (cadr p) 2)] 589 [as '()] 590 [xs (syntax->list #'(x ...))]) 591 (if (zero? n) 592 #`(list 'foo `#,(reverse as) #,@xs) 593 (loop (sub1 n) 594 (cons #`(#,(car xs) ,#,(cadr xs)) as) 595 (cddr xs))))]))) 596 @foo[x 1 y (* 2 3)]{blah} 597 --> (foo ((x 1) (y 6)) "blah") 598 599 In addition, the Scribble parser uses syntax properties to mark syntax 600 items that are not physically in the original source -- indentation 601 spaces and newlines. Both of these will have a 'scribble property; an 602 indentation string of spaces will have 'indentation as the value of 603 the property, and a newline will have a '(newline S) value where S is 604 the original newline string including spaces that precede and follow 605 it (which includes the indentation for the following item). This can 606 be used to implement a verbatim environment: drop indentation strings, 607 and use the original source strings instead of single-newline string. 608 Here is an example of this. 609 610 (define-syntax (verb stx) 611 (syntax-case stx () 612 [(_ cmd item ...) 613 #`(cmd 614 #,@(let loop ([items (syntax->list #'(item ...))]) 615 (if (null? items) 616 '() 617 (let* ([fst (car items)] 618 [prop (syntax-property fst 'scribble)] 619 [rst (loop (cdr items))]) 620 (cond [(eq? prop 'indentation) rst] 621 [(not (and (pair? prop) 622 (eq? (car prop) 'newline))) 623 (cons fst rst)] 624 [else (cons (datum->syntax-object 625 fst (cadr prop) fst) 626 rst)])))))])) 627 @verb[string-append]{ 628 foo 629 bar 630 } 631 --> "foo\n bar" 632 633 *** Interface 634 635 The "reader.ss" module provides very little functionality for advanced 636 needs. 637 638 > (read [input-port]) 639 > (read-syntax [source-name] [input-port]) 640 641 These procedures implement the Scribble reader. They do so by 642 constructing a reader table based on the current one, and using that 643 in reading. 644 645 > (read-inside [input-port]) 646 > (read-syntax-inside [source-name] [input-port]) 647 648 These `-inside' variants parse as if starting inside a "@{...}", and 649 they return a (syntactic) list. Useful for implementing languages 650 that are textual by default (see "docreader.ss" for example). 651 652 > (make-at-readtable [keyword-args...]) 653 654 Constructs an @-readtable. The keyword arguments can customize the 655 resulting reader in several ways. 656 657 * #:readtable -- a readtable to base the @-readtable on. Defaults to 658 the current readtable. 659 660 * #:command-char -- the character used for @-forms; defaults to `#\@'. 661 662 * #:datum-readtable -- determines the readtable used for reading the 663 datum part. The default (#t) is to use the @-readtable, otherwise 664 it can be a readtable, or a readtable-to-readtable function that 665 will construct one from the @-readtable. The idea is that you may 666 want to have completely different uses for the datum part, for 667 example, introducing a convenient `key=val' syntax for attributes. 668 669 * #:syntax-post-processor -- a function that is applied on each 670 resulting syntax value after it has been parsed (but before it is 671 wrapped quoting punctuations). You can use this to further control 672 uses of @-forms, for example, making the command be the head of a 673 list: 674 675 (use-at-readtable 676 #:syntax-post-processor 677 (lambda (stx) 678 (syntax-case stx () 679 [(cmd rest ...) #'(list 'cmd rest ...)] 680 [_else (error "@ forms must have a body")]))) 681 682 Beware that the syntax may contain placeholder values at this stage 683 (e.g: the command part), so you can `plant' your own form that will 684 do some plain processing later. For example, here's a setup that 685 uses a `mk-' prefix for all command names: 686 687 (use-at-readtable 688 #:syntax-post-processor 689 (lambda (stx) 690 (syntax-case stx () 691 [(cmd rest ...) #'(add-mk cmd rest ...)] 692 [_else (error "@ forms must have a body")]))) 693 (define-syntax (add-mk stx) 694 (syntax-case stx () 695 [(_ cmd rest ...) 696 (identifier? #'cmd) 697 (with-syntax ([mk-cmd (datum->syntax-object 698 #'cmd 699 (string->symbol 700 (format "mk-~a" (syntax-e #'cmd))) 701 #'cmd)]) 702 (syntax/loc stx (mk-cmd rest ...)))])) 703 704 > (use-at-readtable [keyword-args]) 705 706 Installs the Scribble readtable as the default. Useful for REPL 707 experimentation. (Note: enables line and column tracking.) The given 708 keyword arguments are used with `make-at-readtable'.