Compare commits

..

No commits in common. "f97bd84050489c0da47876d1482c1e532d0365be" and "918d929c0995ba38e2f3564679f369a6276b9a66" have entirely different histories.

29 changed files with 131 additions and 403 deletions

View File

@ -62,4 +62,4 @@ jobs:
./tricu
token: '${{ secrets.RELEASE_TOKEN }}'
body: '${{ gitea.event.head_commit.message }}'
prerelease: true
pre_release: true

View File

@ -10,13 +10,12 @@ tricu is the word for "tree" in Lojban: `(x1) is a tree of species/cultivar (x2)
- Tree Calculus operator: `t`
- Assignments: `x = t t`
- Immutabile definitions
- Lambda abstraction syntax: `id = (\a : a)`
- List, Number, and String literals: `[(2) ("Hello")]`
- Function application: `not (not false)`
- Higher order/first-class functions: `map (\a : lconcat a "!") [("Hello")]`
- Intensionality blurs the distinction between functions and data (see REPL examples)
- Simple module system for code organization
- Immutability
## REPL examples

View File

@ -1,7 +1,3 @@
!module Equality
!import "lib/base.tri" Lib
main = lambdaEqualsTC
-- We represent `false` with a Leaf and `true` with a Stem Leaf
@ -28,7 +24,7 @@ not_Lambda? = demo_matchBool demo_false demo_true
-- to different tree representations even if they share extensional behavior.
-- Let's see if these are the same:
lambdaEqualsTC = Lib.equal? not_TC? not_Lambda?
lambdaEqualsTC = equal? not_TC? not_Lambda?
-- Here are some checks to verify their extensional behavior is the same:
true_TC? = not_TC? demo_false
@ -37,5 +33,5 @@ false_TC? = not_TC? demo_true
true_Lambda? = not_Lambda? demo_false
false_Lambda? = not_Lambda? demo_true
bothTrueEqual? = Lib.equal? true_TC? true_Lambda?
bothFalseEqual? = Lib.equal? false_TC? false_Lambda?
bothTrueEqual? = equal? true_TC? true_Lambda?
bothFalseEqual? = equal? false_TC? false_Lambda?

View File

@ -1,7 +1,3 @@
!module LOT
!import "lib/base.tri" Lib
main = exampleTwo
-- Level Order Traversal of a labelled binary tree
-- Objective: Print each "level" of the tree on a separate line
@ -19,41 +15,41 @@ main = exampleTwo
-- / / \
-- 4 5 6
label = \node : Lib.head node
label = \node : head node
left = (\node : Lib.if (Lib.emptyList? node)
left = (\node : if (emptyList? node)
[]
(Lib.if (Lib.emptyList? (Lib.tail node))
(if (emptyList? (tail node))
[]
(Lib.head (Lib.tail node))))
(head (tail node))))
right = (\node : Lib.if (Lib.emptyList? node)
right = (\node : if (emptyList? node)
[]
(Lib.if (Lib.emptyList? (Lib.tail node))
(if (emptyList? (tail node))
[]
(Lib.if (Lib.emptyList? (Lib.tail (Lib.tail node)))
(if (emptyList? (tail (tail node)))
[]
(Lib.head (Lib.tail (Lib.tail node))))))
(head (tail (tail node))))))
processLevel = Lib.y (\self queue : Lib.if (Lib.emptyList? queue)
processLevel = y (\self queue : if (emptyList? queue)
[]
(Lib.pair (Lib.map label queue) (self (Lib.filter
(\node : Lib.not? (Lib.emptyList? node))
(Lib.lconcat (Lib.map left queue) (Lib.map right queue))))))
(pair (map label queue) (self (filter
(\node : not? (emptyList? node))
(lconcat (map left queue) (map right queue))))))
levelOrderTraversal_ = \a : processLevel (t a t)
toLineString = Lib.y (\self levels : Lib.if (Lib.emptyList? levels)
toLineString = y (\self levels : if (emptyList? levels)
""
(Lib.lconcat
(Lib.lconcat (Lib.map (\x : Lib.lconcat x " ") (Lib.head levels)) "")
(Lib.if (Lib.emptyList? (Lib.tail levels)) "" (Lib.lconcat (t (t 10 t) t) (self (Lib.tail levels))))))
(lconcat
(lconcat (map (\x : lconcat x " ") (head levels)) "")
(if (emptyList? (tail levels)) "" (lconcat (t (t 10 t) t) (self (tail levels))))))
levelOrderToString = \s : toLineString (levelOrderTraversal_ s)
flatten = Lib.foldl (\acc x : Lib.lconcat acc x) ""
flatten = foldl (\acc x : lconcat acc x) ""
levelOrderTraversal = \s : Lib.lconcat (t 10 t) (flatten (levelOrderToString s))
levelOrderTraversal = \s : lconcat (t 10 t) (flatten (levelOrderToString s))
exampleOne = levelOrderTraversal [("1")
[("2") [("4") t t] t]

View File

@ -1,24 +1,20 @@
!module Size
!import "lib/base.tri" Lib
main = size size
compose = \f g x : f (g x)
succ = Lib.y (\self :
Lib.triage
succ = y (\self :
triage
1
t
(Lib.triage
(triage
(t (t t))
(\_ Lib.tail : t t (self Lib.tail))
(\_ tail : t t (self tail))
t))
size = (\x :
(Lib.y (\self x :
(y (\self x :
compose succ
(Lib.triage
(triage
(\x : x)
self
(\x y : compose (self x) (self y))

View File

@ -1,8 +1,4 @@
!module ToSource
!import "lib/base.tri" Lib
main = toSource Lib.not?
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.
@ -16,29 +12,29 @@ main = toSource Lib.not?
-- triage = (\leaf stem fork : t (t leaf stem) fork)
-- Base case of a single Leaf
sourceLeaf = t (Lib.head "t")
sourceLeaf = t (head "t")
-- Stem case
sourceStem = (\convert : (\a rest :
t (Lib.head "(") -- Start with a left parenthesis "(".
(t (Lib.head "t") -- Add a "t"
(t (Lib.head " ") -- Add a space.
(convert a -- Recursively convert the argument.
(t (Lib.head ")") rest)))))) -- Close with ")" and append the rest.
t (head "(") -- Start with a left parenthesis "(".
(t (head "t") -- Add a "t"
(t (head " ") -- Add a space.
(convert a -- Recursively convert the argument.
(t (head ")") rest)))))) -- Close with ")" and append the rest.
-- Fork case
sourceFork = (\convert : (\a b rest :
t (Lib.head "(") -- Start with a left parenthesis "(".
(t (Lib.head "t") -- Add a "t"
(t (Lib.head " ") -- Add a space.
(convert a -- Recursively convert the first arg.
(t (Lib.head " ") -- Add another space.
(convert b -- Recursively convert the second arg.
(t (Lib.head ")") rest)))))))) -- Close with ")" and append the rest.
t (head "(") -- Start with a left parenthesis "(".
(t (head "t") -- Add a "t"
(t (head " ") -- Add a space.
(convert a -- Recursively convert the first arg.
(t (head " ") -- Add another space.
(convert b -- Recursively convert the second arg.
(t (head ")") rest)))))))) -- Close with ")" and append the rest.
-- Wrapper around triage
toSource_ = Lib.y (\self arg :
Lib.triage
toSource_ = y (\self arg :
triage
sourceLeaf -- `triage` "a" case, Leaf
(sourceStem self) -- `triage` "b" case, Stem
(sourceFork self) -- `triage` "c" case, Fork
@ -47,5 +43,5 @@ toSource_ = Lib.y (\self arg :
-- toSource takes a single TC term and returns a String
toSource = \v : toSource_ v ""
exampleOne = toSource Lib.true -- OUT: "(t t)"
exampleTwo = toSource Lib.not? -- OUT: "(t (t (t t) (t t t)) (t t (t t t)))"
exampleOne = toSource true -- OUT: "(t t)"
exampleTwo = toSource not? -- OUT: "(t (t (t t) (t t t)) (t t (t t t)))"

View File

@ -3,7 +3,7 @@ module Eval where
import Parser
import Research
import Data.List (partition, (\\))
import Data.List (partition)
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.Set as Set
@ -17,19 +17,19 @@ evalSingle env term
"Error: Identifier '" ++ name ++ "' is already defined."
| otherwise ->
let res = evalAST env body
in Map.insert "!result" res (Map.insert name res env)
in Map.insert "__result" res (Map.insert name res env)
| SApp func arg <- term =
let res = apply (evalAST env func) (evalAST env arg)
in Map.insert "!result" res env
in Map.insert "__result" res env
| SVar name <- term =
case Map.lookup name env of
Just v ->
Map.insert "!result" v env
Map.insert "__result" v env
Nothing ->
errorWithoutStackTrace $ "Variable `" ++ name ++ "` not defined\n\
\This error should never occur here. Please report this as an issue."
| otherwise =
Map.insert "!result" (evalAST env term) env
Map.insert "__result" (evalAST env term) env
evalTricu :: Env -> [TricuAST] -> Env
evalTricu env x = go env (reorderDefs env x)
@ -37,7 +37,7 @@ evalTricu env x = go env (reorderDefs env x)
go env [] = env
go env [x] =
let updatedEnv = evalSingle env x
in Map.insert "!result" (result updatedEnv) updatedEnv
in Map.insert "__result" (result updatedEnv) updatedEnv
go env (x:xs) =
evalTricu (evalSingle env x) xs
@ -109,11 +109,10 @@ freeVars (SStr _ ) = Set.empty
freeVars (SList s ) = foldMap freeVars s
freeVars (SApp f a ) = freeVars f <> freeVars a
freeVars (TLeaf ) = Set.empty
freeVars (SDef _ _ b) = freeVars b
freeVars (SDef _ _ b) = freeVars b
freeVars (TStem t ) = freeVars t
freeVars (TFork l r ) = freeVars l <> freeVars r
freeVars (SLambda v b ) = foldr Set.delete (freeVars b) v
freeVars _ = Set.empty
reorderDefs :: Env -> [TricuAST] -> [TricuAST]
reorderDefs env defs
@ -123,20 +122,17 @@ reorderDefs env defs
| otherwise = orderedDefs ++ others
where
(defsOnly, others) = partition isDef defs
defNames = [ name | SDef name _ _ <- defsOnly ]
defsWithFreeVars = [(def, freeVars body) | def@(SDef _ _ body) <- defsOnly]
graph = buildDepGraph defsOnly
sortedDefs = sortDeps graph
defMap = Map.fromList [(name, def) | def@(SDef name _ _) <- defsOnly]
orderedDefs = map (\name -> defMap Map.! name) sortedDefs
freeVarsDefs = foldMap snd defsWithFreeVars
freeVarsOthers = foldMap freeVars others
allFreeVars = freeVarsDefs <> freeVarsOthers
validNames = Set.fromList defNames `Set.union` Set.fromList (Map.keys env)
missingDeps = Set.toList (allFreeVars `Set.difference` validNames)
graph = buildDepGraph defsOnly
sortedDefs = sortDeps graph
defMap = Map.fromList [(name, def) | def@(SDef name _ _) <- defsOnly]
orderedDefs = map (\name -> defMap Map.! name) sortedDefs
topDefNames = Set.fromList (Map.keys defMap)
envNames = Set.fromList (Map.keys env)
freeVarsDefs = foldMap (\(SDef _ _ body) -> freeVars body) defsOnly
freeVarsOthers = foldMap freeVars others
allFreeVars = freeVarsDefs <> freeVarsOthers
validNames = topDefNames `Set.union` envNames
missingDeps = Set.toList (allFreeVars `Set.difference` validNames)
isDef (SDef _ _ _) = True
isDef _ = False
@ -157,21 +153,20 @@ buildDepGraph topDefs
countOccurrences = foldr (\x -> Map.insertWith (+) x 1) Map.empty
sortDeps :: Map.Map String (Set.Set String) -> [String]
sortDeps graph = go [] Set.empty (Map.keys graph)
sortDeps graph = go [] (Map.keys graph)
where
go sorted sortedSet [] = sorted
go sorted sortedSet remaining =
let ready = [ name | name <- remaining
, let deps = Map.findWithDefault Set.empty name graph
, Set.isSubsetOf deps sortedSet ]
notReady = remaining \\ ready
in if null ready
then errorWithoutStackTrace
"ERROR: Cyclic dependency detected and prohibited.\n\
\RESOLVE: Use nested lambdas."
else go (sorted ++ ready)
(Set.union sortedSet (Set.fromList ready))
notReady
go sorted [] = sorted
go sorted remaining
| null ready =
errorWithoutStackTrace
"ERROR: Top-level cyclic dependency detected and prohibited\n\
\RESOLVE: Use nested lambdas"
| otherwise = go (sorted ++ ready) notReady
where
ready = [ name | name <- remaining
, all (`elem` sorted) (Set.toList (graph Map.! name))]
notReady =
[ name | name <- remaining , name `notElem` ready]
depends :: [TricuAST] -> TricuAST -> Set.Set String
depends topDefs (SDef _ _ body) =
@ -181,9 +176,9 @@ depends topDefs (SDef _ _ body) =
depends _ _ = Set.empty
result :: Env -> T
result r = case Map.lookup "!result" r of
result r = case Map.lookup "__result" r of
Just a -> a
Nothing -> errorWithoutStackTrace "No !result field found in provided env"
Nothing -> errorWithoutStackTrace "No __result field found in provided env"
mainResult :: Env -> T
mainResult r = case Map.lookup "main" r of

View File

@ -1,150 +1,30 @@
module FileEval where
import Eval
import Lexer
import Parser
import Research
import Data.List (partition)
import Control.Monad (foldM)
import System.IO
import qualified Data.Map as Map
import qualified Data.Set as Set
evaluateFileResult :: FilePath -> IO T
evaluateFileResult filePath = do
contents <- readFile filePath
let tokens = lexTricu contents
let moduleName = case parseProgram tokens of
Right ((SModule name) : _) -> name
_ -> ""
case parseProgram tokens of
Left err -> errorWithoutStackTrace (handleParseError err)
Right _ -> do
ast <- preprocessFile filePath
let finalEnv = mainAlias moduleName $ evalTricu Map.empty ast
case Map.lookup "main" finalEnv of
Just finalResult -> return finalResult
Nothing -> errorWithoutStackTrace "No `main` function detected"
let asts = parseTricu contents
let finalEnv = evalTricu Map.empty asts
case Map.lookup "main" finalEnv of
Just finalResult -> return finalResult
Nothing -> errorWithoutStackTrace "No `main` function detected"
evaluateFile :: FilePath -> IO Env
evaluateFile filePath = do
contents <- readFile filePath
let tokens = lexTricu contents
let moduleName = case parseProgram tokens of
Right ((SModule name) : _) -> name
_ -> ""
case parseProgram tokens of
Left err -> errorWithoutStackTrace (handleParseError err)
Right _ -> do
ast <- preprocessFile filePath
pure $ mainAlias moduleName $ evalTricu Map.empty ast
let asts = parseTricu contents
pure $ evalTricu Map.empty asts
evaluateFileWithContext :: Env -> FilePath -> IO Env
evaluateFileWithContext env filePath = do
contents <- readFile filePath
let tokens = lexTricu contents
let moduleName = case parseProgram tokens of
Right ((SModule name) : _) -> name
_ -> ""
case parseProgram tokens of
Left err -> errorWithoutStackTrace (handleParseError err)
Right _ -> do
ast <- preprocessFile filePath
pure $ mainAlias moduleName $ evalTricu env ast
mainAlias :: String -> Env -> Env
mainAlias "" env = env
mainAlias moduleName env =
case Map.lookup (moduleName ++ ".main") env of
Just value -> Map.insert "main" value env
Nothing -> env
preprocessFile :: FilePath -> IO [TricuAST]
preprocessFile filePath = preprocessFile' Set.empty filePath
preprocessFile' :: Set.Set FilePath -> FilePath -> IO [TricuAST]
preprocessFile' inProgress filePath
| filePath `Set.member` inProgress =
errorWithoutStackTrace $ "Encountered cyclic import: " ++ filePath
| otherwise = do
contents <- readFile filePath
let tokens = lexTricu contents
case parseProgram tokens of
Left err -> errorWithoutStackTrace (handleParseError err)
Right asts -> do
let (moduleName, restAST) = extractModule asts
let (imports, nonImports) = partition isImport restAST
let newInProgress = Set.insert filePath inProgress
importedASTs <- concat <$> mapM (processImport newInProgress) imports
let namespacedAST = namespaceDefinitions moduleName nonImports
pure $ importedASTs ++ namespacedAST
where
extractModule :: [TricuAST] -> (String, [TricuAST])
extractModule ((SModule name) : xs) = (name, xs)
extractModule xs = ("", xs)
isImport :: TricuAST -> Bool
isImport (SImport _ _) = True
isImport _ = False
processImport :: Set.Set FilePath -> TricuAST -> IO [TricuAST]
processImport inProgress (SImport filePath moduleName) = do
importedAST <- preprocessFile' inProgress filePath
pure $ namespaceDefinitions moduleName importedAST
processImport _ _ = error "Unexpected non-import in processImport"
namespaceDefinitions :: String -> [TricuAST] -> [TricuAST]
namespaceDefinitions moduleName = map (namespaceDefinition moduleName)
namespaceDefinition :: String -> TricuAST -> TricuAST
namespaceDefinition "" def = def
namespaceDefinition moduleName (SDef name args body)
| isPrefixed name = SDef name args (namespaceBody moduleName body)
| otherwise = SDef (namespaceVariable moduleName name)
args (namespaceBody moduleName body)
namespaceDefinition moduleName other =
namespaceBody moduleName other
namespaceBody :: String -> TricuAST -> TricuAST
namespaceBody moduleName (SVar name)
| isPrefixed name = SVar name
| otherwise = SVar (namespaceVariable moduleName name)
namespaceBody moduleName (SApp func arg) =
SApp (namespaceBody moduleName func) (namespaceBody moduleName arg)
namespaceBody moduleName (SLambda args body) =
SLambda args (namespaceBodyScoped moduleName args body)
namespaceBody moduleName (SList items) =
SList (map (namespaceBody moduleName) items)
namespaceBody moduleName (TFork left right) =
TFork (namespaceBody moduleName left) (namespaceBody moduleName right)
namespaceBody moduleName (TStem subtree) =
TStem (namespaceBody moduleName subtree)
namespaceBody moduleName (SDef name args body)
| isPrefixed name = SDef name args (namespaceBody moduleName body)
| otherwise = SDef (namespaceVariable moduleName name)
args (namespaceBody moduleName body)
namespaceBody _ other = other
namespaceBodyScoped :: String -> [String] -> TricuAST -> TricuAST
namespaceBodyScoped moduleName args body = case body of
SVar name ->
if name `elem` args
then SVar name
else namespaceBody moduleName (SVar name)
SApp func arg -> SApp (namespaceBodyScoped moduleName args func) (namespaceBodyScoped moduleName args arg)
SLambda innerArgs innerBody -> SLambda innerArgs (namespaceBodyScoped moduleName (args ++ innerArgs) innerBody)
SList items -> SList (map (namespaceBodyScoped moduleName args) items)
TFork left right -> TFork (namespaceBodyScoped moduleName args left) (namespaceBodyScoped moduleName args right)
TStem subtree -> TStem (namespaceBodyScoped moduleName args subtree)
SDef name innerArgs innerBody ->
SDef (namespaceVariable moduleName name) innerArgs (namespaceBodyScoped moduleName (args ++ innerArgs) innerBody)
other -> other
isPrefixed :: String -> Bool
isPrefixed name = '.' `elem` name
namespaceVariable :: String -> String -> String
namespaceVariable "" name = name
namespaceVariable moduleName name = moduleName ++ "." ++ name
let asts = parseTricu contents
pure $ evalTricu env asts

View File

@ -20,11 +20,11 @@ identifier = do
first <- letterChar <|> char '_'
rest <- many $ letterChar
<|> digitChar
<|> char '_' <|> char '-' <|> char '?' <|> char '.'
<|> char '_' <|> char '-' <|> char '?' <|> char '!'
<|> char '$' <|> char '#' <|> char '@' <|> char '%'
let name = first : rest
if (name == "t" || name == "!result")
then fail "Keywords (`t`, `!result`) cannot be used as an identifier"
if (name == "t" || name == "__result")
then fail "Keywords (`t`, `__result`) cannot be used as an identifier"
else return (LIdentifier name)
integerLiteral :: Lexer LToken
@ -39,22 +39,6 @@ stringLiteral = do
char '"' --"
return (LStringLiteral content)
lModule :: Lexer LToken
lModule = do
_ <- string "!module"
space1
LIdentifier moduleName <- identifier
return (LModule moduleName)
lImport :: Lexer LToken
lImport = do
_ <- string "!import"
space1
LStringLiteral path <- stringLiteral
space1
LIdentifier name <- identifier
return (LImport path name)
assign :: Lexer LToken
assign = char '=' *> pure LAssign
@ -88,36 +72,28 @@ sc = space
tricuLexer :: Lexer [LToken]
tricuLexer = do
sc
header <- many $ do
tok <- choice
[ try lModule
, try lImport
, lnewline
]
sc
pure tok
tokens <- many $ do
tok <- choice tricuLexer'
sc
pure tok
sc
eof
pure (header ++ tokens)
where
tricuLexer' =
[ try lnewline
, try identifier
, try keywordT
, try integerLiteral
, try stringLiteral
, assign
, colon
, backslash
, openParen
, closeParen
, openBracket
, closeBracket
]
pure tokens
where
tricuLexer' =
[ try lnewline
, try identifier
, try keywordT
, try integerLiteral
, try stringLiteral
, assign
, colon
, backslash
, openParen
, closeParen
, openBracket
, closeBracket
]
lexTricu :: String -> [LToken]
lexTricu input = case runParser tricuLexer "" input of

View File

@ -61,7 +61,7 @@ main = do
putStrLn "Welcome to the tricu REPL"
putStrLn "You can exit with `CTRL+D` or the `:_exit` command.`"
library <- liftIO $ evaluateFile "./lib/base.tri"
repl $ Map.delete "!result" library
repl $ Map.delete "__result" library
Evaluate { file = filePaths, form = form } -> do
result <- case filePaths of
[] -> do

