tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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:
Mtest/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