diff --git a/README.md b/README.md index 26953fb..f08be1c 100644 --- 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 index 57fe0a9..09c9df2 100644 --- 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 index 79e6db1..b3434c8 100644 --- 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 index 096ddf5..3a601fd 100644 --- 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 index b59647b..c182dd8 100644 --- 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 ->