View File

@ -74,33 +74,9 @@ parseSingle input =
parseProgramM :: ParserM [TricuAST]
parseProgramM = do
skipMany topLevelNewline
moduleNode <- optional parseModuleM
skipMany topLevelNewline
importNodes <- many (do
node <- parseImportM
skipMany topLevelNewline
return node)
skipMany topLevelNewline
exprs <- sepEndBy parseOneExpression (some topLevelNewline)
skipMany topLevelNewline
return (maybe [] (: []) moduleNode ++ importNodes ++ exprs)
parseModuleM :: ParserM TricuAST
parseModuleM = do
LModule moduleName <- satisfyM isModule
pure (SModule moduleName)
where
isModule (LModule _) = True
isModule _ = False
parseImportM :: ParserM TricuAST
parseImportM = do
LImport filePath moduleName <- satisfyM isImport
pure (SImport filePath moduleName)
where
isImport (LImport _ _) = True
isImport _ = False
return exprs
parseOneExpression :: ParserM TricuAST
parseOneExpression = scnParserM *> parseExpressionM
@ -268,7 +244,7 @@ parseVarM :: ParserM TricuAST
parseVarM = do
satisfyM (\case LIdentifier _ -> True; _ -> False) >>= \case
LIdentifier name
| name == "t" || name == "!result" ->
| name == "t" || name == "__result" ->
fail ("Reserved keyword: " ++ name ++ " cannot be assigned.")
| otherwise ->
pure (SVar name)

