+and matching_pattern env loc tactic cargs pattern params =
+ match pattern with
+ | Ast.PTactic (loc, ttactic, targs)
+ when tactic <> ttactic ||
+ List.length cargs <> List.length targs ->
+ None (* Can't possibly match if tactic name or #args is different. *)
+ | Ast.PTactic (loc, ttactic, targs) ->
+ (* Do the args match with a possible params binding? *)
+ try Some (matching_params env loc params targs cargs)
+ with Not_found -> None
+
+(* Return a possible binding. For example the goal is:
+ * goal compile (name) = "%name.o": "%name.c" {}
+ * which means that params = ["name"] and targs = ["%name.o"].
+ *
+ * If we are called with cargs = ["file1.o"], we would
+ * return ["file1"].
+ *
+ * On non-matching this raises Not_found.
+ *)
+and matching_params env loc params targs cargs =
+ (* This is going to record the resulting binding. *)
+ let res = ref Ast.Env.empty in
+ List.iter2 (matching_param env loc params res) targs cargs;
+
+ (* Rearrange the result into goal parameter order. Also this
+ * checks that every parameter got a binding.
+ *)
+ let res = !res in
+ List.map (
+ (* Allow the Not_found exception to escape if no binding for this param. *)
+ fun param -> Ast.Env.find param res
+ ) params
+
+(* If targ = "%name.o" and carg = "file.o" then this would set
+ * name => "file" in !res. If they don't match, raises Not_found.
+ *)
+and matching_param env loc params res targ carg =
+ match carg with
+ | Ast.CString carg ->
+ (* Substitute any non parameters in targ from the environment. *)
+ let targ =
+ List.map (
+ function
+ | Ast.SString _ as s -> s
+ | Ast.SVar name ->
+ if not (List.mem name params) then (
+ try
+ let expr = Ast.getvar env loc name in
+ match Ast.to_constant env expr with
+ | Ast.CString s -> Ast.SString s
+ with Failure _ -> raise Not_found
+ )
+ else
+ Ast.SVar name
+ ) targ in
+
+ (* Do the actual pattern matching. Any remaining SVar elements
+ * must refer to goal parameters.
+ *)
+ let carg = ref carg in
+ let rec loop = function
+ | [] ->
+ (* End of targ, we must have matched all of carg. *)
+ if !carg <> "" then raise Not_found
+ | Ast.SString s :: rest ->
+ (* Does this match the first part of !carg? *)
+ let clen = String.length !carg in
+ let slen = String.length s in
+ if slen > clen || s <> String.sub !carg 0 slen then
+ raise Not_found;
+ (* Yes, so continue after the matching prefix. *)
+ carg := String.sub !carg slen (clen-slen);
+ loop rest
+ | Ast.SVar name :: Ast.SString s :: rest ->
+ (* This is a goal parameter. Find s later in !carg. *)
+ let i = string_find !carg s in
+ if i = -1 then raise Not_found;
+ (* Set the binding in !res. *)
+ let r = Ast.EConstant (Ast.noloc,
+ Ast.CString (String.sub !carg 0 i)) in
+ res := Ast.Env.add name r !res;
+ (* Continue after the match. *)
+ let skip = i + String.length s in
+ carg := String.sub !carg skip (String.length !carg - skip);
+ loop rest
+ | Ast.SVar name :: [] ->
+ (* Matches the whole remainder of the string. *)
+ let r = Ast.EConstant (Ast.noloc, Ast.CString !carg) in
+ res := Ast.Env.add name r !res
+ | Ast.SVar x :: Ast.SVar y :: _ ->
+ (* TODO! We cannot match a target like "%x%y". *)
+ assert false
+ in
+ loop targ