Skip to content

Commit 6f380e8

Browse files
authored
Merge pull request #134 from hhefesto/modules-2
Modules
2 parents a727a03 + f9a39fd commit 6f380e8

13 files changed

Lines changed: 416 additions & 164 deletions

File tree

app/Evaluare.hs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import qualified System.IO.Strict as Strict
2020
import qualified Telomare as Tel
2121
import Telomare (IExpr (..), IExprF (..))
2222
import qualified Telomare.Eval as TE
23-
import Telomare.Parser (AnnotatedUPT, parsePrelude)
23+
import Telomare.Parser (AnnotatedUPT, parseModule)
2424
import Text.Read (readMaybe)
2525

2626
type VtyExample t m =
@@ -64,7 +64,7 @@ node n0 = do
6464
tile ( fixed . pure . (+1) . T.length . _node_label $ n0) $ do
6565
grout flex . text . pure . _node_label $ n0
6666
pure ()
67-
value :: Dynamic t Bool <- tile (fixed 4) $ checkbox def $ _node_selected n0
67+
value :: Dynamic t Bool <- tile (fixed 4) . checkbox def $ _node_selected n0
6868
pure $ NodeOutput
6969
{ _nodeOutput_node = Node (_node_label n0) (_node_eval n0) <$> value
7070
, _nodeOutput_expand = updated value
@@ -148,26 +148,29 @@ nodify = removeExtraNumbers . fmap go . allNodes 0 where
148148
-> Cofree IExprF (Int, IExpr)
149149
-> [(Int, Cofree IExprF (Int, IExpr))]
150150
allNodes i = \case
151-
x@(_ :< ZeroF) -> (i, x) : []
152-
x@(_ :< EnvF) -> (i, x) : []
153-
x@(_ :< TraceF) -> (i, x) : []
151+
x@(_ :< ZeroF) -> [(i, x)]
152+
x@(_ :< EnvF) -> [(i, x)]
153+
x@(_ :< TraceF) -> [(i, x)]
154154
x@(_ :< (SetEnvF a)) -> (i, x) : allNodes (i + 1) a
155155
x@(_ :< (DeferF a)) -> (i, x) : allNodes (i + 1) a
156156
x@(_ :< (PLeftF a)) -> (i, x) : allNodes (i + 1) a
157157
x@(_ :< (PRightF a)) -> (i, x) : allNodes (i + 1) a
158158
x@(_ :< (PairF a b)) -> (i, x) : allNodes (i + 1) a <> allNodes (i + 1) b
159159
x@(_ :< (GateF a b)) -> (i, x) : allNodes (i + 1) a <> allNodes (i + 1) b
160160

161-
loadFiles :: [String] -> IO [(String, AnnotatedUPT)]
162-
loadFiles filenames = do
163-
filesStrings <- mapM Strict.readFile filenames
164-
case parsePrelude . unlines $ filesStrings of
165-
Right p -> pure p
161+
162+
-- parseModule :: String -> Either String [Either AnnotatedUPT (String, AnnotatedUPT)]
163+
-- TODO: Load modules qualifed
164+
loadModules :: [String] -> IO [(String, [Either AnnotatedUPT (String, AnnotatedUPT)])]
165+
loadModules filenames = do
166+
filesStrings :: [String] <- mapM Strict.readFile filenames
167+
case sequence $ parseModule <$> filesStrings of
168+
Right p -> pure $ zip filesStrings p
166169
Left pe -> error pe
167170

168171
main :: IO ()
169172
main = do
170-
prelude :: [(String, AnnotatedUPT)] <- getArgs >>= loadFiles
173+
modules :: [(String, [Either AnnotatedUPT (String, AnnotatedUPT)])] <- getArgs >>= loadModules
171174
let go :: Text -> IO ()
172175
go textErr =
173176
mainWidget $ initManager_ $ do
@@ -191,7 +194,7 @@ main = do
191194
rec
192195
eEitherIExpr :: Event t (Either String IExpr) <- grout flex $ col $ do
193196
telomareTextInput :: TextInput t <- grout flex textBox
194-
pure . updated $ TE.eval2IExpr prelude . T.unpack <$> _textInput_value telomareTextInput
197+
pure . updated $ TE.eval2IExpr modules . T.unpack <$> _textInput_value telomareTextInput
195198
grout (fixed 2) . col . text $ ""
196199
let -- telomareNodes :: Event t (Either String [Node])
197200
telomareNodes = fmap (nodify . TE.tagIExprWithEval) <$> eEitherIExpr

app/Main.hs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
1+
{-# LANGUAGE ScopedTypeVariables #-}
2+
13
module Main where
24

35
import qualified Options.Applicative as O
6+
import System.Directory (listDirectory)
7+
import System.FilePath (takeBaseName, takeExtension)
48
import qualified System.IO.Strict as Strict
59
import Telomare.Eval (runMain)
610

7-
data TelomareOpts = TelomareOpts
8-
{ telomareFile :: String
9-
, preludeFile :: String
10-
} deriving Show
11+
newtype TelomareOpts
12+
= TelomareOpts {telomareFile :: String}
13+
deriving Show
1114

1215
telomareOpts :: O.Parser TelomareOpts
1316
telomareOpts = TelomareOpts
1417
<$> O.argument O.str (O.metavar "TELOMARE-FILE")
15-
<*> O.strOption
16-
( O.long "prelude"
17-
<> O.metavar "PRELUDE-FILE"
18-
<> O.showDefault
19-
<> O.value "./Prelude.tel"
20-
<> O.short 'p'
21-
<> O.help "Telomare prelude file" )
18+
19+
getAllModules :: IO [(String, String)]
20+
getAllModules = do
21+
allEntries <- listDirectory "."
22+
let telFiles = filter (\f -> takeExtension f == ".tel") allEntries
23+
readTelFile :: FilePath -> IO (String, String)
24+
readTelFile file = do
25+
content <- readFile file
26+
return (takeBaseName file, content)
27+
mapM readTelFile telFiles
2228

2329
main :: IO ()
2430
main = do
2531
let opts = O.info (telomareOpts O.<**> O.helper)
2632
( O.fullDesc
2733
<> O.progDesc "A simple but robust virtual machine" )
2834
topts <- O.execParser opts
29-
preludeString <- Strict.readFile $ preludeFile topts
30-
Strict.readFile (telomareFile topts) >>= runMain preludeString
35+
allModules :: [(String, String)] <- getAllModules
36+
runMain allModules . takeBaseName . telomareFile $ topts

src/Telomare.hs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import qualified Control.Monad.State as State
2626
import Data.Bool (bool)
2727
import Data.Char (chr, ord)
2828
import Data.Eq.Deriving (deriveEq1)
29-
import Data.Functor.Classes (Eq1 (..), Eq2 (..), Show1 (..), Show2 (..))
29+
import Data.Functor.Classes (Eq1 (..), Eq2 (..), Show1 (..), Show2 (..), eq1)
3030
import Data.Functor.Foldable (Base, Corecursive (embed),
3131
Recursive (cata, project))
3232
import Data.Functor.Foldable.TH (MakeBaseFunctor (makeBaseFunctor))
@@ -973,10 +973,58 @@ data UnprocessedParsedTerm
973973
| CheckUP UnprocessedParsedTerm UnprocessedParsedTerm
974974
| HashUP UnprocessedParsedTerm -- ^ On ad hoc user defined types, this term will be substitued to a unique Int.
975975
| CaseUP UnprocessedParsedTerm [(Pattern, UnprocessedParsedTerm)]
976+
-- TODO: check if adding this doesn't create partial functions
977+
| ImportQualifiedUP String String
978+
| ImportUP String
976979
deriving (Eq, Ord, Show)
977980
makeBaseFunctor ''UnprocessedParsedTerm -- Functorial version UnprocessedParsedTerm
978981
makePrisms ''UnprocessedParsedTerm
979982

983+
instance Eq a => Eq (UnprocessedParsedTermF a) where
984+
(==) = eq1
985+
986+
instance Eq1 UnprocessedParsedTermF where
987+
liftEq eq (VarUPF s1) (VarUPF s2) = s1 == s2
988+
liftEq eq (ITEUPF c1 t1 e1) (ITEUPF c2 t2 e2) =
989+
eq c1 c2 && eq t1 t2 && eq e1 e2
990+
liftEq eq (LetUPF binds1 body1) (LetUPF binds2 body2) =
991+
liftEq (\(s1, t1) (s2, t2) -> s1 == s2 && eq t1 t2) binds1 binds2 && eq body1 body2
992+
liftEq eq (ListUPF items1) (ListUPF items2) =
993+
liftEq eq items1 items2
994+
liftEq eq (IntUPF n1) (IntUPF n2) =
995+
n1 == n2
996+
liftEq eq (StringUPF s1) (StringUPF s2) =
997+
s1 == s2
998+
liftEq eq (PairUPF a1 b1) (PairUPF a2 b2) =
999+
eq a1 a2 && eq b1 b2
1000+
liftEq eq (AppUPF f1 x1) (AppUPF f2 x2) =
1001+
eq f1 f2 && eq x1 x2
1002+
liftEq eq (LamUPF var1 body1) (LamUPF var2 body2) =
1003+
var1 == var2 && eq body1 body2
1004+
liftEq eq (ChurchUPF n1) (ChurchUPF n2) =
1005+
n1 == n2
1006+
liftEq eq (UnsizedRecursionUPF a1 b1 c1) (UnsizedRecursionUPF a2 b2 c2) =
1007+
eq a1 a2 && eq b1 b2 && eq c1 c2
1008+
liftEq eq (LeftUPF x1) (LeftUPF x2) =
1009+
eq x1 x2
1010+
liftEq eq (RightUPF x1) (RightUPF x2) =
1011+
eq x1 x2
1012+
liftEq eq (TraceUPF x1) (TraceUPF x2) =
1013+
eq x1 x2
1014+
liftEq eq (CheckUPF a1 b1) (CheckUPF a2 b2) =
1015+
eq a1 a2 && eq b1 b2
1016+
liftEq eq (HashUPF x1) (HashUPF x2) =
1017+
eq x1 x2
1018+
liftEq eq (CaseUPF scrutinee1 patterns1) (CaseUPF scrutinee2 patterns2) =
1019+
eq scrutinee1 scrutinee2 &&
1020+
liftEq (\(pat1, expr1) (pat2, expr2) -> pat1 == pat2 && eq expr1 expr2) patterns1 patterns2
1021+
liftEq eq (ImportQualifiedUPF mod1 alias1) (ImportQualifiedUPF mod2 alias2) =
1022+
mod1 == mod2 && alias1 == alias2
1023+
liftEq eq (ImportUPF mod1) (ImportUPF mod2) =
1024+
mod1 == mod2
1025+
liftEq _ _ _ = False
1026+
1027+
9801028
instance (Show a) => Show (UnprocessedParsedTermF a) where
9811029
show (VarUPF s) = "VarUPF " <> show s
9821030
show (ITEUPF c t e) = "ITEUPF " <> show c <> " " <> show t <> " " <> show e
@@ -998,6 +1046,8 @@ instance (Show a) => Show (UnprocessedParsedTermF a) where
9981046

9991047
instance Show1 UnprocessedParsedTermF where
10001048
liftShowsPrec showsPrecFunc showList d term = case term of
1049+
ImportQualifiedUPF s1 s2 -> showString "ImportQualifedUPF " . shows s1 . showString " " . shows s2
1050+
ImportUPF s -> showString "ImportUPF " . shows s
10011051
VarUPF s -> showString "VarUPF " . shows s
10021052
ITEUPF c t e -> showString "ITEUPF " . showsPrecFunc 11 c . showChar ' '
10031053
. showsPrecFunc 11 t . showChar ' ' . showsPrecFunc 11 e

src/Telomare/Eval.hs

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import System.Process (CreateProcess (std_out), StdStream (CreatePipe),
2222
createProcess, shell)
2323
import Telomare
2424
import Telomare.Optimizer (optimize)
25-
import Telomare.Parser (AnnotatedUPT, parseOneExprOrTopLevelDefs, parsePrelude)
25+
import Telomare.Parser (AnnotatedUPT, parseModule, parseOneExprOrTopLevelDefs)
2626
import Telomare.Possible (AbortExpr, abortExprToTerm4, evalA, sizeTerm,
2727
term3ToUnsizedExpr)
28-
import Telomare.Resolver (parseMain, process)
28+
import Telomare.Resolver (main2Term3, process, resolveAllImports)
2929
import Telomare.RunTime (pureEval, rEval)
3030
import Telomare.TypeChecker (TypeCheckError (..), typeCheck)
3131
import Text.Megaparsec (errorBundlePretty, runParser)
@@ -207,24 +207,45 @@ funWrap eval fun inp =
207207
Pair disp newState -> (g2s disp, Just newState)
208208
z -> ("runtime error, dumped:\n" <> show z, Nothing)
209209

210-
runMainCore :: String -> String -> (IExpr -> IO a) -> IO a
211-
runMainCore preludeString s e =
212-
let prelude :: [(String, AnnotatedUPT)]
213-
prelude =
214-
case parsePrelude preludeString of
215-
Right p -> p
216-
Left pe -> error pe
210+
runMainCore :: [(String, String)] -> String -> (IExpr -> IO a) -> IO a
211+
runMainCore modulesStrings s e =
212+
let parsedModules :: [(String, Either String [Either AnnotatedUPT (String, AnnotatedUPT)])]
213+
parsedModules = (fmap . fmap) parseModule modulesStrings
214+
parsedModulesErrors :: [(String, Either String [Either AnnotatedUPT (String, AnnotatedUPT)])]
215+
parsedModulesErrors = filter (\(moduleStr, parsed) -> case parsed of
216+
Left _ -> True
217+
Right _ -> False)
218+
parsedModules
219+
flattenLeft = \case
220+
Left a -> a
221+
Right a -> error "flattenLeft error: got a Right when it should be Left"
222+
flattenRight = \case
223+
Right a -> a
224+
Left a -> error "flattenRight error: got a Left when it should be Right"
225+
modules :: [(String, [Either AnnotatedUPT (String, AnnotatedUPT)])]
226+
modules =
227+
case parsedModulesErrors of
228+
[] -> (fmap . fmap) flattenRight parsedModules
229+
errors -> let moduleWithError :: [(String, String)]
230+
moduleWithError = (fmap . fmap) flattenLeft parsedModulesErrors
231+
joinModuleError :: (String, String) -> String
232+
joinModuleError (moduleName, errorStr) = "Error in module " <> moduleName <> ":\n" <> errorStr
233+
in error . unlines $ joinModuleError <$> moduleWithError
234+
217235
in
218-
case compileMain <$> parseMain prelude s of
236+
case compileMain <$> main2Term3 modules s of
219237
Left e -> error $ concat ["failed to parse ", s, " ", e]
220238
Right (Right g) -> e g
221239
Right z -> error $ "compilation failed somehow, with result " <> show z
222240

223-
runMain_ :: String -> String -> IO String
224-
runMain_ preludeString s = runMainCore preludeString s evalLoop_
241+
runMain_ :: [(String, String)] -> String -> IO String
242+
runMain_ modulesStrings s = runMainCore modulesStrings s evalLoop_
243+
244+
runMain :: [(String, String)] -> String -> IO ()
245+
runMain modulesStrings s = runMainCore modulesStrings s evalLoop
225246

226-
runMain :: String -> String -> IO ()
227-
runMain preludeString s = runMainCore preludeString s evalLoop
247+
runMainWithInput :: [String] -> [(String, String)] -> String -> IO String
248+
runMainWithInput inputList modulesStrings s = runMainCore modulesStrings s (evalLoopWithInput inputList)
228249

229250
schemeEval :: IExpr -> IO ()
230251
schemeEval iexpr = do
@@ -270,19 +291,6 @@ evalLoopWithInput inputList iexpr = evalLoopCore iexpr printAcc "" inputList
270291
then pure out
271292
else pure (acc <> "\n" <> out)
272293

273-
runMainWithInput :: [String] -> String -> String -> IO String
274-
runMainWithInput inputList preludeString s =
275-
let prelude :: [(String, AnnotatedUPT)]
276-
prelude =
277-
case parsePrelude preludeString of
278-
Right p -> p
279-
Left pe -> error pe
280-
in
281-
case compileMain <$> parseMain prelude s of
282-
Left e -> pure $ concat ["failed to parse ", s, " ", e]
283-
Right (Right g) -> evalLoopWithInput inputList g
284-
Right z -> pure $ "compilation failed somehow, with result " <> show z
285-
286294
-- |Same as `evalLoop`, but keeping what was displayed.
287295
evalLoop_ :: IExpr -> IO String
288296
evalLoop_ iexpr = evalLoopCore iexpr printAcc "" []
@@ -302,10 +310,14 @@ calculateRecursionLimits t3 =
302310
Left a -> Left . StaticCheckError . convertAbortMessage $ a
303311
Right t -> pure t
304312

305-
eval2IExpr :: [(String, AnnotatedUPT)] -> String -> Either String IExpr
306-
eval2IExpr prelude str = bimap errorBundlePretty (\x -> DummyLoc :< LetUPF prelude x) (runParser (parseOneExprOrTopLevelDefs prelude) "" str)
307-
>>= process
308-
>>= first show . compileUnitTest
313+
eval2IExpr :: [(String, [Either AnnotatedUPT (String, AnnotatedUPT)])] -> String -> Either String IExpr
314+
eval2IExpr extraModuleBindings str =
315+
first errorBundlePretty (runParser (parseOneExprOrTopLevelDefs resolved) "" str)
316+
>>= process
317+
>>= first show . compileUnitTest
318+
where
319+
aux = (\str -> Left (DummyLoc :< ImportQualifiedUPF str str)) . fst <$> extraModuleBindings
320+
resolved = resolveAllImports extraModuleBindings aux
309321

310322
tagIExprWithEval :: IExpr -> Cofree IExprF (Int, IExpr)
311323
tagIExprWithEval iexpr = evalState (para alg iexpr) 0 where

src/Telomare/Parser.hs

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ reserved w = (lexeme . try) (string w *> notFollowedBy alphaNumChar)
9898

9999
-- |List of reserved words
100100
rws :: [String]
101-
rws = ["let", "in", "if", "then", "else", "case", "of" ]
101+
rws = ["let", "in", "if", "then", "else", "case", "of", "import"]
102102

103103
-- |Variable identifiers can consist of alphanumeric characters, underscore,
104104
-- and must start with an English alphabet letter
105105
identifier :: TelomareParser String
106106
identifier = lexeme . try $ p >>= check
107107
where
108-
p = (:) <$> letterChar <*> many (alphaNumChar <|> char '_' <?> "variable")
108+
p = (:) <$> letterChar <*> many (alphaNumChar <|> char '_' <|> char '.' <?> "variable")
109109
check x = if x `elem` rws
110110
then fail ("keyword " <> (show x <> " cannot be an identifier"))
111111
else pure x
@@ -324,12 +324,29 @@ parseAssignment = do
324324

325325
-- |Parse top level expressions.
326326
parseTopLevel :: TelomareParser AnnotatedUPT
327-
parseTopLevel = parseTopLevelWithPrelude []
327+
parseTopLevel = parseTopLevelWithExtraModuleBindings []
328+
329+
parseImport :: TelomareParser AnnotatedUPT
330+
parseImport = do
331+
x <- getLineColumn
332+
reserved "import" <* scn
333+
var <- identifier <* scn
334+
pure $ x :< ImportUPF var
335+
336+
parseImportQualified :: TelomareParser AnnotatedUPT
337+
parseImportQualified = do
338+
x <- getLineColumn
339+
reserved "import" <* scn
340+
reserved "qualified" <* scn
341+
m <- identifier <* scn
342+
reserved "as" <* scn
343+
qualifier <- identifier <* scn
344+
pure $ x :< ImportQualifiedUPF qualifier m
328345

329346
-- |Parse top level expressions.
330-
parseTopLevelWithPrelude :: [(String, AnnotatedUPT)] -- ^Prelude
331-
-> TelomareParser AnnotatedUPT
332-
parseTopLevelWithPrelude lst = do
347+
parseTopLevelWithExtraModuleBindings :: [(String, AnnotatedUPT)]
348+
-> TelomareParser AnnotatedUPT
349+
parseTopLevelWithExtraModuleBindings lst = do
333350
x <- getLineColumn
334351
bindingList <- scn *> many parseAssignment <* eof
335352
pure $ x :< LetUPF (lst <> bindingList) (fromJust $ lookup "main" bindingList)
@@ -372,15 +389,31 @@ parsePrelude :: String -> Either String [(String, AnnotatedUPT)]
372389
parsePrelude str = let result = runParser (scn *> many parseAssignment <* eof) "" str
373390
in first errorBundlePretty result
374391

375-
-- |Parse either a single expression or top level definitions defaulting to the `main` definition.
376-
-- This function is useful and was made for telomare-evaluare
377-
parseOneExprOrTopLevelDefs :: [(String, AnnotatedUPT)] -> TelomareParser AnnotatedUPT
378-
parseOneExprOrTopLevelDefs prelude = choice $ try <$> [ parseTopLevelWithPrelude prelude
379-
, parseLongExpr
380-
]
392+
parseImportOrAssignment :: TelomareParser (Either AnnotatedUPT (String, AnnotatedUPT))
393+
parseImportOrAssignment = do
394+
x <- getLineColumn
395+
maybeImport <- optional $ scn *> (try parseImportQualified <|> try parseImport) <* scn
396+
case maybeImport of
397+
Nothing -> do
398+
maybeAssignment <- optional $ scn *> try parseAssignment <* scn
399+
case maybeAssignment of
400+
Nothing -> fail "Expected either an import statement or an assignment"
401+
Just a -> pure $ Right a
402+
Just imp -> pure $ Left imp
381403

382-
-- |Parse with specified prelude
383404
parseWithPrelude :: [(String, AnnotatedUPT)] -- ^Prelude
384405
-> String -- ^Raw string to be parsed
385406
-> Either String AnnotatedUPT -- ^Error on Left
386-
parseWithPrelude prelude str = first errorBundlePretty $ runParser (parseTopLevelWithPrelude prelude) "" str
407+
parseWithPrelude prelude str = first errorBundlePretty $ runParser (parseTopLevelWithExtraModuleBindings prelude) "" str
408+
409+
parseModule :: String -> Either String [Either AnnotatedUPT (String, AnnotatedUPT)]
410+
parseModule str = let result = runParser (scn *> many parseImportOrAssignment <* eof) "" str
411+
in first errorBundlePretty result
412+
413+
-- |Parse either a single expression or top level definitions defaulting to the `main` definition.
414+
-- This function was made for telomare-evaluare
415+
parseOneExprOrTopLevelDefs :: [(String, AnnotatedUPT)] -> TelomareParser AnnotatedUPT
416+
parseOneExprOrTopLevelDefs extraModuleBindings =
417+
choice $ try <$> [ parseTopLevelWithExtraModuleBindings extraModuleBindings
418+
, parseLongExpr
419+
]

0 commit comments

Comments
 (0)