View File

@ -26,7 +26,7 @@ repl env = runInputT defaultSettings (loop env)
| Just s <- minput, strip s == "" -> do
outputStrLn ""
loop env
| Just s <- minput, strip s == "!import" -> do
| Just s <- minput, strip s == "!load" -> do
path <- getInputLine "File path to load < "
if
| Nothing <- path -> do
@ -34,7 +34,7 @@ repl env = runInputT defaultSettings (loop env)
loop env
| Just p <- path -> do
loadedEnv <- liftIO $ evaluateFileWithContext env (strip p) `catch` \e -> errorHandler env e
loop $ Map.delete "!result" (Map.union loadedEnv env)
loop $ Map.delete "__result" (Map.union loadedEnv env)
| Just s <- minput -> do
if
| take 2 s == "--" -> loop env
@ -47,7 +47,7 @@ repl env = runInputT defaultSettings (loop env)
let asts = parseTricu input
newEnv = evalTricu env asts
if
| Just r <- Map.lookup "!result" newEnv -> do
| Just r <- Map.lookup "__result" newEnv -> do
putStrLn $ "tricu > " ++ decodeResult r
| otherwise -> return ()
return newEnv

View File

@ -26,8 +26,6 @@ data TricuAST
| TFork TricuAST TricuAST
| SLambda [String] TricuAST
| SEmpty
| SModule String
| SImport String String
deriving (Show, Eq, Ord)
-- Lexer Tokens
@ -44,8 +42,6 @@ data LToken
| LOpenBracket
| LCloseBracket
| LNewline
| LModule String
| LImport String String
deriving (Show, Eq, Ord)
-- Output formats

