Source

ocaml-pkgbuild / bashexpand.ml

open Bashparam
open Printf

module Bashexpand =
  struct

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

    (** 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 = assoc_str 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, assoc_str 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, assoc_str 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, assoc_str 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

    let expand str params =
      parameter_expand str params

  end

let _ =
  let paramslist = [ ("FOO", Param("BAR")) ;
                     ("BAR", Param("Hello, World!")) ] in
  print_endline (Bashexpand.parameter_expand
                   "Substitutions${BAZ:- IS WORKING}!" paramslist)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.