ocamlppx

Metaquot: recognize a list pattern and store the resulting list


I have the following attribute attached to nodes [@profiling.mark [ "label1"; "label2"; "label3" ]] which gives me this AST:

[{attr_name = {txt = "profiling.mark"};
 attr_payload =
  PStr
   [{pstr_desc =
      Pstr_eval
       ({pexp_desc =
          Pexp_construct ({txt = Lident "::"},
           Some
            {pexp_desc =
              Pexp_tuple
               [{pexp_desc =
                  Pexp_constant
                   (Pconst_string ("label1", ...));
                 pexp_loc_stack = []};
                {pexp_desc =
                  Pexp_construct ({txt = Lident "::"},
                   Some
                    {pexp_desc =
                      Pexp_tuple
                       [{pexp_desc =
                          Pexp_constant
                           (Pconst_string ("label2", ...));
                         pexp_loc_stack = []};
                        {pexp_desc =
                          Pexp_construct
                           ({txt = Lident "::"},
                           Some
                            {pexp_desc =
                              Pexp_tuple
                               [{pexp_desc =
                                  Pexp_constant
                                   (Pconst_string ("label3",
                                     ...));
                                 pexp_loc_stack = []};
                                {pexp_desc =
                                  Pexp_construct
                                   ({txt = Lident "[]"},
                                   None);
                                 pexp_loc_stack = []}];
                             pexp_loc_stack = []});
                         pexp_loc_stack = []}];
                     pexp_loc_stack = []});
                 pexp_loc_stack = []}];
             pexp_loc_stack = []});
         pexp_loc_stack = []},
       ...)}]}]

If I want to extract the labels, I need to destruct a Pstr_construct so I did this:

Ppxlib.PStr                                                 
  [                                                         
    [%stri                                                  
      [%e?                                                  
        {                                                   
          pexp_desc =                                       
            Pexp_construct                                  
              ( { txt = Lident "::"; _ },                   
                Some { pexp_desc = Pexp_tuple labels; _ } );
          _;                                                
        }]];                                                
  ] ->                                                      
  { metadata = labels }                                     

First of all, I tried to use metaquot to destroy the list but couldn't find the way to do it. But the most important is that labels is an expression list and not an expression so I can't reconstruct it with [%e labels].

I then came up with a different approach:

Ppxlib.PStr                                                        
  [                                                                
    [%stri                                                         
      [%e?                                                         
        {                                                          
          pexp_desc =                                              
            Pexp_construct ({ txt = Lident "::"; _ }, Some labels);
          _;                                                       
        }]];                                                       
  ] ->                                                             
  { metadata = Some labels }                                       

But when I read the preprocessed file I see this: ("label1", ["label2"; "label3"]). I don't have a list but a tuple with an element and a list. In my case, I need ["label1"; "label2"; "label3"].

Am I doing something wrong or missing something somewhere?


Solution

  • Extracting a list can be done with

    let rec extract_list =function
      | [%expr [%e? h] :: [%e? r] ] -> Option.map (List.cons h) (extract_list r)
      | [%expr [] ] -> Some []
      | _ -> None
    

    Reconstructing the list is similar:

    let rec embed_list loc = function
      | [] -> [%expr []]
      | [e] -> [%expr [[%e e]] ]
      | a::q -> [%expr [%e a]::[%e embed_list loc q] ]