From 4fed459d89b6b3abcce8028b19af806b48a38bd7 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Sun, 22 Mar 2026 13:16:21 +0100 Subject: Add builtin arithmetic operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * also allow choosing evaluator * ensures equivalence of evaluators up-to α-renaming * checks server accepts answers from challenge --- .../haskell/test/Minilang/Lambda/ParserSpec.hs | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'lambda-calcul/haskell/test/Minilang/Lambda/ParserSpec.hs') diff --git a/lambda-calcul/haskell/test/Minilang/Lambda/ParserSpec.hs b/lambda-calcul/haskell/test/Minilang/Lambda/ParserSpec.hs index e9cfc6b..08ec661 100644 --- a/lambda-calcul/haskell/test/Minilang/Lambda/ParserSpec.hs +++ b/lambda-calcul/haskell/test/Minilang/Lambda/ParserSpec.hs @@ -11,6 +11,7 @@ import qualified Data.Text as Text import Minilang.Lambda.Eval (Term (Lam)) import qualified Minilang.Lambda.Eval as Eval import Minilang.Lambda.Parser (AST (..), desugar, initialChars, parse, parseProgram, pretty, restChars) +import Minilang.Lambda.Eval (normalise) import Test.Hspec (Spec, describe, it, parallel, shouldBe, shouldSatisfy) import Test.Hspec.QuickCheck (prop) import Test.QuickCheck ( @@ -114,6 +115,66 @@ spec = parallel $ do let spaced = "- " <> Text.pack (show n) in parse spaced `shouldSatisfy` (/= Right (Lit (negate n))) + describe "arithmetic operators" $ do + -- Operator names are valid identifiers: they parse as Sym and round-trip. + it "parses + as a symbol" $ + parse "+" `shouldBe` Right (Sym "+") + + it "parses - as a symbol" $ + parse "-" `shouldBe` Right (Sym "-") + + it "parses * as a symbol" $ + parse "*" `shouldBe` Right (Sym "*") + + it "parses div as a symbol" $ + parse "div" `shouldBe` Right (Sym "div") + + it "parses mod as a symbol" $ + parse "mod" `shouldBe` Right (Sym "mod") + + it "parses = as a symbol" $ + parse "=" `shouldBe` Right (Sym "=") + + it "parses < as a symbol" $ + parse "<" `shouldBe` Right (Sym "<") + + it "parses neg as a symbol" $ + parse "neg" `shouldBe` Right (Sym "neg") + + -- Full expression parses and round-trips + it "parses (+ 3 4) and pretty-prints back" $ + let expr = App (Sym "+") (Lit 3) [Lit 4] + in parse "(+ 3 4)" `shouldBe` Right expr + + it "pretty (+ 3 4) round-trips through parse" $ + let expr = App (Sym "+") (Lit 3) [Lit 4] + in parse (pretty expr) `shouldBe` Right expr + + -- desugar of an arithmetic expression produces a normalised integer + it "desugar and normalise (+ 3 4) == Num 7" $ + normalise (desugar (App (Sym "+") (Lit 3) [Lit 4])) `shouldBe` Eval.Num 7 + + it "desugar and normalise (* 2 (+ 1 4)) == Num 10" $ + normalise (desugar (App (Sym "*") (Lit 2) [App (Sym "+") (Lit 1) [Lit 4]])) + `shouldBe` Eval.Num 10 + + -- Comparison operators produce Church booleans + it "desugar and normalise (= 3 3) == Church True" $ + normalise (desugar (App (Sym "=") (Lit 3) [Lit 3])) + `shouldBe` Eval.Lam "f" (Eval.Lam "t" (Eval.Var "t")) + + it "desugar and normalise (= 3 4) == Church False" $ + normalise (desugar (App (Sym "=") (Lit 3) [Lit 4])) + `shouldBe` Eval.Lam "f" (Eval.Lam "t" (Eval.Var "f")) + + it "desugar and normalise (< 2 5) == Church True" $ + normalise (desugar (App (Sym "<") (Lit 2) [Lit 5])) + `shouldBe` Eval.Lam "f" (Eval.Lam "t" (Eval.Var "t")) + + -- A partial application (missing second arg) pretty-prints as an App + it "pretty (+ 1) is \"(+ 1)\"" $ + pretty (App (Sym "+") (Lit 1) []) `shouldBe` "(+ 1)" + it "parses multiline program with blank lines" $ do let program = "(def id (lam (x) x))\n\ -- cgit v1.3.1