modular-macros / ocaml-macros

OCaml with macros
Other
54 stars 3 forks source link

Incorrect behavior of the sprintf macro #16

Open OlivierNicole opened 2 years ago

OlivierNicole commented 2 years ago

There seems to be an incorrect shadowing when splicing a fast exponentiation macro. Using the following power.ml file:

type (_, _) fmt =
    Int : (int -> 'a, 'a) fmt
  | Lit : string -> ('a, 'a) fmt
  | Cat : ('a, 'b) fmt * ('b, 'c) fmt -> ('a, 'c) fmt

static (%) x y = Cat (x, y)

macro rec printk :
    type a b. (string expr -> b expr) -> (a, b) fmt -> a expr =
  fun k -> function
  | Int -> << fun s -> $(k <<string_of_int s>>) >>
  | Lit s -> k (Expr.of_string s)
  | Cat (l, r) ->
      printk (fun x ->
        printk (fun y -> k << $x ^ $y >>) r) l

macro sprintf fmt = printk (fun x -> x) fmt

static p = Lit "(" % Int % Lit "," % Int % Lit ")"

We get the following results in the toplevel at revision https://github.com/modular-macros/ocaml-macros/commit/05372c7248b5a7b1aa507b3c581f710380f17fcd:

# #use "power.ml";;
type (_, _) fmt =
    Int : (int -> 'a, 'a) fmt
  | Lit : string -> ('a, 'a) fmt
  | Cat : ('a, 'b) fmt * ('b, 'c) fmt -> ('a, 'c) fmt
static val ( % ) : ('a, 'b) fmt -> ('b, 'c) fmt -> ('a, 'c) fmt = <fun>
macro printk : (string expr -> 'b expr) -> ('a, 'b) fmt -> 'a expr = <fun>
macro sprintf : ('a, string) fmt -> 'a expr = <fun>
static val p : (int -> int -> '_a, '_a) fmt =
  Cat (Cat (Cat (Cat (Lit "(", Int), Lit ","), Int), Lit ")")
# sprintf p;;;
- : (int -> int -> string) expr =
<< (function s/1287
     (function s/1287
       (apply (field 15 (global Pervasives!))
         (apply (field 15 (global Pervasives!))
           (apply (field 15 (global Pervasives!))
             (apply (field 15 (global Pervasives!)) "("
               (apply (field 19 (global Pervasives!)) s/1287))
             ",")
           (apply (field 19 (global Pervasives!)) s/1287))
         ")"))) >>
# $(sprintf p) 1 3;;
- : string = "(3,3)"

The result string should be "(1,3)".

This example used to work at the time of this blog post so maybe a bug was introduced when switch from Parsetree to Lambda for the quotes?

yallop commented 2 years ago

Similarly (?),

$ cat splices.ml
macro one = << 1 >>
macro two = << 2 >>
macro three = << 3 >>

let () =
  begin
    Printf.printf "%d\n" $(one);
    Printf.printf "%d\n" $(two);
    Printf.printf "%d\n" $(three);
  end
$ ocaml splices.ml
1
1
1
OlivierNicole commented 2 years ago

I see why this is happening. Visibly I made an error when switch the quoting/splicing mechanisms from Parsetre to Lambda quotes. https://github.com/modular-macros/ocaml-macros/blob/c1d7fc74b7c55515bf9435ae07ffee89384f6689/bytecomp/translcore.ml#L1159-L1165 splice_index is never updated. In addition, the contribution of https://github.com/modular-macros/ocaml-macros/commit/42a00617d75290056c2632c6f73b792432584f3d which introduced a sane (i.e. not using mutable state) management of toplevel splices has somehow disappeared.