Eval optimization! Tests for demos
This commit is contained in:
		| @ -1,19 +1,19 @@ | |||||||
| -- We represent `false` with a Leaf and `true` with a Stem Leaf | -- We represent `false` with a Leaf and `true` with a Stem Leaf | ||||||
| false = t | demo_false = t | ||||||
| true  = t t | demo_true  = t t | ||||||
|  |  | ||||||
| -- Tree Calculus representation of the Boolean `not` function | -- Tree Calculus representation of the Boolean `not` function | ||||||
| not_TC?     = t (t (t t) (t t t)) (t t (t t t)) | not_TC?     = t (t (t t) (t t t)) (t t (t t t)) | ||||||
|  |  | ||||||
| -- /demos/toSource.tri contains an explanation of `triage` | -- /demos/toSource.tri contains an explanation of `triage` | ||||||
| triage = (\a b c : t (t a b) c) | demo_triage = (\a b c : t (t a b) c) | ||||||
| matchBool = (\ot of : triage | demo_matchBool = (\ot of : demo_triage | ||||||
|   of |   of | ||||||
|   (\_ : ot) |   (\_ : ot) | ||||||
|   (\_ _ : ot) |   (\_ _ : ot) | ||||||
| ) | ) | ||||||
| -- Lambda representation of the Boolean `not` function | -- Lambda representation of the Boolean `not` function | ||||||
| not_Lambda? = matchBool false true | not_Lambda? = demo_matchBool demo_false demo_true | ||||||
|  |  | ||||||
| -- Since tricu eliminates Lambda terms to SKI combinators, the tree form of many | -- Since tricu eliminates Lambda terms to SKI combinators, the tree form of many | ||||||
| -- functions defined via Lambda terms are larger than the most efficient TC | -- functions defined via Lambda terms are larger than the most efficient TC | ||||||
| @ -25,11 +25,11 @@ not_Lambda? = matchBool false true | |||||||
| lambdaEqualsTC = equal? not_TC? not_Lambda? | lambdaEqualsTC = equal? not_TC? not_Lambda? | ||||||
|  |  | ||||||
| -- Here are some checks to verify their extensional behavior is the same: | -- Here are some checks to verify their extensional behavior is the same: | ||||||
| true_TC?  = not_TC? false | true_TC?  = not_TC? demo_false | ||||||
| false_TC? = not_TC? true | false_TC? = not_TC? demo_true | ||||||
|  |  | ||||||
| true_Lambda?  = not_Lambda? false | true_Lambda?  = not_Lambda? demo_false | ||||||
| false_Lambda? = not_Lambda? true | false_Lambda? = not_Lambda? demo_true | ||||||
|  |  | ||||||
| bothTrueEqual?  = equal? true_TC?  true_Lambda? | bothTrueEqual?  = equal? true_TC?  true_Lambda? | ||||||
| bothFalseEqual? = equal? false_TC? false_Lambda? | bothFalseEqual? = equal? false_TC? false_Lambda? | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| -- NOTICE: This demo relies on tricu base library functions | -- NOTICE: This demo relies on tricu base library functions | ||||||
| --  | --  | ||||||
| -- We model labelled binary trees as sublists where values act as labels. We | -- We model labelled binary trees as sublists where values act as labels. We | ||||||
| -- require explicit notation of empty nodes. Empty nodes can be represented  | -- require explicit not?ation of empty nodes. Empty nodes can be represented  | ||||||
| -- with an empty list, `[]`, which is equivalent to a single node `t`.  | -- with an empty list, `[]`, which is equivalent to a single node `t`.  | ||||||
| -- | -- | ||||||
| -- Example tree inputs: | -- Example tree inputs: | ||||||
| @ -19,33 +19,33 @@ | |||||||
|  |  | ||||||
| label = (\node : head node) | label = (\node : head node) | ||||||
|  |  | ||||||
| left  = (\node : if (emptyList node)  | left  = (\node : if (emptyList? node)  | ||||||
|   []  |   []  | ||||||
|   (if (emptyList (tail node))  |   (if (emptyList? (tail node))  | ||||||
|     []  |     []  | ||||||
|     (head (tail node)))) |     (head (tail node)))) | ||||||
|  |  | ||||||
| right = (\node : if (emptyList node)  | right = (\node : if (emptyList? node)  | ||||||
|   []  |   []  | ||||||
|   (if (emptyList (tail node))  |   (if (emptyList? (tail node))  | ||||||
|     []  |     []  | ||||||
|     (if (emptyList (tail (tail node)))  |     (if (emptyList? (tail (tail node)))  | ||||||
|       []  |       []  | ||||||
|       (head (tail (tail node)))))) |       (head (tail (tail node)))))) | ||||||
|  |  | ||||||
| processLevel = y (\self queue : if (emptyList queue)  | processLevel = y (\self queue : if (emptyList? queue)  | ||||||
|   []  |   []  | ||||||
|   (pair (map label queue) (self (filter  |   (pair (map label queue) (self (filter  | ||||||
|     (\node : not (emptyList node))  |     (\node : not? (emptyList? node))  | ||||||
|       (lconcat (map left queue) (map right queue)))))) |       (lconcat (map left queue) (map right queue)))))) | ||||||
|  |  | ||||||
| levelOrderTraversal_ = (\a : processLevel (t a t)) | levelOrderTraversal_ = (\a : processLevel (t a t)) | ||||||
|  |  | ||||||
| toLineString = y (\self levels : if (emptyList levels)  | toLineString = y (\self levels : if (emptyList? levels)  | ||||||
|   ""  |   ""  | ||||||
|   (lconcat  |   (lconcat  | ||||||
|     (lconcat (map (\x : lconcat x " ") (head levels)) "")  |     (lconcat (map (\x : lconcat x " ") (head levels)) "")  | ||||||
|     (if (emptyList (tail levels)) "" (lconcat (t (t 10 t) t) (self (tail levels)))))) |     (if (emptyList? (tail levels)) "" (lconcat (t (t 10 t) t) (self (tail levels)))))) | ||||||
|  |  | ||||||
| levelOrderToString = (\s : toLineString (levelOrderTraversal_ s)) | levelOrderToString = (\s : toLineString (levelOrderTraversal_ s)) | ||||||
|  |  | ||||||
|  | |||||||
| @ -17,3 +17,5 @@ size = (\x : | |||||||
|         self  |         self  | ||||||
|         (\x y : compose (self x) (self y))  |         (\x y : compose (self x) (self y))  | ||||||
|         x)) x 0)) |         x)) x 0)) | ||||||
|  |  | ||||||
|  | size size | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
| -- the Tree Calculus term, `triage` enables branching logic based on the term's  | -- the Tree Calculus term, `triage` enables branching logic based on the term's  | ||||||
| -- shape, making it possible to perform structure-specific operations such as | -- shape, making it possible to perform structure-specific operations such as | ||||||
| -- reconstructing the terms' source code representation. | -- reconstructing the terms' source code representation. | ||||||
| triage = (\leaf stem fork : t (t leaf stem) fork) | -- triage = (\leaf stem fork : t (t leaf stem) fork) | ||||||
|  |  | ||||||
| -- Base case of a single Leaf | -- Base case of a single Leaf | ||||||
| sourceLeaf = t (head "t") | sourceLeaf = t (head "t") | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								src/Eval.hs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/Eval.hs
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ evalSingle env term | |||||||
|   | SVar name <- term = |   | SVar name <- term = | ||||||
|       case Map.lookup name env of |       case Map.lookup name env of | ||||||
|         Just v  -> Map.insert "__result" v env |         Just v  -> Map.insert "__result" v env | ||||||
|         Nothing -> errorWithoutStackTrace $ "Variable " ++ name ++ " not defined" |         Nothing -> errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined" | ||||||
|   | otherwise = |   | otherwise = | ||||||
|       Map.insert "__result" (evalAST env term) env |       Map.insert "__result" (evalAST env term) env | ||||||
|  |  | ||||||
| @ -59,11 +59,13 @@ evalAST env term | |||||||
| elimLambda :: TricuAST -> TricuAST | elimLambda :: TricuAST -> TricuAST | ||||||
| elimLambda = go | elimLambda = go | ||||||
|   where |   where | ||||||
|  |     go (SLambda [v] (SApp f (SVar x))) | ||||||
|  |       | v == x && not (isFree v f) = elimLambda f | ||||||
|     go (SLambda (v:vs) body) |     go (SLambda (v:vs) body) | ||||||
|       | null vs              = toSKI v (elimLambda body) |       | null vs                    = toSKI v (elimLambda body) | ||||||
|       | otherwise            = elimLambda (SLambda [v] (SLambda vs body)) |       | otherwise                  = elimLambda (SLambda [v] (SLambda vs body)) | ||||||
|     go (SApp f g)            = SApp (elimLambda f) (elimLambda g) |     go (SApp f g)                  = SApp (elimLambda f) (elimLambda g) | ||||||
|     go x                     = x |     go x                           = x | ||||||
|  |  | ||||||
|     toSKI x (SVar y) |     toSKI x (SVar y) | ||||||
|       | x == y           = _I |       | x == y           = _I | ||||||
| @ -73,7 +75,6 @@ elimLambda = go | |||||||
|       | otherwise        = SApp (SApp _S (toSKI x n)) (toSKI x u) |       | otherwise        = SApp (SApp _S (toSKI x n)) (toSKI x u) | ||||||
|     toSKI x t |     toSKI x t | ||||||
|       | not (isFree x t) = SApp _K t |       | not (isFree x t) = SApp _K t | ||||||
|       | otherwise        = SApp (SApp _S (toSKI x t)) TLeaf |  | ||||||
|  |  | ||||||
|     _S = parseSingle "t (t (t t t)) t" |     _S = parseSingle "t (t (t t t)) t" | ||||||
|     _K = parseSingle "t t" |     _K = parseSingle "t t" | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								test/Spec.hs
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								test/Spec.hs
									
									
									
									
									
								
							| @ -25,16 +25,17 @@ runTricu s = show $ result (evalTricu Map.empty $ parseTricu s) | |||||||
