continued work on pattern matching v2

This commit is contained in:
sebastianselander 2023-03-20 17:40:09 +01:00
parent c3ea343d00
commit 9cd2cdb511
3 changed files with 279 additions and 60 deletions

219
Session.vim Normal file
View file

@ -0,0 +1,219 @@
let SessionLoad = 1
let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1
let v:this_session=expand("<sfile>:p")
silent only
silent tabonly
cd ~/Documents/bachelor_thesis/language
if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''
let s:wipebuf = bufnr('%')
endif
let s:shortmess_save = &shortmess
if &shortmess =~ 'A'
set shortmess=aoOA
else
set shortmess=aoO
endif
badd +1 ~/Documents/bachelor_thesis/language
badd +298 src/TypeChecker/TypeChecker.hs
badd +7 test_program
badd +46 src/TypeChecker/TypeCheckerIr.hs
badd +6 Grammar.cf
badd +1 src/Grammar/Abs.hs
argglobal
%argdel
$argadd ~/Documents/bachelor_thesis/language
set stal=2
tabnew +setlocal\ bufhidden=wipe
tabnew +setlocal\ bufhidden=wipe
tabnew +setlocal\ bufhidden=wipe
tabrewind
edit src/TypeChecker/TypeChecker.hs
let s:save_splitbelow = &splitbelow
let s:save_splitright = &splitright
set splitbelow splitright
wincmd _ | wincmd |
vsplit
1wincmd h
wincmd w
let &splitbelow = s:save_splitbelow
let &splitright = s:save_splitright
wincmd t
let s:save_winminheight = &winminheight
let s:save_winminwidth = &winminwidth
set winminheight=0
set winheight=1
set winminwidth=0
set winwidth=1
exe 'vert 1resize ' . ((&columns * 99 + 86) / 173)
exe 'vert 2resize ' . ((&columns * 73 + 86) / 173)
argglobal
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 298 - ((18 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 298
normal! 029|
lcd ~/Documents/bachelor_thesis/language
wincmd w
argglobal
if bufexists(fnamemodify("~/Documents/bachelor_thesis/language/Grammar.cf", ":p")) | buffer ~/Documents/bachelor_thesis/language/Grammar.cf | else | edit ~/Documents/bachelor_thesis/language/Grammar.cf | endif
if &buftype ==# 'terminal'
silent file ~/Documents/bachelor_thesis/language/Grammar.cf
endif
balt ~/Documents/bachelor_thesis/language/src/TypeChecker/TypeChecker.hs
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 7 - ((6 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 7
normal! 0
lcd ~/Documents/bachelor_thesis/language
wincmd w
exe 'vert 1resize ' . ((&columns * 99 + 86) / 173)
exe 'vert 2resize ' . ((&columns * 73 + 86) / 173)
tabnext
edit ~/Documents/bachelor_thesis/language/src/TypeChecker/TypeCheckerIr.hs
let s:save_splitbelow = &splitbelow
let s:save_splitright = &splitright
set splitbelow splitright
wincmd _ | wincmd |
vsplit
1wincmd h
wincmd w
let &splitbelow = s:save_splitbelow
let &splitright = s:save_splitright
wincmd t
let s:save_winminheight = &winminheight
let s:save_winminwidth = &winminwidth
set winminheight=0
set winheight=1
set winminwidth=0
set winwidth=1
exe 'vert 1resize ' . ((&columns * 86 + 86) / 173)
exe 'vert 2resize ' . ((&columns * 86 + 86) / 173)
argglobal
balt ~/Documents/bachelor_thesis/language/test_program
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 1 - ((0 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 1
normal! 0
lcd ~/Documents/bachelor_thesis/language
wincmd w
argglobal
if bufexists(fnamemodify("~/Documents/bachelor_thesis/language/src/Grammar/Abs.hs", ":p")) | buffer ~/Documents/bachelor_thesis/language/src/Grammar/Abs.hs | else | edit ~/Documents/bachelor_thesis/language/src/Grammar/Abs.hs | endif
if &buftype ==# 'terminal'
silent file ~/Documents/bachelor_thesis/language/src/Grammar/Abs.hs
endif
balt ~/Documents/bachelor_thesis/language/src/TypeChecker/TypeCheckerIr.hs
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 1 - ((0 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 1
normal! 0
lcd ~/Documents/bachelor_thesis/language
wincmd w
exe 'vert 1resize ' . ((&columns * 86 + 86) / 173)
exe 'vert 2resize ' . ((&columns * 86 + 86) / 173)
tabnext
edit ~/Documents/bachelor_thesis/language/Grammar.cf
argglobal
balt ~/Documents/bachelor_thesis/language/src/Grammar/Abs.hs
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 40 - ((12 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 40
normal! 0
lcd ~/Documents/bachelor_thesis/language
tabnext
edit ~/Documents/bachelor_thesis/language/test_program
argglobal
balt ~/Documents/bachelor_thesis/language/src/TypeChecker/TypeChecker.hs
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=0
setlocal fml=1
setlocal fdn=20
setlocal fen
silent! normal! zE
let &fdl = &fdl
let s:l = 7 - ((6 * winheight(0) + 21) / 42)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 7
normal! 010|
lcd ~/Documents/bachelor_thesis/language
tabnext 1
set stal=1
if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'
silent exe 'bwipe ' . s:wipebuf
endif
unlet! s:wipebuf
set winheight=1 winwidth=20
let &shortmess = s:shortmess_save
let s:sx = expand("<sfile>:p:r")."x.vim"
if filereadable(s:sx)
exe "source " . fnameescape(s:sx)
endif
let &g:so = s:so_save | let &g:siso = s:siso_save
set hlsearch
nohlsearch
doautoall SessionLoadPost
unlet SessionLoad
" vim: set ft=vim :

View file

@ -8,13 +8,16 @@ import Control.Monad.Except
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Data.Foldable (traverse_) import Data.Foldable (traverse_)
import Data.Function (on)
import Data.Functor.Identity (runIdentity) import Data.Functor.Identity (runIdentity)
import Data.List (foldl') import Data.List (foldl')
import Data.List.Extra (allSame)
import Data.Map (Map) import Data.Map (Map)
import Data.Map qualified as M import Data.Map qualified as M
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Set (Set) import Data.Set (Set)
import Data.Set qualified as S import Data.Set qualified as S
import Data.Tree (flatten)
import Debug.Trace (trace) import Debug.Trace (trace)
import Grammar.Abs import Grammar.Abs
import Grammar.Print (printTree) import Grammar.Print (printTree)
@ -204,9 +207,9 @@ algoW = \case
-- \| ------------------ -- \| ------------------
-- \| Γ ⊢ i : Int, ∅ -- \| Γ ⊢ i : Int, ∅
ELit (LInt n) -> ELit lit ->
return (nullSubst, TMono "Int", T.ELit (TMono "Int") (LInt n)) let lt = litType lit
ELit a -> error $ "NOT IMPLEMENTED YET: ELit " ++ show a in return (nullSubst, lt, T.ELit lt lit)
-- \| x : σ ∈ Γ τ = inst(σ) -- \| x : σ ∈ Γ τ = inst(σ)
-- \| ---------------------- -- \| ----------------------
-- \| Γ ⊢ x : τ, ∅ -- \| Γ ⊢ x : τ, ∅
@ -289,16 +292,14 @@ algoW = \case
(s2, t2, e1') <- algoW e1 (s2, t2, e1') <- algoW e1
let composition = s2 `compose` s1 let composition = s2 `compose` s1
return (composition, t2, apply composition $ T.ELet (T.Bind (name, t2) e0') e1') return (composition, t2, apply composition $ T.ELet (T.Bind (name, t2) e0') e1')
-- TODO: give caseExpr a concrete type before proceeding
-- probably by returning substitutions in the functions used in this body
ECase caseExpr injs -> do ECase caseExpr injs -> do
(_, t0, e0') <- algoW caseExpr (sub, _, e') <- algoW caseExpr
(injs', ts) <- mapAndUnzipM (checkInj t0) injs trace ("SUB: " ++ show sub) return ()
case ts of t <- checkCase caseExpr injs
[] -> throwError "Case expression missing any matches" return (sub, t, T.ECase t e' (map (\(Inj i _) -> T.Inj (i, t) e') injs))
ts -> do
unified <- zipWithM unify ts (tail ts)
let composition = foldl' compose mempty unified
let typ = apply composition (head ts)
return (composition, typ, apply composition $ T.ECase typ e0' injs')
-- | Unify two types producing a new substitution -- | Unify two types producing a new substitution
unify :: Type -> Type -> Infer Subst unify :: Type -> Type -> Infer Subst
@ -312,7 +313,6 @@ unify t0 t1 = do
(a, TPol b) -> occurs b a (a, TPol b) -> occurs b a
(TMono a, TMono b) -> (TMono a, TMono b) ->
if a == b then return M.empty else throwError "Types do not unify" if a == b then return M.empty else throwError "Types do not unify"
-- \| TODO: Figure out a cleaner way to express the same thing
(TConstr (Constr name t), TConstr (Constr name' t')) -> (TConstr (Constr name t), TConstr (Constr name' t')) ->
if name == name' && length t == length t' if name == name' && length t == length t'
then do then do
@ -464,52 +464,45 @@ insertConstr i t =
-------- PATTERN MATCHING --------- -------- PATTERN MATCHING ---------
-- "case expr of", the type of 'expr' is caseType unifyAll :: [Type] -> Infer [Subst]
checkInj :: Type -> Inj -> Infer (T.Inj, Type) unifyAll [] = return []
checkInj caseType (Inj it expr) = do unifyAll [_] = return []
(args, t') <- initType caseType it unifyAll (x : y : xs) = do
subst <- unify caseType t' uni <- unify x y
applySt subst $ do all <- unifyAll (y : xs)
(_, t, e') <- local (\st -> st{vars = args `M.union` vars st}) (algoW expr) return $ uni : all
return (T.Inj (it, t') e', t)
initType :: Type -> Init -> Infer (Map Ident Poly, Type) checkCase :: Exp -> [Inj] -> Infer Type
initType expected = \case checkCase e injs = do
InitLit lit -> error "Pattern match on literals not implemented yet" expT <- fst <$> inferExp e
InitConstr c args -> do (injTs, returns) <- mapAndUnzipM checkInj injs
st <- gets constructors unifyAll (expT : injTs)
case M.lookup c st of subst <- foldl1 compose <$> zipWithM unify returns (tail returns)
Nothing -> let substed = map (apply subst) returns
throwError $ unless (allSame substed || null substed) (throwError "Different return types of case, or no cases")
unwords return $ head substed
[ "Constructor:"
, printTree c {- | fst = type of init
, "does not exist" | snd = type of expr
] -}
Just t -> do checkInj :: Inj -> Infer (Type, Type)
let flat = flattenType t checkInj (Inj it expr) = do
let returnType = last flat initT <- inferInit it
case ( length (init flat) == length args (exprT, _) <- inferExp expr
, returnType `isMoreSpecificOrEq` expected return (initT, exprT)
) of
(True, True) -> inferInit :: Init -> Infer Type
return inferInit = \case
( M.fromList $ zip args (map (Forall []) flat) InitLit lit -> return $ litType lit
, expected InitConstr fn vars -> do
) gets (M.lookup fn . constructors) >>= \case
(False, _) -> Nothing -> throwError $ "Constructor: " ++ printTree fn ++ " does not exist"
throwError $ Just a -> do
"Can't partially match on the constructor: " let ft = init $ flattenType a
++ printTree c case compare (length vars) (length ft) of
(_, False) -> EQ -> return . last $ flattenType a
throwError $ _ -> throwError "Partial pattern match not allowed"
unwords InitCatch -> fresh
[ "Inferred type"
, printTree returnType
, "does not match expected type:"
, printTree expected
]
InitCatch -> return (mempty, expected)
flattenType :: Type -> [Type] flattenType :: Type -> [Type]
flattenType (TArr a b) = flattenType a ++ flattenType b flattenType (TArr a b) = flattenType a ++ flattenType b

View file

@ -1,2 +1,9 @@
id : 'a -> 'a ; data Bool () where {
id = \x. x ; True : Bool ()
False : Bool ()
};
main : Bool () -> _Int ;
main x = case x of {
1 => 0
}