real tests!

This commit is contained in:
crumbtoo
2023-12-14 12:59:31 -07:00
parent 7a0b2e0711
commit 5ea629a439
6 changed files with 148 additions and 57 deletions

View File

@@ -23,13 +23,13 @@ library
, GM , GM
, Compiler.RLPC , Compiler.RLPC
, Core.Syntax , Core.Syntax
, Core.Examples
, Core.Utils , Core.Utils
, Core.TH
other-modules: Data.Heap other-modules: Data.Heap
, Data.Pretty , Data.Pretty
, Core.Parse , Core.Parse
, Core.TH
, Core.Examples
, Core.Lex , Core.Lex
, Control.Monad.Errorful , Control.Monad.Errorful
, Core2Core , Core2Core
@@ -83,5 +83,8 @@ test-suite rlp-test
build-depends: base ^>=4.18.0.0 build-depends: base ^>=4.18.0.0
, rlp , rlp
, QuickCheck , QuickCheck
, hspec ==2.*
other-modules: Arith other-modules: Arith
, GMSpec
build-tool-depends: hspec-discover:hspec-discover

View File

@@ -4,7 +4,10 @@ Description : Core examples (may eventually be unit tests)
-} -}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Core.Examples where module Core.Examples
( fac3
, sumList
) where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Core.Syntax import Core.Syntax
import Core.TH import Core.TH
@@ -12,6 +15,7 @@ import Core.TH
-- TODO: my shitty lexer isn't inserting semicolons -- TODO: my shitty lexer isn't inserting semicolons
letrecExample :: Program'
letrecExample = [coreProg| letrecExample = [coreProg|
pair x y f = f x y; pair x y f = f x y;
@@ -27,18 +31,22 @@ letrecExample = [coreProg|
main = f 3 4; main = f 3 4;
|] |]
idExample :: Program'
idExample = [coreProg| idExample = [coreProg|
main = id 3; main = id 3;
|] |]
indExample1 :: Program'
indExample1 = [coreProg| indExample1 = [coreProg|
main = twice twice id 3; main = twice twice id 3;
|] |]
indExample2 :: Program'
indExample2 = [coreProg| indExample2 = [coreProg|
main = twice twice twice id 3; main = twice twice twice id 3;
|] |]
indExample3 :: Program'
indExample3 = [coreProg| indExample3 = [coreProg|
main = letrec main = letrec
{ x = 2 { x = 2
@@ -49,51 +57,63 @@ indExample3 = [coreProg|
g a b = a; g a b = a;
|] |]
negExample1 :: Program'
negExample1 = [coreProg| negExample1 = [coreProg|
main = negate# (id 3); main = negate# (id 3);
|] |]
negExample2 :: Program'
negExample2 = [coreProg| negExample2 = [coreProg|
main = negate# 3; main = negate# 3;
|] |]
negExample3 :: Program'
negExample3 = [coreProg| negExample3 = [coreProg|
main = twice negate# 3; main = twice negate# 3;
|] |]
arithExample1 :: Program'
arithExample1 = [coreProg| arithExample1 = [coreProg|
main = (+#) 3 (negate# 2); main = (+#) 3 (negate# 2);
|] |]
arithExample2 :: Program'
arithExample2 = [coreProg| arithExample2 = [coreProg|
main = negate# ((+#) 2 ((*#) 5 3)); main = negate# ((+#) 2 ((*#) 5 3));
|] |]
ifExample1 :: Program'
ifExample1 = [coreProg| ifExample1 = [coreProg|
main = if# True 2 3; main = if# True 2 3;
|] |]
ifExample2 :: Program'
ifExample2 = [coreProg| ifExample2 = [coreProg|
main = if# (id True) 2 3; main = if# (id True) 2 3;
|] |]
facExample :: Program'
facExample = [coreProg| facExample = [coreProg|
fac n = if# ((==#) n 0) 1 ((*#) n (fac ((-#) n 1))); fac n = if# ((==#) n 0) 1 ((*#) n (fac ((-#) n 1)));
main = fac 3; main = fac 3;
|] |]
pairExample1 :: Program'
pairExample1 = [coreProg| pairExample1 = [coreProg|
main = fst (snd (fst (MkPair (MkPair 1 (MkPair 2 3)) 4))); main = fst (snd (fst (MkPair (MkPair 1 (MkPair 2 3)) 4)));
|] |]
pairExample2 :: Program'
pairExample2 = [coreProg| pairExample2 = [coreProg|
main = (if# False fst snd) (MkPair 2 3); main = (if# False fst snd) (MkPair 2 3);
|] |]
listExample1 :: Program'
listExample1 = [coreProg| listExample1 = [coreProg|
main = caseList# (Cons 2 Nil) 3 k; main = caseList# (Cons 2 Nil) 3 k;
|] |]
listExample2 :: Program'
listExample2 = [coreProg| listExample2 = [coreProg|
cc f x xs = Cons (f x) (map f xs); cc f x xs = Cons (f x) (map f xs);
map f l = caseList# l Nil (cc f); map f l = caseList# l Nil (cc f);
@@ -101,6 +121,7 @@ listExample2 = [coreProg|
main = map negate# list; main = map negate# list;
|] |]
listExample3 :: Program'
listExample3 = [coreProg| listExample3 = [coreProg|
cc f z x xs = f x (foldr f z xs); cc f z x xs = f x (foldr f z xs);
foldr f z l = caseList# l z (cc f z); foldr f z l = caseList# l z (cc f z);
@@ -108,6 +129,7 @@ listExample3 = [coreProg|
main = foldr (+#) 0 list; main = foldr (+#) 0 list;
|] |]
simple1 :: Program'
simple1 = [coreProg| simple1 = [coreProg|
k a b = a; k a b = a;
s f g x = f x (g x); s f g x = f x (g x);
@@ -115,6 +137,7 @@ simple1 = [coreProg|
main = s k k 3; main = s k k 3;
|] |]
caseBool1 :: Program'
caseBool1 = [coreProg| caseBool1 = [coreProg|
_if c x y = case c of _if c x y = case c of
{ 1 -> x { 1 -> x
@@ -127,7 +150,8 @@ caseBool1 = [coreProg|
main = _if false ((+#) 2 3) ((*#) 4 5); main = _if false ((+#) 2 3) ((*#) 4 5);
|] |]
factorialGM = [coreProg| fac3 :: Program'
fac3 = [coreProg|
fac n = case (==#) n 0 of fac n = case (==#) n 0 of
{ 1 -> 1 { 1 -> 1
; 0 -> (*#) n (fac ((-#) n 1)) ; 0 -> (*#) n (fac ((-#) n 1))
@@ -136,6 +160,20 @@ factorialGM = [coreProg|
main = fac 3; main = fac 3;
|] |]
-- TODO: paramaterised examples? eg. sumList could take a haskell list of ints
-- as an input, translate it to a rl' list, and run the example on that?
sumList :: Program'
sumList = [coreProg|
nil = Pack{0 0};
cons x y = Pack{1 2} x y;
list = cons 1 (cons 2 (cons 3 nil));
sum l = case l of
{ 0 -> 0
; 1 x xs -> (+#) x (sum xs)
};
main = sum list;
|]
corePrelude :: Module Name corePrelude :: Module Name
corePrelude = Module (Just ("Prelude", [])) $ corePrelude = Module (Just ("Prelude", [])) $
-- non-primitive defs -- non-primitive defs

View File

@@ -9,6 +9,10 @@ module GM
( hdbgProg ( hdbgProg
, evalProg , evalProg
, Node(..) , Node(..)
, gmEvalProg
, finalStateOf
, resultOf
, resultOfExpr
) )
where where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -701,17 +705,12 @@ buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
compileBinder (_ := v, a) = compileC g' v <> [Update a] compileBinder (_ := v, a) = compileC g' v <> [Update a]
-- special cases for prim functions; essentially inlining -- special cases for prim functions; essentially inlining
compileE g ("negate#" :$ a) = compileE g a <> [Neg] compileE g ("negate#" :$ a) = inlineOp1 g Neg a
compileE g ("+#" :$ a :$ b) = compileE g a <> compileE g b <> [Add] compileE g ("+#" :$ a :$ b) = inlineOp2 g Add a b
-- note that we only bother offsetting the environment and evaluationg compileE g ("-#" :$ a :$ b) = inlineOp2 g Sub a b
-- in the "correct" order with non-commutative operations. if we compileE g ("*#" :$ a :$ b) = inlineOp2 g Mul a b
-- implemented Sub the same way as Add, (-#) 3 2 would evaluate to -1. compileE g ("/#" :$ a :$ b) = inlineOp2 g Div a b
compileE g ("-#" :$ a :$ b) = compileE g b <> compileE g' a <> [Sub] compileE g ("==#" :$ a :$ b) = inlineOp2 g Equals a b
where g' = argOffset 1 g
compileE g ("*#" :$ a :$ b) = compileE g a <> compileE g b <> [Mul]
compileE g ("/#" :$ a :$ b) = compileE g a <> compileE g' b <> [Div]
where g' = argOffset 1 g
compileE g ("==#" :$ a :$ b) = compileE g a <> compileE g b <> [Equals]
compileE g (Case e as) = compileE g e <> [CaseJump (compileD g as)] compileE g (Case e as) = compileE g e <> [CaseJump (compileD g as)]
@@ -728,6 +727,13 @@ buildInitialHeap (Program ss) = mapAccumL allocateSc mempty compiledScs
g' = binds ++ argOffset n g g' = binds ++ argOffset n g
c = compileE g' e c = compileE g' e
inlineOp1 :: Env -> Instr -> Expr' -> Code
inlineOp1 g i a = compileE g a <> [i]
inlineOp2 :: Env -> Instr -> Expr' -> Expr' -> Code
inlineOp2 g i a b = compileE g b <> compileE g' a <> [i]
where g' = argOffset 1 g
-- | offset each address in the environment by n -- | offset each address in the environment by n
argOffset :: Int -> Env -> Env argOffset :: Int -> Env -> Env
argOffset n = each . _2 %~ (+n) argOffset n = each . _2 %~ (+n)
@@ -855,7 +861,7 @@ showNodeAtP p st a = case hLookup a h of
name = case lookup a (swap <$> g) of name = case lookup a (swap <$> g) of
Just (NameKey n) -> n Just (NameKey n) -> n
Just (ConstrKey t n) -> idPack t n Just (ConstrKey t n) -> idPack t n
_ -> errTxtInvalidAddress _ -> errTxtInvalidAddress
-- TODO: left-associativity -- TODO: left-associativity
Just (NAp f x) -> pprec $ showNodeAtP (p+1) st f Just (NAp f x) -> pprec $ showNodeAtP (p+1) st f
<+> showNodeAtP (p+1) st x <+> showNodeAtP (p+1) st x
@@ -950,4 +956,26 @@ sweepNodes st = st & gmHeap %~ thread (f <$> addresses h)
thread :: [a -> a] -> (a -> a) thread :: [a -> a] -> (a -> a)
thread = appEndo . foldMap Endo thread = appEndo . foldMap Endo
--} ----------------------------------------------------------------------------------
gmEvalProg :: Program' -> GmState
gmEvalProg p = compile p & eval & last
finalStateOf :: (GmState -> r) -> Program' -> r
finalStateOf f = f . gmEvalProg
resultOf :: Program' -> Maybe Node
resultOf p = do
a <- res
n <- hLookup a h
pure n
where
res = st ^? gmStack . _head
st = gmEvalProg p
h = st ^. gmHeap
resultOfExpr :: Expr' -> Maybe Node
resultOfExpr e = resultOf $ Program
[ ScDef "main" [] e
]

View File

@@ -1,7 +1,8 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
module Arith module Arith
( runTestsArith ( evalCore
, evalArith
) where ) where
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
import Data.Functor.Classes (eq1) import Data.Functor.Classes (eq1)
@@ -27,16 +28,16 @@ data ArithExpr = IntA Int
| ArithExpr ::- ArithExpr | ArithExpr ::- ArithExpr
deriving Show deriving Show
evalA :: ArithExpr -> Int evalArith :: ArithExpr -> Int
evalA (IntA n) = n evalArith (IntA n) = n
evalA (IdA e) = evalA e evalArith (IdA e) = evalArith e
evalA (NegateA a) = negate (evalA a) evalArith (NegateA a) = negate (evalArith a)
evalA (a :+ b) = evalA a + evalA b evalArith (a :+ b) = evalArith a + evalArith b
evalA (a :* b) = evalA a * evalA b evalArith (a :* b) = evalArith a * evalArith b
evalA (a :- b) = evalA a - evalA b evalArith (a :- b) = evalArith a - evalArith b
evalA (a ::+ b) = evalA a + evalA b evalArith (a ::+ b) = evalArith a + evalArith b
evalA (a ::* b) = evalA a * evalA b evalArith (a ::* b) = evalArith a * evalArith b
evalA (a ::- b) = evalA a - evalA b evalArith (a ::- b) = evalArith a - evalArith b
instance Arbitrary ArithExpr where instance Arbitrary ArithExpr where
arbitrary = gen 4 arbitrary = gen 4
@@ -47,7 +48,7 @@ instance Arbitrary ArithExpr where
-- i don't feel like dealing with division at the moment -- i don't feel like dealing with division at the moment
[ IntA <$> int [ IntA <$> int
, NegateA <$> arbitrary , NegateA <$> arbitrary
, IdA <$> arbitrary -- , IdA <$> arbitrary
, b (:+) , b (:+)
, b (:-) , b (:-)
, b (:*) , b (:*)
@@ -62,11 +63,11 @@ instance Arbitrary ArithExpr where
-- int = chooseInt (minBound,maxBound) -- int = chooseInt (minBound,maxBound)
int = chooseInt (-500,500) int = chooseInt (-500,500)
prop_ArithExprEqCoreExpr :: ArithExpr -> Bool -- prop_ArithExprEqCoreExpr :: ArithExpr -> Bool
prop_ArithExprEqCoreExpr e = arithResult `eq1` coreResult -- prop_ArithExprEqCoreExpr e = arithResult `eq1` coreResult
where -- where
arithResult = Just (evalA e) -- arithResult = Just (evalArith e)
coreResult = evalCore (toCore e) -- coreResult = evalCore (toCore e)
toCore :: ArithExpr -> Program' toCore :: ArithExpr -> Program'
toCore expr = Program toCore expr = Program
@@ -87,15 +88,18 @@ toCore expr = Program
f n a b = n :$ go a :$ go b f n a b = n :$ go a :$ go b
evalCore :: Program' -> Maybe Int evalProgram :: Program' -> Maybe Int
evalCore p = do evalProgram p = do
a <- fst <$> evalProg p a <- fst <$> evalProg p
case a of case a of
(NNum n) -> Just n (NNum n) -> Just n
_ -> Nothing _ -> Nothing
pure [] evalCore :: ArithExpr -> Maybe Int
evalCore = evalProgram . toCore
runTestsArith :: IO Bool -- pure []
runTestsArith = $quickCheckAll
-- runTestsArith :: IO Bool
-- runTestsArith = $quickCheckAll

35
tst/GMSpec.hs Normal file
View File

@@ -0,0 +1,35 @@
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
module GMSpec
( spec
)
where
----------------------------------------------------------------------------------
import Test.Hspec
import Arith
import Test.QuickCheck
import GM (Node(..), resultOf, resultOfExpr)
import Core.TH
import Core.Examples qualified as Ex
----------------------------------------------------------------------------------
spec :: Spec
spec = do
it "should correctly evaluate 3-2 with inlining" $ do
resultOf [coreProg|main = (-#) 3 2;|] `shouldBe` Just (NNum 1)
it "should correctly evaluate 3-2 without inlining" $ do
resultOf [coreProg|id x = x; main = (id (-#)) 3 2;|] `shouldBe` Just (NNum 1)
it "should correctly evaluate arbitrary arithmetic" $ do
property $ \e ->
let arithRes = Just (evalArith e)
coreRes = evalCore e
in coreRes `shouldBe` arithRes
describe "test programs" $ do
it "fac 3" $ do
resultOf Ex.fac3 `shouldBe` Just (NNum 6)
it "sum [1,2,3]" $ do
resultOf Ex.sumList `shouldBe` Just (NNum 6)

View File

@@ -1,18 +1 @@
module Main (main) where {-# OPTIONS_GHC -F -pgmF hspec-discover #-}
----------------------------------------------------------------------------------
import Control.Monad
import System.Exit
import Test.QuickCheck
import Arith
----------------------------------------------------------------------------------
runTests :: IO Bool
runTests = runTestsArith
main :: IO ()
main = do
good <- runTests
if good
then exitSuccess
else exitFailure