1. juster
  2. ocaml-pkgbuild

Source

ocaml-pkgbuild / bashexpand.ml

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) ->

            (* Similiar to pexpand. Expand everything until closing bracket. *)
            (* XXX: getting tired of passing params around, just use a ref? *)
            let rec uptoclose str 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 pexpand below. Refactor? *)
                      let donechunk = String.sub str idx (sigidx - idx) in
                      match expand_name str (sigidx + 1) params
                      with (nextidx, expanded) ->
                        (* Repeat because the bracket could be gone now. *)
                        match (uptoclose str 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 inbrackets_subst name str idx =
              (* XXX: What about badly formed input? *)
              match uptoclose str (idx + 1) with (newidx, subst) ->
                let pval = Bashparams.string_of_param (get_param 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, Bashparams.string_of_param (get_param name params))
            | ':' -> inbrackets_subst name str (idx+1)
            |  _  -> raise Not_found
          in (* end of inbrackets *)

          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
                   "Substitutions${BAZ:- IS WORKING}?" paramslist)