|  |  | ||||||
| tests :: TestTree | tests :: TestTree | ||||||
| tests = testGroup "Tricu Tests" | tests = testGroup "Tricu Tests" | ||||||
|   [ lexerTests |   [ lexer | ||||||
|   , parserTests |   , parser | ||||||
|   , evaluationTests |   , simpleEvaluation | ||||||
|   , lambdaEvalTests |   , lambdas | ||||||
|   , libraryTests |   , baseLibrary | ||||||
|   , fileEvaluationTests |   , fileEval | ||||||
|  |   , demos | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| lexerTests :: TestTree | lexer :: TestTree | ||||||
| lexerTests = testGroup "Lexer Tests" | lexer = testGroup "Lexer Tests" | ||||||
|   [ testCase "Lex simple identifiers" $ do |   [ testCase "Lex simple identifiers" $ do | ||||||
|       let input = "x a b = a" |       let input = "x a b = a" | ||||||
|           expect = Right [LIdentifier "x", LIdentifier "a", LIdentifier "b", LAssign, LIdentifier "a"] |           expect = Right [LIdentifier "x", LIdentifier "a", LIdentifier "b", LAssign, LIdentifier "a"] | ||||||
| @ -74,8 +75,8 @@ lexerTests = testGroup "Lexer Tests" | |||||||
|           Right _ -> assertFailure "Expected failure when trying to assign the value of __result" |           Right _ -> assertFailure "Expected failure when trying to assign the value of __result" | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| parserTests :: TestTree | parser :: TestTree | ||||||
| parserTests = testGroup "Parser Tests" | parser = testGroup "Parser Tests" | ||||||
|   [ testCase "Error when assigning a value to T" $ do |   [ testCase "Error when assigning a value to T" $ do | ||||||
|       let tokens = lexTricu "t = x"  |       let tokens = lexTricu "t = x"  | ||||||
|       case parseSingleExpr tokens of |       case parseSingleExpr tokens of | ||||||
| @ -175,8 +176,8 @@ parserTests = testGroup "Parser Tests" | |||||||
|       parseTricu input @?= expect |       parseTricu input @?= expect | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| evaluationTests :: TestTree | simpleEvaluation :: TestTree | ||||||
| evaluationTests = testGroup "Evaluation Tests" | simpleEvaluation = testGroup "Evaluation Tests" | ||||||
|   [ testCase "Evaluate single Leaf" $ do |   [ testCase "Evaluate single Leaf" $ do | ||||||
|       let input = "t" |       let input = "t" | ||||||
|       let ast = parseSingle input |       let ast = parseSingle input | ||||||
| @ -244,7 +245,7 @@ evaluationTests = testGroup "Evaluation Tests" | |||||||
|       (result env) @?= (Stem (Stem Leaf)) |       (result env) @?= (Stem (Stem Leaf)) | ||||||
|  |  | ||||||
|  |  | ||||||
|   , testCase "Evaluate variable shadowing" $ do |   , testCase "Immutable definitions" $ do | ||||||
|       let input = "x = t t\nx = t\nx" |       let input = "x = t t\nx = t\nx" | ||||||
|           env = evalTricu Map.empty (parseTricu input) |           env = evalTricu Map.empty (parseTricu input) | ||||||
|       result <- try (evaluate (runTricu input)) :: IO (Either SomeException String) |       result <- try (evaluate (runTricu input)) :: IO (Either SomeException String) | ||||||
| @ -260,8 +261,8 @@ evaluationTests = testGroup "Evaluation Tests" | |||||||
|       result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) Leaf |       result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) Leaf | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| lambdaEvalTests :: TestTree | lambdas :: TestTree | ||||||
| lambdaEvalTests = testGroup "Lambda Evaluation Tests" | lambdas = testGroup "Lambda Evaluation Tests" | ||||||
|   [ testCase "Lambda Identity Function" $ do |   [ testCase "Lambda Identity Function" $ do | ||||||
|       let input = "id = (\\x : x)\nid t" |       let input = "id = (\\x : x)\nid t" | ||||||
|       runTricu input @?= "Leaf" |       runTricu input @?= "Leaf" | ||||||
| @ -340,8 +341,8 @@ lambdaEvalTests = testGroup "Lambda Evaluation Tests" | |||||||
|         runTricu input @?= "Fork Leaf (Fork (Stem Leaf) Leaf)" |         runTricu input @?= "Fork Leaf (Fork (Stem Leaf) Leaf)" | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| libraryTests :: TestTree | baseLibrary :: TestTree | ||||||
| libraryTests = testGroup "Library Tests" | baseLibrary = testGroup "Library Tests" | ||||||
|   [ testCase "K combinator 1" $ do |   [ testCase "K combinator 1" $ do | ||||||
|       library <- evaluateFile "./lib/base.tri" |       library <- evaluateFile "./lib/base.tri" | ||||||
|       let input = "k (t) (t t)" |       let input = "k (t) (t t)" | ||||||
| @ -476,8 +477,8 @@ libraryTests = testGroup "Library Tests" | |||||||
|       result env @?= Stem Leaf |       result env @?= Stem Leaf | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
| fileEvaluationTests :: TestTree | fileEval :: TestTree | ||||||
| fileEvaluationTests = testGroup "Evaluation tests" | fileEval = testGroup "File evaluation tests" | ||||||
|   [ testCase "Forks" $ do |   [ testCase "Forks" $ do | ||||||
|       res <- liftIO $ evaluateFileResult "./test/fork.tri" |       res <- liftIO $ evaluateFileResult "./test/fork.tri" | ||||||
|       res @?= Fork Leaf Leaf |       res @?= Fork Leaf Leaf | ||||||
| @ -495,3 +496,24 @@ fileEvaluationTests = testGroup "Evaluation tests" | |||||||
|       res <- liftIO $ evaluateFileWithContext library "./test/string.tri" |       res <- liftIO $ evaluateFileWithContext library "./test/string.tri" | ||||||
|       decodeResult (result res) @?= "\"String test!\"" |       decodeResult (result res) @?= "\"String test!\"" | ||||||
|   ] |   ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | demos :: TestTree | ||||||
|  | demos = testGroup "Test provided demo functionality" | ||||||
|  |   [ testCase "Structural equality demo" $ do | ||||||
|  |       library <- liftIO $ evaluateFile "./lib/base.tri" | ||||||
|  |       res     <- liftIO $ evaluateFileWithContext library "./demos/equality.tri" | ||||||
|  |       decodeResult (result res) @?= "t t" | ||||||
|  |   , testCase "Convert values back to source code demo" $ do | ||||||
|  |       library <- liftIO $ evaluateFile "./lib/base.tri" | ||||||
|  |       res     <- liftIO $ evaluateFileWithContext library "./demos/toSource.tri" | ||||||
|  |       decodeResult (result res) @?= "\"(t (t (t t) (t t t)) (t t (t t t)))\"" | ||||||
|  |   , testCase "Determining the size of functions" $ do | ||||||
|  |       library <- liftIO $ evaluateFile "./lib/base.tri" | ||||||
|  |       res     <- liftIO $ evaluateFileWithContext library "./demos/size.tri" | ||||||
|  |       decodeResult (result res) @?= "2071" | ||||||
|  |   , testCase "Level Order Traversal demo" $ do | ||||||
|  |       library <- liftIO $ evaluateFile "./lib/base.tri" | ||||||
|  |       res     <- liftIO $ evaluateFileWithContext library "./demos/levelOrderTraversal.tri" | ||||||
|  |       decodeResult (result res) @?= "\"\n1 \n2 3 \n4 5 6 7 \n8 11 10 9 12 \"" | ||||||
|  |   ] | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 James Eversole
						James Eversole