The printed form round-trips, and the macro is named before the walk replaces it
Two things the first pass left on unspecified ground. Form.to_source is the only field in the protocol carrying arbitrary literal data -- a macro may build any literal at all -- and Wire.quote escapes only the quote and the backslash, on the stated ground that both readers take everything else as itself. test_repl checks that ground now: every escape the reader knows, both byte-literal spellings to_string would have written raw, and a whole float, each sent through the printer and the socket and back. And expand_all read the macro's name out of a tuple beside the walk that replaces it. OCaml does not promise which half runs first; the failure would have been the wrong macro named, never an error.
This commit is contained in:
parent
3dd9f61b7d
commit
9534d5c077
10
lib/macro.ml
10
lib/macro.ml
@ -429,4 +429,12 @@ let expand_step (f : Form.t) : Form.t * string option =
|
|||||||
let expand_all (f : Form.t) : Form.t * string option =
|
let expand_all (f : Form.t) : Form.t * string option =
|
||||||
match loaded_for [ f ] with
|
match loaded_for [ f ] with
|
||||||
| None -> (f, None)
|
| None -> (f, None)
|
||||||
| Some l -> with_module l (fun () -> (expand_form l f, head_macro l f))
|
| Some l ->
|
||||||
|
with_module l (fun () ->
|
||||||
|
(* The name is read off the *input*, so it has to be taken before the
|
||||||
|
walk replaces it. Bound rather than written as a tuple: OCaml does
|
||||||
|
not promise the order a tuple's components are evaluated in, and
|
||||||
|
[Dev.serve] already carries a comment about the one place that bit.
|
||||||
|
Here it would be silent — the wrong macro named, never an error. *)
|
||||||
|
let name = head_macro l f in
|
||||||
|
(expand_form l f, name))
|
||||||
|
|||||||
@ -261,6 +261,24 @@ let () =
|
|||||||
(* And the one typed at the editor a moment ago, which is the session's
|
(* And the one typed at the editor a moment ago, which is the session's
|
||||||
set rather than the file's: nothing on disk declares [thrice]. *)
|
set rather than the file's: nothing on disk declares [thrice]. *)
|
||||||
expands "a macro defined at the editor, expanded" "(thrice 14)" "(* 14 3)";
|
expands "a macro defined at the editor, expanded" "(thrice 14)" "(* 14 3)";
|
||||||
|
(* Every escape the reader knows, through the printer and then through
|
||||||
|
the wire, and back out as the same text. This is the one field in the
|
||||||
|
protocol that carries arbitrary literal data — a macro may build any
|
||||||
|
literal at all, which is why [Form.to_source] exists beside
|
||||||
|
[Form.to_string] — and [Wire.quote] escapes only the quote and the
|
||||||
|
backslash, on the stated ground that both readers take everything
|
||||||
|
else as itself. This is that ground, checked. *)
|
||||||
|
expands "every string escape survives the printer and the wire"
|
||||||
|
"\"a\\nb\\0c\\\"d\\\\e\\tf\\rg\"" "\"a\\nb\\0c\\\"d\\\\e\\tf\\rg\"";
|
||||||
|
(* And the byte literals, where [to_string] would have written a NUL and
|
||||||
|
a carriage return into the middle of the line. *)
|
||||||
|
expands "a byte literal is written as a name the reader has"
|
||||||
|
"[\\nul \\return \\space \\tab \\newline \\A]"
|
||||||
|
"[\\nul \\return \\space \\tab \\newline \\A]";
|
||||||
|
(* A float that is a whole number, which [to_string]'s %g writes as an
|
||||||
|
integer — and an integer is what it would read back as. *)
|
||||||
|
expands "a whole float keeps its point" "[1.0 0.5 -2.0]" "[1.0 0.5 -2.0]";
|
||||||
|
|
||||||
(* A form with no macro in it comes back as itself, and says so rather
|
(* A form with no macro in it comes back as itself, and says so rather
|
||||||
than echoing and leaving the editor to diff. *)
|
than echoing and leaving the editor to diff. *)
|
||||||
(let r =
|
(let r =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user