-- Copyright (c) 2021-2023 Chris Pressey, Cat's Eye Technologies.
-- Distributed under a 2-clause BSD license. For more details, see
-- LicenseRef-BSD-2-Clause-X-Lariat.txt in the LICENSES directory.
--
-- SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Lariat
module Main where
--
-- Test suite for Haskell implementation of Lariat.
-- This "Main" module just runs the tests and exits.
--
import Prelude hiding (abs)
import Data.Char (ord, chr)
import Data.List (nub)
import Data.Lariat (name, var, app, abs, destruct)
--
-- Test helpers
--
-- First, a simple function to express our illustrative tests:
-- Given a list of pairs, show those pairs that are not equal.
-- Anything other than an empty list returned indicates a mistake.
expect [] = []
expect ((a, b):rest) = if (show a) == b then expect rest else ((a, b):expect rest)
--
-- Convenience functions
--
right (Right x) = x
a = name "a"
b = name "b"
c = name "c"
f = name "f"
n = name "n"
m = name "m"
q = name "q"
t = name "t"
x = name "x"
y = name "y"
--
-- Test cases
--
showDestruct t = destruct t
(\n -> "var:" ++ (show n))
(\u v -> "app:" ++ (show u) ++ "," ++ (show v))
(\u n -> "abs:" ++ (show n) ++ ":" ++ (show u))
testShowDestruct = expect
[
(showDestruct (var n), "\"var:n\""),
(showDestruct (app (var n) (var m)), "\"app:FreeVar n,FreeVar m\""),
(showDestruct (abs n (var n)), "\"abs:y:FreeVar y\"")
]
freevars t = nub $ freevars' t [] where
freevars' t ours = destruct t
(\n -> if elem n ours then [] else [n])
(\u v -> freevars' u ours ++ freevars' v ours)
(\u n -> freevars' u (n:ours))
testFreeVars = expect
[
(freevars (app (abs n (var n)) (var n)), "[n]"),
(freevars (abs n (var n)), "[]"),
(freevars (app (var n) (var n)), "[n]")
]
resolve t x = destruct t
(\n -> t)
(\u v -> t)
(\u n -> replaceAll u n x)
replaceAll t m x = destruct t
(\n -> if n == m then x else (var n))
(\u v -> app (replaceAll u m x) (replaceAll v m x))
(\u n -> abs n (replaceAll u m x))
testResolve = expect
[
(resolve ((app (abs n (var n)) (var n))) (var q), "App (Abs (BoundVar 0)) (FreeVar n)"),
(resolve (abs n (var m)) (var q), "FreeVar m"),
(resolve (abs n (var n)) (var q), "FreeVar q")
]
--
-- "beta-reduce a term" example
--
beta t = destruct t
(\n -> t)
(\u v -> destruct u
(\_ -> app u v)
(\_ _ -> app u v)
(\u n -> replaceAll u n v)
)
(\_ _ -> t)
testBeta = expect
[
(beta (app (abs n (var n)) (var n)), "FreeVar n"),
(beta (abs n (var n)), "Abs (BoundVar 0)"),
(beta (var q), "FreeVar q")
]
isBetaReducible t = destruct t
(\_ -> False)
(\u v -> destruct u
(\_ -> False)
(\_ _ -> False)
(\_ _ -> True)
)
(\_ _ -> False)
testIsBetaReducible = expect
[
(isBetaReducible (app (abs n (var n)) (var n)), "True"),
(isBetaReducible (abs n (var n)), "False"),
(isBetaReducible (var q), "False")
]
--
-- Returns `Right t'` is the term was rewritten, `Left t` if it was not.
--
reduceOnce t =
if isBetaReducible t then (Right (beta t)) else destruct t
(\n -> Left (var n))
(\u v ->
case reduceOnce u of
Right r -> Right (app r v)
Left f ->
case reduceOnce v of
Right s -> Right (app u s)
Left g -> Left (app f g)
)
(\u n -> Left (abs n u))
testReduceOnce = expect
[
(reduceOnce (app (abs n (var n)) (var n)),
"Right (FreeVar n)"),
(reduceOnce (abs n (var n)),
"Left (Abs (BoundVar 0))"),
(reduceOnce (var q),
"Left (FreeVar q)"),
(reduceOnce (app (app (abs q (var q)) (abs q (var q))) (var n)),
"Right (App (Abs (BoundVar 0)) (FreeVar n))"),
(reduceOnce (right $ reduceOnce (app (app (abs q (var q)) (abs q (var q))) (var n))),
"Right (FreeVar n)"),
(reduceOnce (app (var n) (app (abs q (var q)) (var m))),
"Right (App (FreeVar n) (FreeVar m))")
]
--
-- Reduce a term to normal form.
--
normalize t =
case reduceOnce t of
Right t' -> normalize t'
Left _ -> t
testNormalize = expect
[
(normalize (app (abs n (var n)) (var n)),
"FreeVar n"),
(normalize (app
(abs x (app (var a) (var x)))
(app (abs y (app (var b) (var y))) (var c))
),
"App (FreeVar a) (App (FreeVar b) (FreeVar c))"),
(normalize (app (app (abs x (abs y (app (var y) (var x)))) (var a)) (var b)),
"App (FreeVar b) (FreeVar a)"),
(normalize (app (app (abs x (abs y (app (var y) (var x)))) (var y)) (var x)),
"App (FreeVar x) (FreeVar y)"),
let
true = (abs t (abs f (var t)))
false = (abs t (abs f (var f)))
test = (abs a (abs b (abs c (app (app (var a) (var b)) (var c)))))
in
(normalize (app (app (app test true) (var m)) (var n)),
"FreeVar m")
]
allTests :: String
allTests =
-- Hugs can only Show tuples up to 5-tuples. Not 6-tuples. Thus this nesting:
case (
(testShowDestruct, testFreeVars, testResolve, testBeta, testIsBetaReducible),
(testReduceOnce, testNormalize)
) of
(([],[],[],[],[]), ([], [])) -> "ok"
other -> error (show other)
main = do
let x = allTests
putStrLn x