Source

ocaml-pkgbuild / bashexpand.ml

Full commit
juster 09947d5 









juster 632c202 












juster 09947d5 



juster 632c202 
juster 09947d5 


juster 42c4021 
juster 09947d5 
juster 42c4021 

juster 09947d5 
juster 42c4021 
juster 81306fa 
juster 42c4021 


juster 81306fa 







juster 42c4021 
juster 81306fa 
juster 42c4021 
juster 81306fa 

juster 42c4021 

juster 81306fa 








juster 42c4021 
juster 81306fa 
juster 42c4021 
juster 632c202 
juster 81306fa 






juster 09947d5 
juster 632c202 
juster 42c4021 
juster 81306fa 
juster 42c4021 
juster 09947d5 
juster 42c4021 
juster 09947d5 











juster 42c4021 
juster 632c202 
juster 09947d5 



juster 42c4021 
juster 09947d5 


juster 42c4021 
juster 632c202 
juster 09947d5 






juster 42c4021 
juster 09947d5 
juster 42c4021 
juster 09947d5 






juster 42c4021 
juster 09947d5 
juster 42c4021 
juster 09947d5 





juster 81306fa 
juster 42c4021 
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 param_lookup expander name plist =
      let param = try List.assoc name plist
        with Not_found -> Bashparams.param_of_string ""
      in expander param

    (** Lookup the value of a parameter in string context.
        This is like $FOO in bash. *)
    let param_str_ctx = param_lookup Bashparams.string_of_param

    (** Lookup the value of a parameter in array context.
        This is like ${FOO[@]} in bash. *)
    (* Commented out because we don't use this, yet. *)
    (* let param_arr_ctx = param_lookup Bashparams.string_of_array *)

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

    (** Search and destroy parameters ($VARs) in a string. **)
    let parameter_expand str params =

      (* Recursive helper function for parameter_expand. *)
      let rec expand_at idx =

        (* Point expand_name to the index after a $. *)
        let rec expand_name idx =

          let expand_inbraces = function (idx, name) ->

            (* Similiar to expand_at. *)
            (* Expand everything until closing bracket. *)
            let rec expand_until_curly idx =
              try
                (* Look ahead for the next } and $. *)
                let brckidx = String.index_from str idx '}' in
                begin
                  try
                    let sigidx = String.index_from str idx '$' in
                    if sigidx > brckidx then raise Not_found
                    else
                      (* XXX: Copy/pasted from expand_at below. Refactor? *)
                      let donechunk = String.sub str idx (sigidx - idx) in
                      match expand_name (sigidx + 1)
                      with (nextidx, expanded) ->
                        (* Repeat because the bracket could be gone now. *)
                        match (expand_until_curly nextidx)
                        with (newidx, fin) ->
                          (newidx, donechunk ^ expanded ^ fin)
                  with Not_found ->
                    (* No $ was found or it comes after the closing bracket. *)
                    (brckidx + 1, String.sub str idx (brckidx - idx))
                end
              (* If no } was found we have a problem. *)
              with Not_found -> raise (ExpandError idx)
            in

            let expand_param_subst name idx =
              (* XXX: What about badly formed input? *)
              match expand_until_curly (idx + 1) with (newidx, subst) ->
                let pval = param_str_ctx name params in
                let expstr =
                  match str.[idx] with
                  | '-' -> if "" = pval then subst else pval
                  |  _  -> ""
                in (newidx, expstr)
            in

            match str.[idx] with
            | '}' -> (idx+1, param_str_ctx name params)
            | ':' -> expand_param_subst name (idx+1)
            |  _  -> raise Not_found
          in (* end of expand_inbraces *)

          let extract_name 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 (idx+1) with (newidx, name) ->
                    (newidx, param_str_ctx name params)
                with Invalid_argument(_) -> raise (ExpandError idx)
              end
          | '{' ->
              begin
                try expand_inbraces (expand_name (idx+1))
                with Invalid_argument(_) -> raise (UnbalancedBracket idx)
              end
          | _ ->
              match extract_name idx with (newidx, name) ->
                (newidx, param_str_ctx 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 (sigidx+1)
              with (nextidx, expanded) ->
                donechunk ^ expanded ^ (expand_at nextidx)

            (* 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 expand_at *)

      expand_at 0

  end

let _ =
  let paramslist = [ ("FOO", Bashparams.param_of_string "BAR") ;
                     ("BAR", Bashparams.param_of_string "Hello, World!") ] in
  print_endline (Bashexpand.parameter_expand
                   "Substitutions${BAZ:- IS WORKING}!" paramslist)