// postfix evaluation: 2 3 * 5 + = 11 "abc" "3" open System; open Result; let rec posteval (vstack:int list, istack:string list) = match (vstack,istack) with | (a::b::vst, "+"::ist) -> posteval ((b+a)::vst,ist) | (a::b::vst, "-"::ist) -> posteval ((b-a)::vst,ist) | (a::b::vst, "*"::ist) -> posteval ((b*a)::vst,ist) | (a::b::vst, "/"::ist) -> posteval ((b/a)::vst,ist) | (a::b::vst, "%"::ist) -> posteval ((b%a)::vst,ist) | (vst, sym::ist) -> let x = ref 0; // will hold integer returned by TryParse if Int32.TryParse(sym,x) then posteval (!x::vst, ist) else raise(Exception("Invalid input symbol "+sym)) | ([result],[]) -> result // success | _ -> eprintfn "postfix evaluation failed, returning 0 as default" 0;; Console.Write("Enter postfix expression: "); let input = Console.ReadLine(); // try 1 3 - 4 * 0 / 3 4 5 + let istack = Array.toList (input.Split()) let unsafe_version() = let mutable result = 0 try result <- posteval([],istack) with | exp -> printfn "Exception %A, result defaults to 0" exp printfn "result = %d" result;; ///// postfix eval using Result Monad for error handling // check for div by zero for / and %, // check for overflow for * (can optionally do for other ops) let safediv(x,y) = if y=0 then Result.Error("division of "+string(x)+" by zero") else Result.Ok(x/(y+1)); let safemod(x,y) = if y=0 then Result.Error("mod "+string(x)+" by zero") else Result.Ok(x%y); let safemult(x,y) = let xy = x*y; if xy<>0 && xy/x<>y then Error("ERROR: arith overflow on multiplication of "+string(x)+" and "+string(y)) else Ok(xy); let safeparse(sym:string) = let x = ref 0; if Int32.TryParse(sym,x) then Ok(!x) else Error("failed to parse "+sym+" to integer"); let rec safe_eval (vstack:Result list, istack:string list) = match (vstack,istack) with | (Ok(a)::Ok(b)::vst, "+"::ist) -> safe_eval(Ok(b+a)::vst,ist) | (Ok(a)::Ok(b)::vst, "-"::ist) -> safe_eval(Ok(b-a)::vst,ist) | (Ok(a)::Ok(b)::vst, "*"::ist) -> safe_eval(safemult(b,a)::vst,ist) | (Ok(a)::Ok(b)::vst, "/"::ist) -> safe_eval(safediv(b,a)::vst,ist) | (Ok(a)::Ok(b)::vst, "%"::ist) -> safe_eval(safemod(b,a)::vst,ist) | (vst, sym::ist) -> match (safeparse sym) with | Ok(n) -> safe_eval(Ok(n)::vst,ist) | Error(s) -> // choice to skip bad symbol or quit Console.Error.WriteLine("Skipping unexpected symbol "+sym); safe_eval(vst,ist) // Error("eval failed due to unexpected symbol") | ([Ok(n)], []) -> Ok(n) | (vst,ist) -> Error((vst,ist)) let result2 = safe_eval([],istack) printfn "save_eval: %A" result2 //unsafe_version(); // in contrast