Skip to content

Commit

Permalink
Fix many bugs, add more checks and make all test pass.
Browse files Browse the repository at this point in the history
  • Loading branch information
Joald committed Apr 23, 2019
1 parent 95d7426 commit 47f97bd
Show file tree
Hide file tree
Showing 55 changed files with 411 additions and 156 deletions.
58 changes: 44 additions & 14 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ module Main (main) where
import Parser.Parser (ParserError, parseProgram, composeASTs)
import Parser.TypeDefs (fnName, AST(AST), typeDecls, mapFromDeclList)
import TypeSystem.PatternChecker (PatternError, runCoverageCheck, checkPatterns)
import TypeSystem.TypeDefs (TypeSystemError)
import TypeSystem.TypeDefs
import System.Environment (getArgs)
import Data.Bifunctor (first)
import Semantics.Builtins (makePrelude)
import TypeSystem.Preprocessor (preprocess)
import TypeSystem.TypeSystem (typeCheck)
import Semantics.Interpreter (interpretAST)
import Semantics.TypeDefs
import Debug.Trace (traceM)

import System.IO
import Data.Either (isLeft, isRight)
import Control.Monad (when, unless)
import System.Exit (exitFailure)
import System.Directory

data ProgramError =
ParseError ParserError
Expand All @@ -31,31 +36,56 @@ showResult :: Show a => Either ProgramError a -> String
showResult = either show show

preludeFileName :: String
preludeFileName = "examples/prelude.cont"
preludeFileName = "prelude.cont"

parseRealProgram :: String -> String -> Either ProgramError AST
parseRealProgram name = first ParseError . parseProgram name

mapPrelude :: AST -> AST
mapPrelude (AST types fns) = AST types $ map (\fn -> fn { fnName = makePrelude $ fnName fn }) fns

getRight :: Either e a -> a
getRight (Right x) = x

doInterpret :: Either ProgramError IAST -> IO Value
doInterpret res = if isRight res then interpretAST (getRight res) else return (VAlg "Error occured, exiting!" [])

doChecks :: String -> String -> String -> Either ProgramError IAST
doChecks fname contents preludeContents =
do prelude <- mapPrelude <$> parseRealProgram preludeFileName preludeContents
ast <- parseRealProgram fname contents
let full = composeASTs prelude ast
iast <- first ContinuationError $ preprocess full
let typeEnv = mapFromDeclList $ typeDecls full
traceM $ "Full AST is: " ++ show full
traceM $ "Full IAST is: " ++ show iast
first PatternCoverageError $ runCoverageCheck typeEnv $ checkPatterns full
first TypeError $ typeCheck iast
return iast

-- prints to stdout to separate from trace output;
-- TODO: replace by die when removing trace
printAndExit :: String -> IO ()
printAndExit s = putStrLn s >> exitFailure

assertFileExists :: String -> IO ()
assertFileExists name = do
exists <- doesFileExist name
unless exists . printAndExit $ "Cannot find file \"" ++ name ++ "\"."

{- | Parses one file, typechecks it and prints the AST. -}
oneFileParser :: IO ()
oneFileParser = do
fname:_ <- getArgs
assertFileExists preludeFileName
assertFileExists fname
contents <- readFile fname
preludeContents <- readFile preludeFileName
putStrLn . showResult $ do
prelude <- mapPrelude <$> parseRealProgram preludeFileName preludeContents
ast <- parseRealProgram fname contents
let full = composeASTs prelude ast
iast <- first ContinuationError $ preprocess full
let typeEnv = mapFromDeclList $ typeDecls full
traceM $ "Full AST is: " ++ show full
traceM $ "Full IAST is: " ++ show iast
first PatternCoverageError $ runCoverageCheck typeEnv $ checkPatterns full
first TypeError $ typeCheck iast
return $ interpretAST iast
let res = doChecks fname contents preludeContents
when (isLeft res) . printAndExit $ showResult res
v <- doInterpret res
print v
hFlush stderr

