# 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.
This commit is contained in:
parent
425489a73f
commit
c9d0d04630
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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."
|
||||
|
@ -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
|
||||
|
12
src/Main.hs
12
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -1 +1 @@
|
||||
t t t
|
||||
main = t t t
|
||||
|
24
test/map.tri
24
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!")]
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user