Source

ocaml-pkgbuild / bashexpand.ml

Full commit
open Bashparams
open Printf

module Bashexpand =
  struct

    (** Our error stores the index of the character where parsing failed. *)
    exception ExpandError       of int
    exception UnbalancedBracket of int

    let get_param name plist =
      printf "*DBG* get_param: name=%s\n" name ;
      try List.assoc name plist
      with Not_found -> Bashparams.param_of_string ""

    let set_param name param plist =
      (name, param) :: List.remove_assoc name plist

    let parameter_expand str params =

      (* Recursive helper function for parameter_expand. *)
      let rec pexpand str idx params =

        let rec expand_name str idx params =

          let inbrackets = fun str -> function (idx, name) ->
            match str.[idx] with
              '}' -> (idx+1, name)
            | _   -> raise Not_found
          in

          let extractname str idx =
            if Str.string_match (Str.regexp "[a-zA-Z_-]+") str idx then
              (Str.match_end (), Str.matched_string str)
            else
              raise (ExpandError idx)
          in

          printf "*DBG* str=%s idx=%d\n" str idx ;
          
          match str.[idx] with
          | '$' ->
              begin
                try
                  match expand_name str (idx+1) params with (newidx, name) ->
                    (newidx, Bashparams.string_of_param (get_param name params))
                with Invalid_argument(_) -> raise (ExpandError idx)
              end
          | '{' ->
              begin
                try inbrackets str (expand_name str (idx+1) params)
                with Invalid_argument(_) -> raise (UnbalancedBracket idx)
              end
          | _ ->
              match extractname str idx with (newidx, name) ->
                (newidx, Bashparams.string_of_param (get_param name params))
        in (* end of expand_name *)

        try
          let sigidx = String.index_from str idx '$' in
          begin
            try
              let donechunk = String.sub str idx (sigidx - idx) in
              match expand_name str (sigidx+1) params
              with (nextidx, expanded) ->
                donechunk ^ expanded ^ (pexpand str nextidx params)

            (* This exception means the $ is at the end of the string. *)
            with Invalid_argument(_) -> raise (ExpandError sigidx)
          end
        with Not_found ->
          (* There are no more $'s we are done. *)
          let len = String.length str in String.sub str idx (len - idx)
      in (* end of pexpand *)

      pexpand str 0 params

  end

let _ =
  let paramslist = [ ("FOO", Bashparams.param_of_string "BAR") ;
                     ("BAR", Bashparams.param_of_string "Hello, World!") ] in
  print_endline (Bashexpand.parameter_expand "01$" paramslist)