- (* Run the code. *)
- let code = to_shell_script env loc code in
- let code = "set -e\n" (*^ "set -x\n"*) ^ "\n" ^ code in
-
- let chan = Unix.open_process_in code in
- let lines = ref [] in
- (try while true do lines := input_line chan :: !lines done
- with End_of_file -> ());
- let lines = List.rev !lines in
- let st = Unix.close_process_in chan in
- (match st with
- | Unix.WEXITED 0 -> ()
- | Unix.WEXITED i ->
- eprintf "*** function ‘%s’ failed with exit code %d\n" name i
- | Unix.WSIGNALED i ->
- eprintf "*** function ‘%s’ killed by signal %d\n" name i
- | Unix.WSTOPPED i ->
- eprintf "*** function ‘%s’ stopped by signal %d\n" name i
- );
-
- Ast.EList (Ast.noloc,
- (List.map (fun line ->
- Ast.EConstant (Ast.noloc, Ast.CString line))
- lines))
+ if pure then call_function_pure env loc name returning code
+ else call_function_really env loc name returning code
+
+and call_function_really env loc name returning code =
+ match returning with
+ | RetExpr ->
+ let r, b = run_code_to_string env loc code in
+ if r <> 0 then
+ failwithf "function ‘%s’ failed with exit code %d" name r;
+ Parse.parse_expr (sprintf "function:%s" name) b
+
+ | RetString ->
+ let r, b = run_code_to_string env loc code in
+ if r <> 0 then
+ failwithf "function ‘%s’ failed with exit code %d" name r;
+ (* Remove a single trailing \n if present. *)
+ let b =
+ let len = String.length b in
+ if len > 0 && b.[len-1] = '\n' then String.sub b 0 (len-1) else b in
+ Ast.EConstant (loc, Ast.CString b)
+
+ | RetStrings ->
+ let r, lines = run_code_to_string_list env loc code in
+ let strs = List.map (fun s -> Ast.EConstant (loc, Ast.CString s)) lines in
+ EList (loc, strs)
+
+(* For pure functions, check if the function can be matched to
+ * a previously memoized result, but don't fail if this goes wrong.
+ *)
+and call_function_pure env loc name returning code =
+ let code_key =
+ try Some (to_shell_script env loc (fst code))
+ with Failure _ -> None in
+ match code_key with
+ | None -> call_function_really env loc name returning code
+ | Some code_key ->
+ let r =
+ try Some (Hashtbl.find pure_cache code_key)
+ with Not_found -> None in
+ match r with
+ | Some expr -> expr
+ | None ->
+ let expr = call_function_really env loc name returning code in
+ Hashtbl.add pure_cache code_key expr;
+ expr