main :: IO ()
main = oneFileParser
File renamed without changes.
3 changes: 3 additions & 0 deletions bad/bad10.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# multiple top-level identifiers with the same name
main = const 1;
main = const 3;
1 change: 1 addition & 0 deletions bad/bad11.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# empty program is rejected because of no main
6 changes: 6 additions & 0 deletions bad/bad12.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Unbound type.

NonExistentType ::
a = [];

main = const a;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions bad/bad6.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# non-continuation style factorial function

Int -> Int ::
fac n = if n == 0
then 1
else n * fac (n - 1);
5 changes: 5 additions & 0 deletions bad/bad7.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# non-continuation style factorial defined as lambda without a type signature
fac2 = fn n . if n == 0
then 1
else n * fac2 (n - 1);

4 changes: 4 additions & 0 deletions bad/bad8.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# non-continuation function defined as alias on a prelude non-continuation function
Int -> Int ::
intId = id;

6 changes: 6 additions & 0 deletions bad/bad9.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Kind error

Maybe ::
a = Nothing;

main = const a;
2 changes: 2 additions & 0 deletions badout/bad1.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Occurs check: cannot construct infinite type a72 ~ a72 -> a73
2 changes: 2 additions & 0 deletions badout/bad10.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Multiple bindings of identifier "main" in program
2 changes: 2 additions & 0 deletions badout/bad11.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Cannot find the entry point of the program. Did you specify the `main` function?
2 changes: 2 additions & 0 deletions badout/bad12.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Unbound type variable NonExistentType, expected [a73]
2 changes: 2 additions & 0 deletions badout/bad2.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Multiple bindings of identifier "True" in program
2 changes: 2 additions & 0 deletions badout/bad3.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Cannot unify type Int -> Int with Int
1 change: 1 addition & 0 deletions badout/bad4.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cannot merge coverage Ctors "Maybe" (fromList [("Just",[Anything])]) with ExactInt
2 changes: 2 additions & 0 deletions badout/bad5.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Cannot unify type [a77] with Maybe a78
2 changes: 2 additions & 0 deletions badout/bad6.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Cannot unify type (Int -> a81) -> a81 with Int
2 changes: 2 additions & 0 deletions badout/bad7.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Forbidden non-continuation style type of top-level function Int -> Int
2 changes: 2 additions & 0 deletions badout/bad8.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Cannot unify type (Int -> a14) -> a14 with Int
2 changes: 2 additions & 0 deletions badout/bad9.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Type error occured:
Kind error occured: Cannot unify kind * with kind * -> *
1 change: 0 additions & 1 deletion examples/bad6.cont

This file was deleted.

28 changes: 0 additions & 28 deletions examples/prelude.cont

This file was deleted.

29 changes: 0 additions & 29 deletions examples/test.cont

This file was deleted.

2 changes: 1 addition & 1 deletion examples/conttest.cont → good/conttest.cont
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ max3 x y z =
else max2 x y;

Int ::
main = max3 1 2 3 id;
main = max3 1 2 3;
4 changes: 2 additions & 2 deletions examples/fullInference.cont → good/fullInference.cont
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ type Tree a = Empty | Node a (Tree a) (Tree a);

mapTree f t c = match t with
| Empty => c Empty
| Node x t1 t2 => mapTree f t1 (λl . mapTree f t2 (λr . Node (f x) l r));
| Node x t1 t2 => mapTree f t1 (λl . mapTree f t2 (λr . c (Node (f x) l r)));

testTree = Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty);

main = mapTree succ testTree id;
main = mapTree succ testTree;
13 changes: 5 additions & 8 deletions examples/listTest.cont → good/listTest.cont
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
type TwoLists a b = TwoLists [a] [b];

#[a] -> Maybe a ::
#safeHead xs c = match xs with
# | h : _ => c (Just h)
# | [] => c Nothing;
[a] -> Maybe a ::
safeHead xs c = match xs with
| h : _ => c (Just h)
| [] => c Nothing;