View File

@ -7,13 +7,12 @@ import Parser
import REPL
import Research
import Control.Exception (evaluate, try, SomeException)
import Control.Exception (evaluate, try, SomeException)
import Control.Monad.IO.Class (liftIO)
import Data.List (isInfixOf)
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Text.Megaparsec (runParser)
import Text.Megaparsec (runParser)
import qualified Data.Map as Map
import qualified Data.Set as Set
@ -32,7 +31,6 @@ tests = testGroup "Tricu Tests"
, lambdas
, baseLibrary
, fileEval
, modules
, demos
]
@ -72,9 +70,9 @@ lexer = testGroup "Lexer Tests"
Right i -> i @?= expect
, testCase "Error when using invalid characters in identifiers" $ do
case (runParser tricuLexer "" "!result = 5") of
case (runParser tricuLexer "" "__result = 5") of
Left _ -> return ()
Right _ -> assertFailure "Expected failure when trying to assign the value of !result"
Right _ -> assertFailure "Expected failure when trying to assign the value of __result"
]
parser :: TestTree
@ -500,54 +498,22 @@ fileEval = testGroup "File evaluation tests"
decodeResult (result res) @?= "\"String test!\""
]
modules :: TestTree
modules = testGroup "Test modules"
[ testCase "Detect cyclic dependencies" $ do
result <- try (liftIO $ evaluateFileResult "./test/cycle-1.tri") :: IO (Either SomeException T)
case result of
Left e -> do
let errorMsg = show e
if "Encountered cyclic import" `isInfixOf` errorMsg
then return ()
else assertFailure $ "Unexpected error: " ++ errorMsg
Right _ -> assertFailure "Expected cyclic dependencies"
, testCase "Module imports and namespacing" $ do
res <- liftIO $ evaluateFileResult "./test/namespace-A.tri"
res @?= Leaf
, testCase "Multiple imports" $ do
res <- liftIO $ evaluateFileResult "./test/vars-A.tri"
res @?= Leaf
, testCase "Error on unresolved variable" $ do
result <- try (liftIO $ evaluateFileResult "./test/unresolved-A.tri") :: IO (Either SomeException T)
case result of
Left e -> do
let errorMsg = show e
if "undefinedVar" `isInfixOf` errorMsg
then return ()
else assertFailure $ "Unexpected error: " ++ errorMsg
Right _ -> assertFailure "Expected unresolved variable error"
, testCase "Multi-level imports" $ do
res <- liftIO $ evaluateFileResult "./test/multi-level-A.tri"
res @?= Leaf
, testCase "Lambda expression namespaces" $ do
res <- liftIO $ evaluateFileResult "./test/lambda-A.tri"
res @?= Leaf
]
-- All of our demo tests are also module tests
demos :: TestTree
demos = testGroup "Test provided demo functionality"
[ testCase "Structural equality demo" $ do
res <- liftIO $ evaluateFileResult "./demos/equality.tri"
decodeResult res @?= "t t"
library <- liftIO $ evaluateFile "./lib/base.tri"
res <- liftIO $ evaluateFileWithContext library "./demos/equality.tri"
decodeResult (result res) @?= "t t"
, testCase "Convert values back to source code demo" $ do
res <- liftIO $ evaluateFileResult "./demos/toSource.tri"
decodeResult res @?= "\"(t (t (t t) (t t t)) (t t (t t t)))\""
library <- liftIO $ evaluateFile "./lib/base.tri"
res <- liftIO $ evaluateFileWithContext library "./demos/toSource.tri"
decodeResult (result res) @?= "\"(t (t (t t) (t t t)) (t t (t t t)))\""
, testCase "Determining the size of functions" $ do
res <- liftIO $ evaluateFileResult "./demos/size.tri"
decodeResult res @?= "454"
library <- liftIO $ evaluateFile "./lib/base.tri"
res <- liftIO $ evaluateFileWithContext library "./demos/size.tri"
decodeResult (result res) @?= "454"
, testCase "Level Order Traversal demo" $ do
res <- liftIO $ evaluateFileResult "./demos/levelOrderTraversal.tri"
decodeResult res @?= "\"\n1 \n2 3 \n4 5 6 7 \n8 11 10 9 12 \""
library <- liftIO $ evaluateFile "./lib/base.tri"
res <- liftIO $ evaluateFileWithContext library "./demos/levelOrderTraversal.tri"
decodeResult (result res) @?= "\"\n1 \n2 3 \n4 5 6 7 \n8 11 10 9 12 \""
]

