0.2.0
Includes better error handling, additional tests, parsing and lexing fixes to match the desired behavior defined by the new tests, and a very basic REPL implementation.
This commit is contained in:
		
							
								
								
									
										175
									
								
								test/Spec.hs
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								test/Spec.hs
									
									
									
									
									
								
							| @ -28,195 +28,215 @@ lexerTests :: TestTree | ||||
| lexerTests = testGroup "Lexer Tests" | ||||
|   [ testCase "Lex simple identifiers" $ do | ||||
|       let input = "x a b = a" | ||||
|       let expected = Right [LIdentifier "x", LIdentifier "a", LIdentifier "b", LAssign, LIdentifier "a"] | ||||
|       runParser saplingLexer "" input @?= expected | ||||
|       let expect = Right [LIdentifier "x", LIdentifier "a", LIdentifier "b", LAssign, LIdentifier "a"] | ||||
|       runParser saplingLexer "" input @?= expect | ||||
|  | ||||
|   , testCase "Lex Tree Calculus terms" $ do | ||||
|       let input = "t t t" | ||||
|       let expected = Right [LKeywordT, LKeywordT, LKeywordT] | ||||
|       runParser saplingLexer "" input @?= expected | ||||
|  | ||||
|   , testCase "Handle invalid input" $ do | ||||
|       let input = "x = " | ||||
|       case runParser saplingLexer "" input of | ||||
|         Left _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure on invalid input" | ||||
|       let expect = Right [LKeywordT, LKeywordT, LKeywordT] | ||||
|       runParser saplingLexer "" input @?= expect | ||||
|  | ||||
|   , testCase "Lex escaped characters in strings" $ do | ||||
|       let input = "\"hello\\nworld\"" | ||||
|       let expected = Right [LStringLiteral "hello\\nworld"] | ||||
|       runParser saplingLexer "" input @?= expected | ||||
|       let expect = Right [LStringLiteral "hello\\nworld"] | ||||
|       runParser saplingLexer "" input @?= expect | ||||
|  | ||||
|   , testCase "Lex mixed literals" $ do | ||||
|       let input = "t \"string\" 42" | ||||
|       let expected = Right [LKeywordT, LStringLiteral "string", LIntegerLiteral 42] | ||||
|       runParser saplingLexer "" input @?= expected | ||||
|       let expect = Right [LKeywordT, LStringLiteral "string", LIntegerLiteral 42] | ||||
|       runParser saplingLexer "" input @?= expect | ||||
|  | ||||
|   , testCase "Lex invalid token" $ do | ||||
|       let input = "$invalid" | ||||
|       case runParser saplingLexer "" input of | ||||
|           Left _ -> return () | ||||
|           Right _ -> assertFailure "Expected lexer to fail on invalid token" | ||||
|  | ||||
|   , testCase "Drop trailing whitespace in definitions" $ do | ||||
|       let input = "x = 5 " | ||||
|       let expect = [LIdentifier "x",LAssign,LIntegerLiteral 5] | ||||
|       case (runParser saplingLexer "" input) of | ||||
|        Left  _ -> assertFailure "Failed to lex input" | ||||
|        Right i -> i @?= expect | ||||
|  | ||||
|   , testCase "Error when using invalid characters in identifiers" $ do | ||||
|       case (runParser saplingLexer "" "__result = 5") of | ||||
|         Left  _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure when trying to assign the value of __result" | ||||
|   ] | ||||
|  | ||||
| parserTests :: TestTree | ||||
| parserTests = testGroup "Parser Tests" | ||||
|   [ testCase "Parse function definitions" $ do | ||||
|   [ testCase "Error when parsing incomplete definitions" $ do | ||||
|       let input = lexSapling "x = " | ||||
|       case (runParser parseExpression "" input) of | ||||
|         Left  _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure on invalid input" | ||||
|  | ||||
|   , testCase "Error when assigning a value to T" $ do | ||||
|       let input = lexSapling "t = x" | ||||
|       case (runParser parseExpression "" input) of | ||||
|         Left  _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure when trying to assign the value of T" | ||||
|  | ||||
|   , testCase "Error when parsing bodyless definitions with arguments" $ do | ||||
|       let input = lexSapling "x a b = " | ||||
|       case (runParser parseExpression "" input) of | ||||
|         Left  _ -> return () | ||||
|         Right _ -> assertFailure "Expected failure on invalid input" | ||||
|  | ||||
|   , testCase "Parse function definitions" $ do | ||||
|       let input = "x a b = a" | ||||
|       let expected = SFunc "x" ["a", "b"] (SApp (SVar "a") []) | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SFunc "x" ["a", "b"] (SApp (SVar "a") []) | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse nested Tree Calculus terms" $ do | ||||
|       let input = "t (t t) t" | ||||
|       let expected = TFork (TStem TLeaf) TLeaf | ||||
|       parseSapling input @?= expected | ||||
|       let expect = TFork (TStem TLeaf) TLeaf | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse sequential Tree Calculus terms" $ do | ||||
|       let input = "t t t" | ||||
|       let expected = TFork TLeaf TLeaf | ||||
|       parseSapling input @?= expected | ||||
|       let expect = TFork TLeaf TLeaf | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse mixed list literals" $ do | ||||
|       -- You must put non-list literals in parentheses | ||||
|       let input = "[t (\"hello\") t]"  | ||||
|       let expected = SList [TLeaf, SStr "hello", TLeaf] | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SList [TLeaf, SStr "hello", TLeaf] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse function with applications" $ do | ||||
|       let input = "f x = t x" | ||||
|       let expected = SFunc "f" ["x"] (SApp TLeaf [SVar "x"]) | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SFunc "f" ["x"] (SApp TLeaf [SVar "x"]) | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse nested lists" $ do | ||||
|       let input = "[t [(t t)]]" | ||||
|       let expected = SList [TLeaf, SList [TStem TLeaf]] | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SList [TLeaf, SList [TStem TLeaf]] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse complex parentheses" $ do | ||||
|       let input = "t (t t (t t))" | ||||
|       let expected = TStem (TFork TLeaf (TStem TLeaf)) | ||||
|       parseSapling input @?= expected | ||||
|       let expect = TStem (TFork TLeaf (TStem TLeaf)) | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse empty list" $ do | ||||
|       let input = "[]" | ||||
|       let expected = SList [] | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SList [] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse multiple nested lists" $ do | ||||
|       let input = "[[t t] [t (t t)]]" | ||||
|       let expected = SList [SList [TLeaf, TLeaf], SList [TLeaf, TStem TLeaf]] | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SList [SList [TLeaf, TLeaf], SList [TLeaf, TStem TLeaf]] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse whitespace variance" $ do | ||||
|       let input1 = "[t    t]" | ||||
|       let input2 = "[ t  t ]" | ||||
|       let expected = SList [TLeaf, TLeaf] | ||||
|       parseSapling input1 @?= expected | ||||
|       parseSapling input2 @?= expected | ||||
|       let expect = SList [TLeaf, TLeaf] | ||||
|       parseSingle input1 @?= expect | ||||
|       parseSingle input2 @?= expect | ||||
|  | ||||
|   , testCase "Parse string in list" $ do | ||||
|       let input = "[(\"hello\")]" | ||||
|       let expected = SList [SStr "hello"] | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SList [SStr "hello"] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse parentheses inside list" $ do | ||||
|       let input = "[t (t t)]" | ||||
|       let expected = SList [TLeaf, TStem TLeaf] | ||||
|       parseSapling input @?= expected | ||||
|  | ||||
|   -- Do I want to allow multi-line indentation-sensitive syntax? | ||||
|   -- Probably not. | ||||
|   --, testCase "Parse multi-line function definition" $ do | ||||
|   --    let input = "f x y =\n  t t" | ||||
|   --    let expected = SFunc "f" ["x", "y"] (TStem TLeaf) | ||||
|   --    parseSapling input @?= expected | ||||
|       let expect = SList [TLeaf, TStem TLeaf] | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Parse nested parentheses in function body" $ do | ||||
|       let input = "f = t (t (t t))" | ||||
|       let expected = SFunc "f" [] (TStem (TStem (TStem TLeaf))) | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SFunc "f" [] (TStem (TStem (TStem TLeaf))) | ||||
|       parseSingle input @?= expect | ||||
|   ] | ||||
|  | ||||
| integrationTests :: TestTree | ||||
| integrationTests = testGroup "Integration Tests" | ||||
|   [ testCase "Combine lexer and parser" $ do | ||||
|       let input = "x = t t t" | ||||
|       let expected = SFunc "x" [] (TFork TLeaf TLeaf) | ||||
|       parseSapling input @?= expected | ||||
|       let expect = SFunc "x" [] (TFork TLeaf TLeaf) | ||||
|       parseSingle input @?= expect | ||||
|  | ||||
|   , testCase "Complex Tree Calculus expression" $ do | ||||
|       let input = "t (t t t) t" | ||||
|       let expected = TFork (TFork TLeaf TLeaf) TLeaf | ||||
|       parseSapling input @?= expected | ||||
|       let expect = TFork (TFork TLeaf TLeaf) TLeaf | ||||
|       parseSingle input @?= expect | ||||
|   ] | ||||
|  | ||||
| evaluationTests :: TestTree | ||||
| evaluationTests = testGroup "Evaluation Tests" | ||||
|   [ testCase "Evaluate single Leaf" $ do | ||||
|       let input = "t" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= Leaf | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= Leaf | ||||
|  | ||||
|   , testCase "Evaluate single Stem" $ do | ||||
|       let input = "t t" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= Stem Leaf | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= Stem Leaf | ||||
|  | ||||
|   , testCase "Evaluate single Fork" $ do | ||||
|       let input = "t t t" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= Fork Leaf Leaf | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= Fork Leaf Leaf | ||||
|  | ||||
|   , testCase "Evaluate nested Fork and Stem" $ do | ||||
|       let input = "t (t t) t" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= Fork (Stem Leaf) Leaf | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= Fork (Stem Leaf) Leaf | ||||
|  | ||||
|   , testCase "Evaluate `not` function" $ do | ||||
|       let input = "t (t (t t) (t t t)) t)" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?=  | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?=  | ||||
|         Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) Leaf | ||||
|  | ||||
|   , testCase "Environment updates with definitions" $ do | ||||
|       let input = "x = t\ny = x" | ||||
|       let env = evalMulti Map.empty (parseMulti input) | ||||
|       let env = evalSapling Map.empty (parseSapling input) | ||||
|       Map.lookup "x" env @?= Just Leaf | ||||
|       Map.lookup "y" env @?= Just Leaf | ||||
|  | ||||
|   , testCase "Variable substitution" $ do | ||||
|       let input = "x = t t\ny = t x\ny" | ||||
|       let env = evalMulti Map.empty (parseMulti input) | ||||
|       let env = evalSapling Map.empty (parseSapling input) | ||||
|       (result env) @?= Stem (Stem Leaf) | ||||
|  | ||||
|   , testCase "Multiline input evaluation" $ do | ||||
|       let input = "x = t\ny = t t\nx" | ||||
|       let env = evalMulti Map.empty (parseMulti input) | ||||
|       let env = evalSapling Map.empty (parseSapling input) | ||||
|       (result env) @?= Leaf | ||||
|  | ||||
|   , testCase "Evaluate string literal" $ do | ||||
|       let input = "\"hello\"" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= toString "hello" | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= toString "hello" | ||||
|  | ||||
|   , testCase "Evaluate list literal" $ do | ||||
|       let input = "[t (t t)]" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= toList [Leaf, Stem Leaf] | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= toList [Leaf, Stem Leaf] | ||||
|  | ||||
|   , testCase "Evaluate empty list" $ do | ||||
|       let input = "[]" | ||||
|       let ast = parseSapling input | ||||
|       (result $ evalSapling Map.empty ast) @?= toList [] | ||||
|       let ast = parseSingle input | ||||
|       (result $ evalSingle Map.empty ast) @?= toList [] | ||||
|  | ||||
|   , testCase "Evaluate variable dependency chain" $ do | ||||
|       let input = "x = t\ny = t x\nz = t y\nz" | ||||
|       let env = evalMulti Map.empty (parseMulti input) | ||||
|       let input = "x = t\n \ | ||||
|                  \ y = t x\n \ | ||||
|                  \ z = t y\n \ | ||||
|                  \ variablewithamuchlongername = z\n \ | ||||
|                  \ variablewithamuchlongername" | ||||
|       let env = evalSapling Map.empty (parseSapling input) | ||||
|       (result env) @?= (Stem (Stem Leaf)) | ||||
|  | ||||
|   , testCase "Evaluate redefinition of variables" $ do | ||||
|       let input = "x = t t\nx = t\nx" | ||||
|       let env = evalMulti Map.empty (parseMulti input) | ||||
|       let env = evalSapling Map.empty (parseSapling input) | ||||
|       (result env) @?= Leaf | ||||
|   ] | ||||
|  | ||||
| @ -227,6 +247,5 @@ propertyTests = testGroup "Property Tests" | ||||
|         Left _ -> property True  | ||||
|         Right tokens -> case runParser parseExpression "" tokens of | ||||
|           Left _ -> property True | ||||
|           Right ast -> parseSapling input === ast | ||||
|           Right ast -> parseSingle input === ast | ||||
|   ] | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 James Eversole
					James Eversole