Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
Recursive-descent calculator parses '(1 + 2) * 3 + 4' = 13. Two parser bugs fixed: 1. parse-let now handles inline 'let rec a () = ... and b () = ... in body' via new (:let-rec-mut BINDINGS BODY) and (:let-mut BINDINGS BODY) AST shapes; eval handles both. 2. has-matching-in? lookahead no longer stops at 'and' — 'and' is internal to let-rec, not a decl boundary. Without this fix, the inner 'let rec a () = ... and b () = ...' inside a let-decl rhs would have been treated as the start of a new top-level decl. Baseline exercises mutually-recursive functions, while-loops, ref-cell imperative parsing, and ADT-based AST construction.
77 lines
1.7 KiB
OCaml
77 lines
1.7 KiB
OCaml
(* Baseline: recursive-descent calculator for "+", "*", parens, ints. *)
|
|
type expr =
|
|
| Lit of int
|
|
| Add of expr * expr
|
|
| Mul of expr * expr
|
|
;;
|
|
|
|
let parse_input src =
|
|
let pos = ref 0 in
|
|
let peek () = if !pos < String.length src then String.get src !pos else "" in
|
|
let advance () = pos := !pos + 1 in
|
|
let skip_ws () =
|
|
while !pos < String.length src && peek () = " " do advance () done
|
|
in
|
|
|
|
let rec parse_atom () =
|
|
skip_ws () ;
|
|
if peek () = "(" then begin
|
|
advance () ;
|
|
let e = parse_expr () in
|
|
skip_ws () ;
|
|
advance () ; (* consume ')' *)
|
|
e
|
|
end
|
|
else
|
|
let start = !pos in
|
|
let rec digits () =
|
|
if !pos < String.length src then
|
|
let c = peek () in
|
|
if c >= "0" && c <= "9" then begin advance () ; digits () end
|
|
else ()
|
|
in
|
|
digits () ;
|
|
let n = Int.of_string (String.sub src start (!pos - start)) in
|
|
Lit n
|
|
|
|
and parse_term () =
|
|
skip_ws () ;
|
|
let lhs = ref (parse_atom ()) in
|
|
let rec loop () =
|
|
skip_ws () ;
|
|
if peek () = "*" then begin
|
|
advance () ;
|
|
lhs := Mul (!lhs, parse_atom ()) ;
|
|
loop ()
|
|
end
|
|
in
|
|
loop () ;
|
|
!lhs
|
|
|
|
and parse_expr () =
|
|
skip_ws () ;
|
|
let lhs = ref (parse_term ()) in
|
|
let rec loop () =
|
|
skip_ws () ;
|
|
if peek () = "+" then begin
|
|
advance () ;
|
|
lhs := Add (!lhs, parse_term ()) ;
|
|
loop ()
|
|
end
|
|
in
|
|
loop () ;
|
|
!lhs
|
|
in
|
|
parse_expr ()
|
|
;;
|
|
|
|
let rec eval e =
|
|
match e with
|
|
| Lit n -> n
|
|
| Add (a, b) -> eval a + eval b
|
|
| Mul (a, b) -> eval a * eval b
|
|
;;
|
|
|
|
(* (1 + 2) * 3 + 4 = 9 + 4 = 13 *)
|
|
eval (parse_input "(1 + 2) * 3 + 4")
|