From 918d929c0995ba38e2f3564679f369a6276b9a66 Mon Sep 17 00:00:00 2001 From: James Eversole Date: Sun, 26 Jan 2025 15:33:12 -0600 Subject: [PATCH] # File eval mode now relies on main function To encourage organizing code in a way that helps in understanding, I have implemented the common idiom of requiring a `main` function. In tricu and other functional languages, it is usually placed near the top of the module. The evaluator gracefully handles the situation of passing multiple files where the intermediary "library" files do not have main functions. --- .gitea/workflows/test-and-build.yml | 12 ++++-------- README.md | 2 +- demos/equality.tri | 2 ++ demos/levelOrderTraversal.tri | 3 +-- demos/size.tri | 4 ++-- demos/toSource.tri | 1 + src/Eval.hs | 5 +++++ src/FileEval.hs | 4 ++-- src/Main.hs | 12 ++++++------ src/REPL.hs | 7 ------- src/Research.hs | 10 ++++++++-- test/Spec.hs | 5 +++-- test/comments-1.tri | 2 +- test/fork.tri | 2 +- test/map.tri | 24 +----------------------- tricu.cabal | 2 +- 16 files changed, 39 insertions(+), 58 deletions(-) diff --git a/.gitea/workflows/test-and-build.yml b/.gitea/workflows/test-and-build.yml index 95ebb58..7b8cc18 100644 --- a/.gitea/workflows/test-and-build.yml +++ b/.gitea/workflows/test-and-build.yml @@ -54,16 +54,12 @@ jobs: cp -L ./result/bin/tricu ./tricu chmod 755 ./tricu nix develop --command upx ./tricu - - - name: Setup go for release action - uses: actions/setup-go@v5 - with: - go-version: '>=1.20.1' - + - name: Release binary - uses: https://gitea.com/actions/release-action@main + uses: akkuman/gitea-release-action@v1 with: files: |- ./tricu - api_key: '${{ secrets.RELEASE_TOKEN }}' + token: '${{ secrets.RELEASE_TOKEN }}' + body: '${{ gitea.event.head_commit.message }}' pre_release: true diff --git a/README.md b/README.md index 12eb641..720326f 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ tricu eval [OPTIONS] -f --file=FILE Input file path(s) for evaluation. Defaults to stdin. - -t --form=FORM Optional output form: (tree|fsl|ast|ternary|ascii). + -t --form=FORM Optional output form: (tree|fsl|ast|ternary|ascii|decode). Defaults to tricu-compatible `t` tree form. tricu decode [OPTIONS] diff --git a/demos/equality.tri b/demos/equality.tri index efbfabe..f450afd 100644 --- a/demos/equality.tri +++ b/demos/equality.tri @@ -1,3 +1,5 @@ +main = lambdaEqualsTC + -- We represent `false` with a Leaf and `true` with a Stem Leaf demo_false = t demo_true = t t diff --git a/demos/levelOrderTraversal.tri b/demos/levelOrderTraversal.tri index ee46261..57c9c75 100644 --- a/demos/levelOrderTraversal.tri +++ b/demos/levelOrderTraversal.tri @@ -1,3 +1,4 @@ +main = exampleTwo -- Level Order Traversal of a labelled binary tree -- Objective: Print each "level" of the tree on a separate line -- @@ -58,5 +59,3 @@ exampleTwo = levelOrderTraversal [("1") [("2") [("4") [("8") t t] [("9") t t]] [("6") [("10") t t] [("12") t t]]] [("3") [("5") [("11") t t] t] [("7") t t]]] - -exampleTwo diff --git a/demos/size.tri b/demos/size.tri index 2efd2c1..74476cb 100644 --- a/demos/size.tri +++ b/demos/size.tri @@ -1,3 +1,5 @@ +main = size size + compose = \f g x : f (g x) succ = y (\self : @@ -17,5 +19,3 @@ size = (\x : self (\x y : compose (self x) (self y)) x)) x 0)) - -size size diff --git a/demos/toSource.tri b/demos/toSource.tri index e2fb054..f8419ea 100644 --- a/demos/toSource.tri +++ b/demos/toSource.tri @@ -1,3 +1,4 @@ +main = toSource not? -- Thanks to intensionality, we can inspect the structure of a given value -- even if it's a function. This includes lambdas which are eliminated to -- Tree Calculus (TC) terms during evaluation. diff --git a/src/Eval.hs b/src/Eval.hs index 51e9993..27dc436 100644 --- a/src/Eval.hs +++ b/src/Eval.hs @@ -179,3 +179,8 @@ result :: Env -> T result r = case Map.lookup "__result" r of Just a -> a Nothing -> errorWithoutStackTrace "No __result field found in provided env" + +mainResult :: Env -> T +mainResult r = case Map.lookup "main" r of + Just a -> a + Nothing -> errorWithoutStackTrace "No valid definition for `main` found." diff --git a/src/FileEval.hs b/src/FileEval.hs index b453eed..40a14c4 100644 --- a/src/FileEval.hs +++ b/src/FileEval.hs @@ -13,9 +13,9 @@ evaluateFileResult filePath = do contents <- readFile filePath let asts = parseTricu contents let finalEnv = evalTricu Map.empty asts - case Map.lookup "__result" finalEnv of + case Map.lookup "main" finalEnv of Just finalResult -> return finalResult - Nothing -> errorWithoutStackTrace "No expressions to evaluate found" + Nothing -> errorWithoutStackTrace "No `main` function detected" evaluateFile :: FilePath -> IO Env evaluateFile filePath = do diff --git a/src/Main.hs b/src/Main.hs index 2335a99..52016da 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -1,6 +1,6 @@ module Main where -import Eval (evalTricu, result) +import Eval (evalTricu, mainResult, result) import FileEval import Parser (parseTricu) import REPL @@ -16,7 +16,7 @@ import qualified Data.Map as Map data TricuArgs = Repl | Evaluate { file :: [FilePath], form :: EvaluatedForm } - | Decode { file :: [FilePath] } + | TDecode { file :: [FilePath] } deriving (Show, Data, Typeable) replMode :: TricuArgs @@ -31,7 +31,7 @@ evaluateMode = Evaluate \ Defaults to stdin." &= name "f" &= typ "FILE" , form = TreeCalculus &= typ "FORM" - &= help "Optional output form: (tree|fsl|ast|ternary|ascii).\n \ + &= help "Optional output form: (tree|fsl|ast|ternary|ascii|decode).\n \ \ Defaults to tricu-compatible `t` tree form." &= name "t" } @@ -40,7 +40,7 @@ evaluateMode = Evaluate &= name "eval" decodeMode :: TricuArgs -decodeMode = Decode +decodeMode = TDecode { file = def &= help "Optional input file path to attempt decoding.\n \ \ Defaults to stdin." @@ -70,10 +70,10 @@ main = do (filePath:restFilePaths) -> do initialEnv <- evaluateFile filePath finalEnv <- foldM evaluateFileWithContext initialEnv restFilePaths - pure $ result finalEnv + pure $ mainResult finalEnv let fRes = formatResult form result putStr fRes - Decode { file = filePaths } -> do + TDecode { file = filePaths } -> do value <- case filePaths of [] -> getContents (filePath:_) -> readFile filePath diff --git a/src/REPL.hs b/src/REPL.hs index 407b423..b725209 100644 --- a/src/REPL.hs +++ b/src/REPL.hs @@ -59,10 +59,3 @@ repl env = runInputT defaultSettings (loop env) strip :: String -> String strip = dropWhileEnd isSpace . dropWhile isSpace - -decodeResult :: T -> String -decodeResult tc - | Right num <- toNumber tc = show num - | Right str <- toString tc = "\"" ++ str ++ "\"" - | Right list <- toList tc = "[" ++ intercalate ", " (map decodeResult list) ++ "]" - | otherwise = formatResult TreeCalculus tc diff --git a/src/Research.hs b/src/Research.hs index f93bc24..c29f494 100644 --- a/src/Research.hs +++ b/src/Research.hs @@ -45,7 +45,7 @@ data LToken deriving (Show, Eq, Ord) -- Output formats -data EvaluatedForm = TreeCalculus | FSL | AST | Ternary | Ascii +data EvaluatedForm = TreeCalculus | FSL | AST | Ternary | Ascii | Decode deriving (Show, Data, Typeable) -- Environment containing previously evaluated TC terms @@ -115,6 +115,7 @@ formatResult FSL = show formatResult AST = show . toAST formatResult Ternary = toTernaryString formatResult Ascii = toAscii +formatResult Decode = decodeResult toSimpleT :: String -> String toSimpleT s = T.unpack @@ -147,4 +148,9 @@ toAscii tree = go tree "" True ++ go left (prefix ++ (if isLast then " " else "| ")) False ++ go right (prefix ++ (if isLast then " " else "| ")) True --- Utility +decodeResult :: T -> String +decodeResult tc + | Right num <- toNumber tc = show num + | Right str <- toString tc = "\"" ++ str ++ "\"" + | Right list <- toList tc = "[" ++ intercalate ", " (map decodeResult list) ++ "]" + | otherwise = formatResult TreeCalculus tc diff --git a/test/Spec.hs b/test/Spec.hs index a44d320..eb04e6f 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -488,8 +488,9 @@ fileEval = testGroup "File evaluation tests" res @?= Fork (Stem Leaf) Leaf , testCase "Mapping and Equality" $ do - res <- liftIO $ evaluateFileResult "./test/map.tri" - res @?= Stem Leaf + library <- liftIO $ evaluateFile "./lib/base.tri" + fEnv <- liftIO $ evaluateFileWithContext library "./test/map.tri" + (mainResult fEnv) @?= Stem Leaf , testCase "Eval and decoding string" $ do library <- liftIO $ evaluateFile "./lib/base.tri" diff --git a/test/comments-1.tri b/test/comments-1.tri index ee8de08..82ef6bd 100644 --- a/test/comments-1.tri +++ b/test/comments-1.tri @@ -2,7 +2,7 @@ -- t (t t) (t (t t t)) -- t (t t t) (t t) -- x = (\a : a) -t (t t) t -- Fork (Stem Leaf) Leaf +main = t (t t) t -- Fork (Stem Leaf) Leaf -- t t -- x -- x = (\a : a) diff --git a/test/fork.tri b/test/fork.tri index 66c0658..5b72b14 100644 --- a/test/fork.tri +++ b/test/fork.tri @@ -1 +1 @@ -t t t +main = t t t diff --git a/test/map.tri b/test/map.tri index f8c8f7a..b534482 100644 --- a/test/map.tri +++ b/test/map.tri @@ -1,24 +1,2 @@ -false = t -true = t t -_ = t -k = t t -i = t (t k) t -s = t (t (k t)) t -m = s i i -b = s (k s) k -c = s (s (k s) (s (k k) s)) (k k) -iC = (\a b c : s a (k c) b) -yi = (\i : b m (c b (i m))) -y = yi iC -triage = (\a b c : t (t a b) c) -pair = t -matchList = (\oe oc : triage oe _ oc) -lconcat = y (\self : matchList (\k : k) (\h r k : pair h (self r k))) -hmap = y (\self : matchList (\f : t) (\hd tl f : pair (f hd) (self tl f))) -map = (\f l : hmap l f) -lAnd = triage (\x : false) (\_ x : x) (\_ _ x : x) -lOr = triage (\x : x) (\_ _ : true) (\_ _ x : true) -equal = y (\self : triage (triage true (\z : false) (\y z : false)) (\ax : triage false (self ax) (\y z : false)) (\ax ay : triage false (\z : false) (\bx by : lAnd (self ax bx) (self ay by)))) - x = map (\i : lconcat "Successfully concatenated " i) [("two strings!")] -equal x [("Successfully concatenated two strings!")] +main = equal? x [("Successfully concatenated two strings!")] diff --git a/tricu.cabal b/tricu.cabal index de8367e..878e117 100644 --- a/tricu.cabal +++ b/tricu.cabal @@ -1,7 +1,7 @@ cabal-version: 1.12 name: tricu -version: 0.10.0 +version: 0.11.0 description: A micro-language for exploring Tree Calculus author: James Eversole maintainer: james@eversole.co