Re: ocamlyacc/lex reentrancy

From: Markus Mottl (mottl@miss.wu-wien.ac.at)
Date: Mon Jan 03 2000 - 17:47:44 MET

  • Next message: Denis Monasse: "Re: Ocaml for Mac"

    > > And, when invoking a lexer function or entry point, you need to pass the
    > > additionnal argument, a in
    > > somerule lexbuf lexdata

    There is another way to get a similar behaviour with some syntactic sugar.
    Use the following implementation of a "flow" (hm, just invented this name),
    which is actually just a queue with some additional operators:

    ---------------------------------------------------------------------------
    type 'a flow = Nil | Cons of 'a * 'a flow ref

    type 'a t = { mutable head: 'a flow; mutable tail: 'a flow }

    let create () = { head = Nil; tail = Nil }

    let add fl x = match fl.tail with
      | Nil -> let c = Cons (x, ref Nil) in fl.head <- c; fl.tail <- c
      | Cons (_, last_ref) -> let c = Cons (x, ref Nil) in
                              last_ref := c; fl.tail <- c

    let append fl1 fl2 = match fl1.tail with
      | Nil -> fl1.head <- fl2.head; fl1.tail <- fl2.tail
      | Cons (_, last_ref) -> last_ref := fl2.head; fl1.tail <- fl2.tail

    let rec iter_aux f = function Nil -> () | Cons (x, t) -> f x; iter_aux f !t
    let iter f q = iter_aux f q.head

    let (%) fl el = add fl el; fl
    let (!%) el = create () % el
    let (%@) fl1 fl2 = append fl1 fl2; fl1
    ---------------------------------------------------------------------------

    I use this in ocamlyacc files as follows:

    ---------------------------------------------------------------------------
    %{ open Semantics.Impl
       open Flow
    %}

    ... some tokens ...

    %start main
    %type <Semantics.Impl.cmd Flow.t> main

    %%

    actions_or_paths
      : action_or_path { $1 }
      | actions_or_paths useless action_or_path { $1 %@ $3 }

    action_or_path : path { $1 } | action { !% $1 }

    action
      : POP_PATH { pop_dir }
      | EMPTY_PATH_STACK { clear_dir_stack }
      | AT ID { dir_stack_at $2 }

    path : op_rel_path { $1 % push_dir } | cl_rel_path { $1 % top_dir }

    op_rel_path
      : cl_rel_path SLASH { $1 }
      | op_el { !% (cd $1) }
      | cl_rel_path SLASH op_el { $1 % (cd $3) }

    cl_rel_path
      : cl_el { !% (cd $1) }
      | set_el { !% (dir_set $1) }
      | cl_rel_path SLASH cl_el { $1 % (cd $3) }
      | cl_rel_path SLASH set_el { $1 % (dir_set $3) }

    ---------------------------------------------------------------------------

    This should lead to very compact and readable specifications. The result of
    parsing is a "flow" (queue) of commands (functions) that transform state.
    See below for an interface of Semantics.Impl:

    ---------------------------------------------------------------------------
    ...
      type state
      type cmd = state -> state
      
      val fresh : state
      val cd : Dir.Spec.el -> cmd
      val cd_root : cmd
      val cd_up : cmd
      val apply_flow : cmd Flow.t -> cmd
    ...
    ---------------------------------------------------------------------------

    "apply_flow parse_result fresh" will apply all the state transformations of
    the "flow" to a "fresh" state. Here the implementation of this function:

      let apply_stream strm s =
        let s' = ref s in Flow.iter (fun f -> s' := f !s') strm; !s'

    Maybe someone else will also find it useful.

    Regards,
    Markus Mottl

    -- 
    Markus Mottl, mottl@miss.wu-wien.ac.at, http://miss.wu-wien.ac.at/~mottl
    



    This archive was generated by hypermail 2b29 : Mon Jan 03 2000 - 17:53:00 MET