commit f41c92fa9d7983ad0a14761729cc45f975a5128f
parent 7a7ee6886a232c240247a8264b771165ea56b3e5
Author: James Eversole <james@eversole.co>
Date: Sun, 29 Dec 2024 10:28:32 -0600
Expansion of testing suite to cover incl. library
Expands the testing suite to verify behavior of provided library
functions. Updates the README further for clarification on important
concepts.
Diffstat:
5 files changed, 118 insertions(+), 46 deletions(-)
diff --git a/README.md b/README.md
@@ -2,27 +2,30 @@
## Introduction
-tricu (pronounced like "tree-shoe") is a "micro-language" that [I'm](https://eversole.co) working on to investigate [Tree Calculus](https://github.com/barry-jay-personal/typed_tree_calculus/blob/main/typed_program_analysis.pdf).
+tricu (pronounced like the words "tree-shoe" in English) is a language that [I'm](https://eversole.co) working on to investigate [Tree Calculus](https://github.com/barry-jay-personal/typed_tree_calculus/blob/main/typed_program_analysis.pdf).
-tricu [means tree in Lojban](https://en.wiktionary.org/wiki/Appendix:Lojban/tricu). This project was named "sapling" until I discovered the name was already being used for other projects in programming language development.
+tricu is the word for "tree" in Lojban (`(x1) is a tree of species/cultivar (x2)`). This project was named "sapling" until I discovered the name is already being used for other (completely unrelated) programming language development projects.
-tricu offers a minimal amount of syntax sugar yet provides a complete and intuitive programming environment. tricu offers:
+tricu offers minimal syntax sugar yet manages to provide a complete, intuitive, and familiar programming environment. There is great power in simplicity. tricu offers:
-- `t` operator behaving by the rules of Tree Calculus
-- Function ("variable") definitions
-- Lambda abstractions
-- List, Number, and String literals
+1. `t` operator behaving by the rules of Tree Calculus
+1. Function definitions/assignments
+1. Lambda abstractions eliminated to Tree Calculus forms
+1. List, Number, and String literals
+1. Parentheses intuitively grouping function application
+
+These basic features move us cleanly out of the [turing tarpit](https://en.wikipedia.org/wiki/Turing_tarpit) territory that you may find yourself in if you try working only with the `t` operator.
## What does it look like?
```
-- Anything after `--` on a line is a comment
--- We can define functions or "variables" as tree calculus values
+-- We can define functions or "variables" as Tree Calculus values
false = t
_ = t
true = t t
--- We can define functions as lambda expressions that are eliminated to tree
--- calculus terms.
+-- We can define functions as lambda expressions that are eliminated to Tree
+-- Calculus terms.
id = (\a : a) -- `id` evaluates to the TC form of: t (t (t t)) t
triage = (\a b c : t (t a b) c)
-- Intensionality! We can inspect program structure, not just inputs/outputs:
diff --git a/src/Library.hs b/src/Library.hs
@@ -30,14 +30,13 @@ library = evalTricu Map.empty $ parseTricu $ unlines
, "matchBool = (\\ot of : triage of (\\_ : ot) (\\_ _ : ot))"
, "matchList = (\\oe oc : triage oe _ oc)"
, "matchPair = (\\op : triage _ _ op)"
+ , "not = matchBool false true"
, "and = matchBool id (\\z : false)"
, "if = (\\cond then else : t (t else (t t then)) t cond)"
- , "test = triage \"leaf\" (\\z : \"stem\") (\\a b : \"fork\")"
+ , "test = triage \"Leaf\" (\\z : \"Stem\") (\\a b : \"Fork\")"
, "emptyList = matchList true (\\y z : false)"
- , "nonEmptyList = matchList false (\\y z : true)"
, "head = matchList t (\\hd tl : hd)"
, "tail = matchList t (\\hd tl : tl)"
- , "isLeaf = (\\_ : triage true false false)"
, "listConcat = y (\\self : matchList (\\k : k) (\\h r k : pair h (self r k)))"
, "lAnd = triage (\\x : false) (\\_ x : x) (\\_ _ x : x)"
, "lOr = triage (\\x : x) (\\_ _ : true) (\\_ _ x : true)"
diff --git a/src/Main.hs b/src/Main.hs
@@ -12,7 +12,7 @@ import Text.Megaparsec (runParser)
main :: IO ()
main = do
- putStrLn "Welcome to the Tricu Interpreter"
+ putStrLn "Welcome to the tricu Interpreter"
putStrLn "You can exit at any time by typing and entering: "
putStrLn ":_exit"
repl library
diff --git a/src/REPL.hs b/src/REPL.hs
@@ -28,7 +28,7 @@ repl env = runInputT defaultSettings (loop env)
case Map.lookup "__result" newEnv of
Just r -> do
outputStrLn $ "tricu > " ++ show r
- outputStrLn $ "DECODE -: " ++ decodeResult r
+ outputStrLn $ "DECODE -: \"" ++ decodeResult r ++ "\""
Nothing -> return ()
loop newEnv
@@ -36,7 +36,7 @@ decodeResult :: T -> String
decodeResult tc = case toNumber tc of
Right num -> show num
Left _ -> case toString tc of
- Right str -> "\"" ++ str ++ "\""
+ Right str -> str
Left _ -> case toList tc of
Right list -> "[" ++ intercalate ", " (map decodeResult list) ++ "]"
Left _ -> ""
diff --git a/test/Spec.hs b/test/Spec.hs
@@ -4,6 +4,7 @@ import Eval
import Lexer
import Library
import Parser
+import REPL
import Research
import Control.Exception (evaluate, try, SomeException)
import Test.Tasty
@@ -27,6 +28,7 @@ tests = testGroup "Tricu Tests"
, integrationTests
, evaluationTests
, lambdaEvalTests
+ , libraryTests
, propertyTests
]
@@ -67,12 +69,12 @@ lexerTests = testGroup "Lexer Tests"
parserTests :: TestTree
parserTests = testGroup "Parser Tests"
- [ --testCase "Error when parsing incomplete definitions" $ do
- -- let input = lexTricu "x = "
- -- case (runParser parseExpression "" input) of
- -- Left _ -> return ()
- -- Right _ -> assertFailure "Expected failure on invalid input"
- testCase "Error when assigning a value to T" $ do
+ [-- testCase "Error when parsing incomplete definitions" $ do
+ -- let input = lexTricu "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 = lexTricu "t = x"
case (runParser parseExpression "" input) of
Left _ -> return ()
@@ -222,30 +224,6 @@ evaluationTests = testGroup "Evaluation Tests"
let input = "x = (\\a : a)\nx " ++ not
env = evalTricu Map.empty (parseTricu input)
result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) Leaf
- , testCase "Constant function matches" $ do
- let input = "k = (\\a b : a)\nk (t t) t"
- env = evalTricu Map.empty (parseTricu input)
- result env @?= Stem Leaf
- , testCase "Boolean AND_ TF" $ do
- let input = "and (t t) (t)"
- env = evalTricu library (parseTricu input)
- result env @?= Leaf
- , testCase "Boolean AND_ FT" $ do
- let input = "and (t) (t t)"
- env = evalTricu library (parseTricu input)
- result env @?= Leaf
- , testCase "Boolean AND_ FF" $ do
- let input = "and (t) (t)"
- env = evalTricu library (parseTricu input)
- result env @?= Leaf
- , testCase "Boolean AND_ TT" $ do
- let input = "and (t t) (t t)"
- env = evalTricu library (parseTricu input)
- result env @?= Stem Leaf
- , testCase "Verifying Equality" $ do
- let input = "equal (t t t) (t t t)"
- env = evalTricu library (parseTricu input)
- result env @?= Stem Leaf
]
lambdaEvalTests :: TestTree
@@ -309,6 +287,98 @@ lambdaEvalTests = testGroup "Lambda Evaluation Tests"
runTricu input @?= "Fork Leaf (Fork (Stem Leaf) Leaf)"
]
+libraryTests :: TestTree
+libraryTests = testGroup "Library Tests"
+ [ testCase "K combinator 1" $ do
+ let input = "k (t) (t t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Leaf
+ , testCase "K combinator 2" $ do
+ let input = "k (t t) (t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Stem Leaf
+ , testCase "K combinator 3" $ do
+ let input = "k (t t t) (t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork Leaf Leaf
+ , testCase "S combinator" $ do
+ let input = "s (t) (t) (t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork Leaf (Stem Leaf)
+ , testCase "SKK == I" $ do -- Tests for fully expanded I form
+ let input = "s k k"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork (Stem (Stem Leaf)) (Stem Leaf)
+ , testCase "I combinator" $ do
+ let input = "i not"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) (Fork Leaf (Fork Leaf Leaf))
+ , testCase "Triage test Leaf" $ do
+ let input = "test t"
+ env = decodeResult $ result $ evalTricu library (parseTricu input)
+ env @?= "Leaf"
+ , testCase "Triage test (Stem Leaf)" $ do
+ let input = "test (t t)"
+ env = decodeResult $ result $ evalTricu library (parseTricu input)
+ env @?= "Stem"
+ , testCase "Triage test (Fork Leaf Leaf)" $ do
+ let input = "test (t t t)"
+ env = decodeResult $ result $ evalTricu library (parseTricu input)
+ env @?= "Fork"
+ , testCase "Boolean NOT: true" $ do
+ let input = "not true"
+ env = result $ evalTricu library (parseTricu input)
+ env @?= Leaf
+ , testCase "Boolean NOT: false" $ do
+ let input = "not false"
+ env = result $ evalTricu library (parseTricu input)
+ env @?= Stem Leaf
+ , testCase "Boolean AND TF" $ do
+ let input = "and (t t) (t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Leaf
+ , testCase "Boolean AND FT" $ do
+ let input = "and (t) (t t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Leaf
+ , testCase "Boolean AND FF" $ do
+ let input = "and (t) (t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Leaf
+ , testCase "Boolean AND TT" $ do
+ let input = "and (t t) (t t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Stem Leaf
+ , testCase "List head" $ do
+ let input = "head [(t) (t t) (t t t)]"
+ env = evalTricu library (parseTricu input)
+ result env @?= Leaf
+ , testCase "List tail" $ do
+ let input = "head (tail (tail [(t) (t t) (t t t)]))"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork Leaf Leaf
+ , testCase "List map" $ do
+ let input = "head (tail (map (\\a : (t t t)) [(t) (t) (t)]))"
+ env = evalTricu library (parseTricu input)
+ result env @?= Fork Leaf Leaf
+ , testCase "Empty list check" $ do
+ let input = "emptyList []"
+ env = evalTricu library (parseTricu input)
+ result env @?= Stem Leaf
+ , testCase "Non-empty list check" $ do
+ let input = "not (emptyList [(1) (2) (3)])"
+ env = evalTricu library (parseTricu input)
+ result env @?= Stem Leaf
+ , testCase "Concatenate strings" $ do
+ let input = "listConcat \"Hello, \" \"world!\""
+ env = decodeResult $ result $ evalTricu library (parseTricu input)
+ env @?= "Hello, world!"
+ , testCase "Verifying Equality" $ do
+ let input = "equal (t t t) (t t t)"
+ env = evalTricu library (parseTricu input)
+ result env @?= Stem Leaf
+ ]
+
propertyTests :: TestTree
propertyTests = testGroup "Property Tests"
[ testProperty "Lexing and parsing round-trip" $ \input ->