commit 267d8a85c82327ea75ccb3efc9cd5b8bdfcac5ae
parent dfcf1970f2e847b7223c56e94e98573d3e8e6dbb
Author: James Eversole <james@eversole.co>
Date: Thu, 7 Aug 2025 15:16:44 -0500
"size" function nodes down from 454 to 321
Diffstat:
| M | test/Spec.hs | | | 73 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 72 insertions(+), 1 deletion(-)
diff --git a/test/Spec.hs b/test/Spec.hs
@@ -34,6 +34,8 @@ tests = testGroup "Tricu Tests"
, modules
-- , demos
, decoding
+ , elimLambdaSingle
+ , stressElimLambda
]
lexer :: TestTree
@@ -532,7 +534,7 @@ demos = testGroup "Test provided demo functionality"
decodeResult res @?= "\"(t (t (t t) (t t t)) (t t (t t t)))\""
, testCase "Determining the size of functions" $ do
res <- liftIO $ evaluateFileResult "./demos/size.tri"
- decodeResult res @?= "454"
+ decodeResult res @?= "321"
, testCase "Level Order Traversal demo" $ do
res <- liftIO $ evaluateFileResult "./demos/levelOrderTraversal.tri"
decodeResult res @?= "\"\n1 \n2 3 \n4 5 6 7 \n8 11 10 9 12 \""
@@ -569,3 +571,72 @@ decoding = testGroup "Decoding Tests"
let input = ofList [ofList [ofString "nested"], ofString "string"]
decodeResult input @?= "[[\"nested\"], \"string\"]"
]
+
+elimLambdaSingle :: TestTree
+elimLambdaSingle = testCase "elimLambda preserves eval, fires eta, and SDef binds" $ do
+ -- 1) eta reduction, purely structural and parsed from source
+ let [etaIn] = parseTricu "x : f x"
+ [fRef ] = parseTricu "f"
+ elimLambda etaIn @?= fRef
+
+ -- 2) SDef binds its own name and parameters
+ let [defFXY] = parseTricu "f x y : f x"
+ fv = freeVars defFXY
+ assertBool "f should be bound in SDef" ("f" `Set.notMember` fv)
+ assertBool "x should be bound in SDef" ("x" `Set.notMember` fv)
+ assertBool "y should be bound in SDef" ("y" `Set.notMember` fv)
+
+ -- 3) semantics preserved on a small program that exercises compose and triage
+ let src =
+ unlines
+ [ "false = t"
+ , "_ = t"
+ , "true = t t"
+ , "id = a : a"
+ , "const = a b : a"
+ , "compose = f g x : f (g x)"
+ , "triage = leaf stem fork : t (t leaf stem) fork"
+ , "test = triage \"Leaf\" (_ : \"Stem\") (_ _ : \"Fork\")"
+ , "main = compose id id test"
+ ]
+ prog = parseTricu src
+ progElim = map elimLambda prog
+ evalBefore = result (evalTricu Map.empty prog)
+ evalAfter = result (evalTricu Map.empty progElim)
+ evalAfter @?= evalBefore
+
+stressElimLambda :: TestTree
+stressElimLambda = testCase "stress elimLambda on wide list under deep curried lambda" $ do
+ let numVars = 200
+ numBody = 800
+ vars = [ "x" ++ show i | i <- [1..numVars] ]
+ body = "(" ++ unwords (replicate numBody "t") ++ ")"
+ etaOne = "h : f h"
+ etaTwo = "k : id k"
+ defId = "id = a : a"
+ lambda = unwords vars ++ " : " ++ body
+ src = unlines
+ [ defId
+ , etaOne
+ , "compose = f g x : f (g x)"
+ , "f = t t"
+ , etaTwo
+ , lambda
+ , "main = compose id id (" ++ head vars ++ " : f " ++ head vars ++ ")"
+ ]
+ prog = parseTricu src
+
+ let out = map elimLambda prog
+ let noLambda term = case term of
+ SLambda _ _ -> False
+ SApp f g -> noLambda f && noLambda g
+ SList xs -> all noLambda xs
+ TFork l r -> noLambda l && noLambda r
+ TStem u -> noLambda u
+ _ -> True
+
+ assertBool "all lambdas eliminated" (all noLambda out)
+
+ let before = result (evalTricu Map.empty prog)
+ after = result (evalTricu Map.empty out)
+ after @?= before