I'm looking for some feedback on my OCaml implementation of some methods to translate infix expressions to postfix and then evaluate them.
I'm very new to OCaml, coming from C#/Java and JavaScript so pretty much any advice is appreciated, in particular how to make it more idiomatic or "functional".
It seems like I have to reverse my stack at some point because I want to operate on both sides, is this unavoidable? Could I be using better exceptions for my helper functions? Is it good practice to declare helper functions like I did? apply_and_store and str_to_fn could be inside eval_postfix, is it better to have them encapsulated or to keep eval_postfix cleaner?
let explode_to_strings (s : string) : string list =
Str.split (Str.regexp "[ \t]+") s;;
(* Method from pleac.sourceforge.net *)
let is_Integer (s : string) : bool =
try ignore (int_of_string s); true with Failure _ -> false;;
let str_to_fn (s : string) : (int -> int -> int) =
match s with
| "+" -> (+)
| "-" -> (-)
| "*" -> ( * )
| "/" -> (/)
| _ -> raise (Invalid_argument "Not an operator");;
let apply_and_store (lst : 'a list) (fn : 'a -> 'a -> 'a) : 'a list =
match lst with
| one :: two :: tl -> fn two one :: tl
| _ -> raise (Invalid_argument "List too short");;
let prec (s : string) : int =
match s with
| "+" | "-" -> 1
| "*" | "/" -> 2
| _ -> raise (Invalid_argument "Not in operator table");;
let infix_to_postfix (lst : string list) : string list =
let rec
push (elem : string) (output : string list) (ops : string list) : (string list * string list) =
match ops with
| [] -> (output, elem :: ops)
| hd :: tl -> if prec elem <= prec hd
then push elem (hd :: output) tl
else (output, elem :: ops)
in
let rec
aux (l : string list) (output : string list) (ops : string list) : string list =
match l with
| [] -> ops @ output
| hd :: tl -> if is_Integer hd
then aux tl (hd :: output) ops
else let ret = push hd output ops in
aux tl (fst ret) (snd ret)
in
aux lst [] [];;
let eval_postfix (lst : string list) : int =
let rec aux (l : string list) (stack : int list) : int =
match l with
| [] -> List.nth stack 0
| hd :: tl -> if is_Integer hd
then aux tl ((int_of_string hd) :: stack)
else aux tl (apply_and_store stack (str_to_fn hd)) in
aux (List.rev lst) [];;
let eval_infix (s : string) : int =
eval_postfix (infix_to_postfix (explode_to_strings s));;