(* simple expression parsing via recursive descent Wheeler Ruml, ruml at cs.unh.edu Here's the grammar: expr -> ( expr op expr ) -> number op -> +, -, *, or / number -> [0-9.]+ Notice that the only choice is how to parse an expr, and this can be determined by looking at its first character (is it a '('?). An example of running the code: typing at an OCaml toplevel: eval_str "((5+7)/2)";; will produce: - : float = 6. Note how the structure of the parsing functions directly reflects the structure of the grammar. Most parsing functions return a float and the index to continue parsing from. Recursion is used wherever the right-hand side of a grammar rule mentions its own left-hand non-terminal. This is why this style of hand-coded parser is called a "recursive-descent" parser. This example code does little error checking and will probably throw an exception (eg, access the input string out of bounds) on malformed input. *) let str = Printf.sprintf (** given format string and args, returns string *) let parse_op s i = (** parse an op at [i] in [s]. Return tuple of operator function and next index in [s]. *) let next = i + 1 in match s.[i] with '+' -> (+.), next | '-' -> (-.), next | '*' -> ( *.), next | '/' -> (/.), next | c -> failwith (str "unrecognized operator '%c' at position %d" c i) let is_num_char c = (** is [c] a legal char to find in a number? *) match c with | '0'..'9' -> true | '.' -> true | _ -> false let parse_num s i = (** parse an number at [i] in [s] *) let len = String.length s and j = ref i in while ((!j < len) && (is_num_char s.[!j])) do incr j done; let substr = String.sub s i (!j - i) in try (float_of_string substr), !j with Failure _ -> failwith (str "couldn't parse '%s' as a float" substr) let rec parse_expr s i = (** tries to parse an expr starting at [i] in [s]. Returns tuple of floating point value and the next index in [s]. *) if s.[i] = '(' then (* trying to parse an expr *) let v1, i = parse_expr s (i+1) in (* got expr, try for op *) let f, i = parse_op s i in (* after op, try for 2nd value *) let v2, i = parse_expr s i in if s.[i] = ')' then (* value of expression is result of applying op to the values *) (f v1 v2), i+1 else failwith (str "expected a ')' at position %d to close an expression" i) else (* trying to parse a number *) parse_num s i let eval_str s = (** evalutes string [s] as an expr, returning a float *) let v, j = parse_expr s 0 in if j != (String.length s) then failwith (str "parsed only %d chars of the input" j) else v (* EOF *)