View File

@ -1,5 +0,0 @@
!module Cycle
!import "test/cycle-2.tri" Cycle2
cycle1 = t Cycle2.cycle2

View File

@ -1,5 +0,0 @@
!module Cycle2
!import "test/cycle-1.tri" Cycle1
cycle2 = t Cycle1.cycle1

View File

@ -1,2 +0,0 @@
!module A
main = (\x : x) t

View File

@ -1,5 +0,0 @@
!module Test
!import "lib/base.tri" Lib
main = Lib.not? t

View File

@ -1 +0,0 @@
n = t t t

View File

@ -1,3 +0,0 @@
!module A
!import "./test/multi-level-B.tri" B
main = B.main

View File

@ -1,3 +0,0 @@
!module B
!import "./test/multi-level-C.tri" C
main = C.val

View File

@ -1,2 +0,0 @@
!module C
val = t

View File

@ -1,3 +0,0 @@
!module A
!import "./test/namespace-B.tri" B
main = B.x

View File

@ -1,2 +0,0 @@
!module B
x = t

View File

@ -1,2 +0,0 @@
!module A
main = undefinedVar

View File

@ -1,7 +0,0 @@
!module A
!import "./test/vars-B.tri" B
!import "./test/vars-C.tri" C
main = B.y (C.z)

View File

@ -1,2 +0,0 @@
!module B
y = \x : x

View File

@ -1,2 +0,0 @@
!module C
z = t

View File

@ -1,7 +1,7 @@
cabal-version: 1.12
name: tricu
version: 0.12.0
version: 0.11.0
description: A micro-language for exploring Tree Calculus
author: James Eversole
maintainer: james@eversole.co