Adds basic tests for modules

Basic tests for modules. Fixes for parsing multiple import statements.
This commit is contained in:
James Eversole 2025-01-27 16:03:03 -06:00
parent ae971ec968
commit f97bd84050
13 changed files with 75 additions and 4 deletions

View File

@ -10,12 +10,13 @@ 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)
- Immutability
- Simple module system for code organization
## REPL examples

View File

@ -76,12 +76,16 @@ parseProgramM = do
skipMany topLevelNewline
moduleNode <- optional parseModuleM
skipMany topLevelNewline
importNodes <- many parseImportM
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

View File

@ -7,12 +7,13 @@ 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
@ -31,6 +32,7 @@ tests = testGroup "Tricu Tests"
, lambdas
, baseLibrary
, fileEval
, modules
, demos
]
@ -498,6 +500,42 @@ 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

2
test/lambda-A.tri Normal file
View File

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

3
test/multi-level-A.tri Normal file
View File

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

3
test/multi-level-B.tri Normal file
View File

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

2
test/multi-level-C.tri Normal file
View File

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

3
test/namespace-A.tri Normal file
View File

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

2
test/namespace-B.tri Normal file
View File

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

2
test/unresolved-A.tri Normal file
View File

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

7
test/vars-A.tri Normal file
View File

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

2
test/vars-B.tri Normal file
View File

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

2
test/vars-C.tri Normal file
View File

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