ocamlcamlp4

What is the purpose of the _loc variable when defining grammar rules in camlp4?


The _loc variable appears in here in the grammar rule for the match ... with expression as an argument passed to the mk_sequence function.

    | "match"; e = sequence; "with"; a = match_case ->
        <:expr< match $mksequence' _loc e$ with [ $a$ ] >>

But it is not used in the mksequence's function body.

  value mksequence _loc =
    fun
    [ <:expr< $_$; $_$ >> | <:expr< $anti:_$ >> as e -> <:expr< do { $e$ } >>
    | e -> e ]
  ;

The _loc variable also appears in other places in the grammar rules.

In the camlp4 code generation tutorial, it says that _loc stands for location. But I don't quite understand the explanation there. Can someone explain to me the purpose for passing an unused _loc variable around?


Solution

  • Roughly speaking, _loc is "the current position". Normally it is bould to the location of the matched AST in parsing rules.

    _loc is an ordinary OCaml variable, and actually omnipresent in CamlP4 code, but P4's syntax sugar nicely hide most of their existence. You can learn how '_loc' is introduced and used in P4 by preprocess your P4 module by P4. For example,

    EXTEND Gram
      my_syntax:
        [ [ "match"; e = sequence; "with"; a = match_case ->
              <:expr< match $mksequence' _loc e$ with [ $a$ ] >>
        ] ];
    END;
    

    If you preprocess the above by camlp4rf, you got what it really means:

    Gram.extend (my_syntax : 'my_syntax Gram.Entry.t)
      ((fun () ->
          (None,
           [ (None, None,
              [ ([ Gram.Skeyword "match";
                   Gram.Snterm
                     (Gram.Entry.obj (sequence : 'sequence Gram.Entry.t));
                   Gram.Skeyword "with";
                   Gram.Snterm
                     (Gram.Entry.obj (match_case : 'match_case Gram.Entry.t)) ],
                 (Gram.Action.mk
                    (fun (a : 'match_case) _ (e : 'sequence) _
                       (_loc : Gram.Loc.t) ->
                       (Ast.ExMat (_loc, (mksequence' _loc e), a) : 'my_syntax)))) ]) ]))
         ())
    

    It is bit hard but you can find Gram.Action.mk takes a function introducing an argument _loc. It is bound to the location of an AST which matches with the specification starts with [ Gram.Skeyword "match".... Then _loc is used at <:expr< match ... >> which is expanded into Ast.ExMat (_loc, ...), in addition to its second use at mksequence' _loc e, which is written by hand.

    <:expr<...>> and the other <:XXX<...>> constructs in p4 uses this _loc variable so that you can create your AST without considering much about the location of it. It automatically uses _loc, "the current parsed location". If you do not want to use _loc for <:expr<...>> you can use <:expr@myloc<...>> to explicitly specify the location of your AST.

    Sometimes you want to use <:expr<...>> outside of parsing rules, and in that case _loc is unbound. You must use <:expr@myloc<...>>, or use <:expr<...> after binding _loc by something else. Typically, let _loc = Loc.ghost which means "no where".

    P4 is very complex, and there are not much documentations available in the net. Sometimes expanding your P4 code by P4 helps understanding how it works.