d :
(a -> b -> (c -> d) -> d) -> [a] -> [b] -> Maybe c ::
Expand All @@ -14,7 +14,4 @@ joinSafeHeads f xs ys c = match TwoLists xs ys with
add x y c = c (x + y);

Maybe Int ::
main = joinSafeHeads add [2, 1, 3, 7] [4, 2, 0] id;


sub = add 1 2;
main = joinSafeHeads add [3, 1, 3, 7] [4, 2, 12];
4 changes: 4 additions & 0 deletions examples/tiny.cont → good/tiny.cont
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ type BExpr =
| EAnd BExpr BExpr
| EOr BExpr BExpr
| EEq Expr Expr;

type NotImplemented = NotImplemented;

main = const NotImplemented;
4 changes: 4 additions & 0 deletions good/typeMutualRecursion.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type Box a = Boxed a;
type Tree a = Node a (Tree a) (Tree a) | Empty;

main = const (Node (Boxed Empty) Empty Empty);
1 change: 1 addition & 0 deletions goodout/conttest.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
1 change: 1 addition & 0 deletions goodout/fullInference.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Node 2 (Node 3 Empty Empty) (Node 4 Empty Empty)
1 change: 1 addition & 0 deletions goodout/listTest.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Just 7
1 change: 1 addition & 0 deletions goodout/tiny.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NotImplemented
1 change: 1 addition & 0 deletions goodout/typeMutualRecursion.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Node (Boxed Empty) Empty Empty
20 changes: 20 additions & 0 deletions loop.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# this program loops, thus demonstrating that the language is strictly evaluated.

c = 1 : c;

Int -> [a] -> [a] ::
drop x l c = match x with
| 0 => c l
| _ => match l with
| [] => c []
| _:t => drop (x - 1) t c;

[a] :
Int -> [a] -> [a] ::
take x l c = match x with
| 0 => c []
| _ => match l with
| [] => []
| h:t => take (x - 1) t (\res . c (h : res));

main = take 3 c;
1 change: 1 addition & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies:
- hspec >= 2.6.1
- hspec-megaparsec
- containers
- directory

library:
source-dirs: src
Expand Down
31 changes: 31 additions & 0 deletions prelude.cont
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# prelude.cont
# this file is loaded before all the input files by the interpreter and contains basic
# definitions of standard library functions and types.

type Bool = True | False;
type Maybe a = Just a | Nothing;
type Either e a = Left e | Right a;

a -> a ::
id x = x;

id2 = \x.x;

Int -> Int ::
succ x = x + 1;

(a -> b) -> (b -> c) -> a -> c ::
comp f g x = g (f x);

(a -> b) -> [a] -> [b] ::
map f xs = match xs with
| [] => []
| h:t => f h : map f t;

(a -> (b -> c) -> c) -> [a] -> ([b] -> c) -> c ::
contMap f xs c = match xs with
| [] => c []
| h:t => f h (\x . contMap f t (\rest . c (x : rest)));

a -> b -> a ::
const x _ = x;
7 changes: 7 additions & 0 deletions scripts/output-generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import os

for file_name in os.listdir("good"):
os.system("stack run good/%s 2>/dev/null >goodout/%s" % (file_name, file_name.replace(".cont", ".out")))

for file_name in os.listdir("bad"):
os.system("stack run bad/%s 2>/dev/null >badout/%s" % (file_name, file_name.replace(".cont", ".out")))
2 changes: 1 addition & 1 deletion src/Parser/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ step :: Parser AST
step = flip AST [] . (:[]) <$> try typeDecl <|> AST [] . (:[]) <$> funDecl

program :: Parser AST
program = sc *> (foldr1 composeASTs <$> many step) <* eof
program = sc *> (foldr composeASTs (AST [] []) <$> many step) <* eof

type ParserError = String

Expand Down
Loading

0 comments on commit 47f97bd

Please sign in to comment.