;; calculator.hs — recursive descent expression evaluator. ;; ;; Exercises: ;; - ADTs with constructor fields: TNum Int, TOp String, R Int [Token] ;; - Nested constructor pattern matching: (R v (TOp "+":rest)) ;; - let bindings in function bodies ;; - Integer arithmetic including `div` (backtick infix) ;; - Left-associative multi-level operator precedence (define hk-prog-val (fn (src name) (hk-deep-force (get (hk-eval-program (hk-core src)) name)))) (define hk-calc-src "data Token = TNum Int | TOp String\ndata Result = R Int [Token]\ngetV (R v _) = v\ngetR (R _ r) = r\neval ts = getV (parseExpr ts)\nparseExpr ts = parseExprRest (parseTerm ts)\nparseExprRest (R v (TOp \"+\":rest)) =\n let t = parseTerm rest\n in parseExprRest (R (v + getV t) (getR t))\nparseExprRest (R v (TOp \"-\":rest)) =\n let t = parseTerm rest\n in parseExprRest (R (v - getV t) (getR t))\nparseExprRest r = r\nparseTerm ts = parseTermRest (parseFactor ts)\nparseTermRest (R v (TOp \"*\":rest)) =\n let t = parseFactor rest\n in parseTermRest (R (v * getV t) (getR t))\nparseTermRest (R v (TOp \"/\":rest)) =\n let t = parseFactor rest\n in parseTermRest (R (v `div` getV t) (getR t))\nparseTermRest r = r\nparseFactor (TNum n:rest) = R n rest\n") (hk-test "calculator: 2 + 3 = 5" (hk-prog-val (str hk-calc-src "result = eval [TNum 2, TOp \"+\", TNum 3]\n") "result") 5) (hk-test "calculator: 2 + 3 * 4 = 14 (precedence)" (hk-prog-val (str hk-calc-src "result = eval [TNum 2, TOp \"+\", TNum 3, TOp \"*\", TNum 4]\n") "result") 14) (hk-test "calculator: 10 - 3 - 2 = 5 (left-assoc)" (hk-prog-val (str hk-calc-src "result = eval [TNum 10, TOp \"-\", TNum 3, TOp \"-\", TNum 2]\n") "result") 5) (hk-test "calculator: 6 / 2 * 3 = 9 (left-assoc)" (hk-prog-val (str hk-calc-src "result = eval [TNum 6, TOp \"/\", TNum 2, TOp \"*\", TNum 3]\n") "result") 9) (hk-test "calculator: single number" (hk-prog-val (str hk-calc-src "result = eval [TNum 42]\n") "result") 42) {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}