Skip to content

Commit a501784

Browse files
authored
Merge pull request #173 from phantamanta44/majestic
majestic: Type-checking improvements + unifying overload resolution + simple REPL
2 parents ac427d6 + 19c5b41 commit a501784

File tree

9 files changed

+430
-83
lines changed

9 files changed

+430
-83
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ DATA_DIR
5656

5757
stack*.yaml.lock
5858

59+
# Generated source files
60+
src/compiler/api/GF/Grammar/Lexer.hs
61+
src/compiler/api/GF/Grammar/Parser.hs
62+
src/compiler/api/PackageInfo_gf.hs
63+
src/compiler/api/Paths_gf.hs
64+
5965
# Output files for test suite
6066
*.out
6167
gf-tests.html

src/compiler/api/GF/Compile/Compute/Concrete.hs

Lines changed: 121 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
{-# LANGUAGE RankNTypes, BangPatterns, CPP, ExistentialQuantification #-}
1+
{-# LANGUAGE RankNTypes, BangPatterns, CPP, ExistentialQuantification, LambdaCase #-}
22

33
-- | Functions for computing the values of terms in the concrete syntax, in
44
-- | preparation for PMCFG generation.
55
module GF.Compile.Compute.Concrete
66
( normalForm, normalFlatForm, normalStringForm
77
, Value(..), Thunk, ThunkState(..), Env, Scope, showValue
8-
, MetaThunks, Constraint, Globals(..), ConstValue(..)
8+
, PredefImpl, Predef(..), PredefCombinator, ($\)
9+
, pdForce, pdClosedArgs, pdArity, pdStandard
10+
, MetaThunks, Constraint, PredefTable, Globals(..), ConstValue(..)
911
, EvalM(..), runEvalM, runEvalOneM, reset, evalError, evalWarn
1012
, eval, apply, force, value2term, patternMatch, stdPredef
1113
, unsafeIOToEvalM
@@ -26,6 +28,7 @@ import GF.Grammar.Predef
2628
import GF.Grammar.Lockfield(lockLabel)
2729
import GF.Grammar.Printer
2830
import GF.Data.Operations(Err(..))
31+
import GF.Data.Utilities(splitAt',(<||>),anyM)
2932
import GF.Infra.CheckM
3033
import GF.Infra.Option
3134
import Data.STRef
@@ -142,6 +145,37 @@ showValue (VAlts _ _) = "VAlts"
142145
showValue (VStrs _) = "VStrs"
143146
showValue (VSymCat _ _ _) = "VSymCat"
144147

148+
isOpen :: [Ident] -> Term -> EvalM s Bool
149+
isOpen bound (Vr x) = return $ x `notElem` bound
150+
isOpen bound (App f x) = isOpen bound f <||> isOpen bound x
151+
isOpen bound (Abs b x t) = isOpen (x:bound) t
152+
isOpen bound (ImplArg t) = isOpen bound t
153+
isOpen bound (Prod b x d cod) = isOpen bound d <||> isOpen (x:bound) cod
154+
isOpen bound (Typed t ty) = isOpen bound t
155+
isOpen bound (Example t s) = isOpen bound t
156+
isOpen bound (RecType fs) = anyM (isOpen bound . snd) fs
157+
isOpen bound (R fs) = anyM (isOpen bound . snd . snd) fs
158+
isOpen bound (P t f) = isOpen bound t
159+
isOpen bound (ExtR t t') = isOpen bound t <||> isOpen bound t'
160+
isOpen bound (Table d cod) = isOpen bound d <||> isOpen bound cod
161+
isOpen bound (T (TTyped ty) cs) = isOpen bound ty <||> anyM (isOpen bound . snd) cs
162+
isOpen bound (T (TWild ty) cs) = isOpen bound ty <||> anyM (isOpen bound . snd) cs
163+
isOpen bound (T _ cs) = anyM (isOpen bound . snd) cs
164+
isOpen bound (V ty cs) = isOpen bound ty <||> anyM (isOpen bound) cs
165+
isOpen bound (S t x) = isOpen bound t <||> isOpen bound x
166+
isOpen bound (Let (x,(ty,d)) t) = isOpen bound d <||> isOpen (x:bound) t
167+
isOpen bound (C t t') = isOpen bound t <||> isOpen bound t'
168+
isOpen bound (Glue t t') = isOpen bound t <||> isOpen bound t'
169+
isOpen bound (EPattType ty) = isOpen bound ty
170+
isOpen bound (ELincat c ty) = isOpen bound ty
171+
isOpen bound (ELin c t) = isOpen bound t
172+
isOpen bound (FV ts) = anyM (isOpen bound) ts
173+
isOpen bound (Markup tag as ts) = anyM (isOpen bound) ts <||> anyM (isOpen bound . snd) as
174+
isOpen bound (Reset c t) = isOpen bound t
175+
isOpen bound (Alts d as) = isOpen bound d <||> anyM (\(x,y) -> isOpen bound x <||> isOpen bound y) as
176+
isOpen bound (Strs ts) = anyM (isOpen bound) ts
177+
isOpen _ _ = return False
178+
145179
eval env (Vr x) vs = do (tnk,depth) <- lookup x env
146180
withVar depth $ do
147181
v <- force tnk
@@ -206,9 +240,8 @@ eval env (S t1 t2) vs = do v1 <- eval env t1 []
206240
v1 -> return v0
207241
eval env (Let (x,(_,t1)) t2) vs = do tnk <- newThunk env t1
208242
eval ((x,tnk):env) t2 vs
209-
eval env (Q q@(m,id)) vs
210-
| m == cPredef = do vs' <- mapM force vs
211-
res <- evalPredef id vs'
243+
eval env t@(Q q@(m,id)) vs
244+
| m == cPredef = do res <- evalPredef env t id vs
212245
case res of
213246
Const res -> return res
214247
RunTime -> return (VApp q vs)
@@ -292,25 +325,25 @@ apply (VClosure env (Abs b x t)) (v:vs) = eval ((x,v):env) t vs
292325
apply v [] = return v
293326

294327

295-
stdPredef :: Map.Map Ident ([Value s] -> EvalM s (ConstValue (Value s)))
328+
stdPredef :: PredefTable s
296329
stdPredef = Map.fromList
297-
[(cLength, \[v] -> case value2string v of
298-
Const s -> return (Const (VInt (genericLength s)))
299-
_ -> return RunTime)
300-
,(cTake, \[v1,v2] -> return (fmap string2value (liftA2 genericTake (value2int v1) (value2string v2))))
301-
,(cDrop, \[v1,v2] -> return (fmap string2value (liftA2 genericDrop (value2int v1) (value2string v2))))
302-
,(cTk, \[v1,v2] -> return (fmap string2value (liftA2 genericTk (value2int v1) (value2string v2))))
303-
,(cDp, \[v1,v2] -> return (fmap string2value (liftA2 genericDp (value2int v1) (value2string v2))))
304-
,(cIsUpper,\[v] -> return (fmap toPBool (liftA (all isUpper) (value2string v))))
305-
,(cToUpper,\[v] -> return (fmap string2value (liftA (map toUpper) (value2string v))))
306-
,(cToLower,\[v] -> return (fmap string2value (liftA (map toLower) (value2string v))))
307-
,(cEqStr, \[v1,v2] -> return (fmap toPBool (liftA2 (==) (value2string v1) (value2string v2))))
308-
,(cOccur, \[v1,v2] -> return (fmap toPBool (liftA2 occur (value2string v1) (value2string v2))))
309-
,(cOccurs, \[v1,v2] -> return (fmap toPBool (liftA2 occurs (value2string v1) (value2string v2))))
310-
,(cEqInt, \[v1,v2] -> return (fmap toPBool (liftA2 (==) (value2int v1) (value2int v2))))
311-
,(cLessInt,\[v1,v2] -> return (fmap toPBool (liftA2 (<) (value2int v1) (value2int v2))))
312-
,(cPlus, \[v1,v2] -> return (fmap VInt (liftA2 (+) (value2int v1) (value2int v2))))
313-
,(cError, \[v] -> case value2string v of
330+
[(cLength, pdStandard 1 $\ \[v] -> case value2string v of
331+
Const s -> return (Const (VInt (genericLength s)))
332+
_ -> return RunTime)
333+
,(cTake, pdStandard 2 $\ \[v1,v2] -> return (fmap string2value (liftA2 genericTake (value2int v1) (value2string v2))))
334+
,(cDrop, pdStandard 2 $\ \[v1,v2] -> return (fmap string2value (liftA2 genericDrop (value2int v1) (value2string v2))))
335+
,(cTk, pdStandard 2 $\ \[v1,v2] -> return (fmap string2value (liftA2 genericTk (value2int v1) (value2string v2))))
336+
,(cDp, pdStandard 2 $\ \[v1,v2] -> return (fmap string2value (liftA2 genericDp (value2int v1) (value2string v2))))
337+
,(cIsUpper,pdStandard 1 $\ \[v] -> return (fmap toPBool (liftA (all isUpper) (value2string v))))
338+
,(cToUpper,pdStandard 1 $\ \[v] -> return (fmap string2value (liftA (map toUpper) (value2string v))))
339+
,(cToLower,pdStandard 1 $\ \[v] -> return (fmap string2value (liftA (map toLower) (value2string v))))
340+
,(cEqStr, pdStandard 2 $\ \[v1,v2] -> return (fmap toPBool (liftA2 (==) (value2string v1) (value2string v2))))
341+
,(cOccur, pdStandard 2 $\ \[v1,v2] -> return (fmap toPBool (liftA2 occur (value2string v1) (value2string v2))))
342+
,(cOccurs, pdStandard 2 $\ \[v1,v2] -> return (fmap toPBool (liftA2 occurs (value2string v1) (value2string v2))))
343+
,(cEqInt, pdStandard 2 $\ \[v1,v2] -> return (fmap toPBool (liftA2 (==) (value2int v1) (value2int v2))))
344+
,(cLessInt,pdStandard 2 $\ \[v1,v2] -> return (fmap toPBool (liftA2 (<) (value2int v1) (value2int v2))))
345+
,(cPlus, pdStandard 2 $\ \[v1,v2] -> return (fmap VInt (liftA2 (+) (value2int v1) (value2int v2))))
346+
,(cError, pdStandard 1 $\ \[v] -> case value2string v of
314347
Const msg -> fail msg
315348
_ -> fail "Indescribable error appeared")
316349
]
@@ -671,6 +704,16 @@ instance Applicative ConstValue where
671704
liftA2 f _ RunTime = RunTime
672705
#endif
673706

707+
instance Foldable ConstValue where
708+
foldr f a (Const x) = f x a
709+
foldr f a RunTime = a
710+
foldr f a NonExist = a
711+
712+
instance Traversable ConstValue where
713+
traverse f (Const x) = Const <$> f x
714+
traverse f RunTime = pure RunTime
715+
traverse f NonExist = pure NonExist
716+
674717
value2string v = fmap (\(_,ws,_) -> unwords ws) (value2string' v False [] [])
675718

676719
value2string' (VStr w1) True (w2:ws) qs = Const (False,(w1++w2):ws,qs)
@@ -728,12 +771,63 @@ string2value' (w:ws) = VC (VStr w) (string2value' ws)
728771
value2int (VInt n) = Const n
729772
value2int _ = RunTime
730773

774+
-----------------------------------------------------------------------
775+
-- * Global/built-in definitions
776+
777+
type PredefImpl a s = [a] -> EvalM s (ConstValue (Value s))
778+
newtype Predef a s = Predef { runPredef :: Term -> Env s -> PredefImpl a s }
779+
type PredefCombinator a b s = Predef a s -> Predef b s
780+
781+
infix 0 $\\
782+
783+
($\) :: PredefCombinator a b s -> PredefImpl a s -> Predef b s
784+
k $\ f = k (Predef (\_ _ -> f))
785+
786+
pdForce :: PredefCombinator (Value s) (Thunk s) s
787+
pdForce def = Predef $ \h env args -> do
788+
argValues <- mapM force args
789+
runPredef def h env argValues
790+
791+
pdClosedArgs :: PredefCombinator (Value s) (Value s) s
792+
pdClosedArgs def = Predef $ \h env args -> do
793+
open <- anyM (value2term True [] >=> isOpen []) args
794+
if open then return RunTime else runPredef def h env args
795+
796+
pdArity :: Int -> PredefCombinator (Thunk s) (Thunk s) s
797+
pdArity n def = Predef $ \h env args ->
798+
case splitAt' n args of
799+
Nothing -> do
800+
t <- papply env h args
801+
let t' = abstract 0 (n - length args) t
802+
Const <$> eval env t' []
803+
Just (usedArgs, remArgs) -> do
804+
res <- runPredef def h env usedArgs
805+
forM res $ \v -> case remArgs of
806+
[] -> return v
807+
_ -> do
808+
t <- value2term False (fst <$> env) v
809+
eval env t remArgs
810+
where
811+
papply env t [] = return t
812+
papply env t (arg:args) = do
813+
arg <- tnk2term False (fst <$> env) arg
814+
papply env (App t arg) args
815+
816+
abstract i n t
817+
| n <= 0 = t
818+
| otherwise = let x = identV (rawIdentS "a") i
819+
in Abs Explicit x (abstract (i + 1) (n - 1) (App t (Vr x)))
820+
821+
pdStandard :: Int -> PredefCombinator (Value s) (Thunk s) s
822+
pdStandard n = pdArity n . pdForce . pdClosedArgs
823+
731824
-----------------------------------------------------------------------
732825
-- * Evaluation monad
733826

734827
type MetaThunks s = Map.Map MetaId (Thunk s)
735828
type Cont s r = MetaThunks s -> Int -> r -> [Message] -> ST s (CheckResult r [Message])
736-
data Globals = Gl Grammar (forall s . Map.Map Ident ([Value s] -> EvalM s (ConstValue (Value s))))
829+
type PredefTable s = Map.Map Ident (Predef (Thunk s) s)
830+
data Globals = Gl Grammar (forall s . PredefTable s)
737831
newtype EvalM s a = EvalM (forall r . Globals -> (a -> Cont s r) -> Cont s r)
738832

739833
instance Functor (EvalM s) where
@@ -792,9 +886,9 @@ evalError msg = EvalM (\gr k _ _ r msgs -> return (Fail msg msgs))
792886
evalWarn :: Message -> EvalM s ()
793887
evalWarn msg = EvalM (\gr k mt d r msgs -> k () mt d r (msg:msgs))
794888

795-
evalPredef :: Ident -> [Value s] -> EvalM s (ConstValue (Value s))
796-
evalPredef id vs = EvalM (\globals@(Gl _ predef) k mt d r msgs ->
797-
case fmap (\f -> f vs) (Map.lookup id predef) of
889+
evalPredef :: Env s -> Term -> Ident -> [Thunk s] -> EvalM s (ConstValue (Value s))
890+
evalPredef env h id args = EvalM (\globals@(Gl _ predef) k mt d r msgs ->
891+
case fmap (\def -> runPredef def h env args) (Map.lookup id predef) of
798892
Just (EvalM f) -> f globals k mt d r msgs
799893
Nothing -> k RunTime mt d r msgs)
800894

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
3+
module GF.Compile.Repl (ReplOpts(..), defaultReplOpts, replOptDescrs, getReplOpts, runRepl, runRepl') where
4+
5+
import Control.Monad (unless, forM_, foldM)
6+
import Control.Monad.IO.Class (MonadIO)
7+
import qualified Data.ByteString.Char8 as BS
8+
import Data.Char (isSpace)
9+
import Data.Function ((&))
10+
import Data.Functor ((<&>))
11+
import qualified Data.Map as Map
12+
13+
import System.Console.GetOpt (ArgOrder(RequireOrder), OptDescr(..), ArgDescr(..), getOpt, usageInfo)
14+
import System.Console.Haskeline (InputT, Settings(..), noCompletion, runInputT, getInputLine, outputStrLn)
15+
import System.Directory (getAppUserDataDirectory)
16+
17+
import GF.Compile (batchCompile)
18+
import GF.Compile.Compute.Concrete (Globals(Gl), stdPredef, normalFlatForm)
19+
import GF.Compile.Rename (renameSourceTerm)
20+
import GF.Compile.TypeCheck.ConcreteNew (inferLType)
21+
import GF.Data.ErrM (Err(..))
22+
import GF.Grammar.Grammar
23+
( Grammar
24+
, mGrammar
25+
, Info
26+
, Module
27+
, ModuleName
28+
, ModuleInfo(..)
29+
, ModuleType(MTResource)
30+
, ModuleStatus(MSComplete)
31+
, OpenSpec(OSimple)
32+
, Location (NoLoc)
33+
, Term
34+
, prependModule
35+
)
36+
import GF.Grammar.Lexer (Posn(..), Lang(GF), runLangP)
37+
import GF.Grammar.Parser (pTerm)
38+
import GF.Grammar.Printer (TermPrintQual(Unqualified), ppTerm)
39+
import GF.Infra.CheckM (Check, runCheck)
40+
import GF.Infra.Ident (moduleNameS)
41+
import GF.Infra.Option (noOptions)
42+
import GF.Infra.UseIO (justModuleName)
43+
import GF.Text.Pretty (render)
44+
45+
data ReplOpts = ReplOpts
46+
{ noPrelude :: Bool
47+
, inputFiles :: [String]
48+
}
49+
50+
defaultReplOpts :: ReplOpts
51+
defaultReplOpts = ReplOpts False []
52+
53+
type Errs a = Either [String] a
54+
type ReplOptsOp = ReplOpts -> Errs ReplOpts
55+
56+
replOptDescrs :: [OptDescr ReplOptsOp]
57+
replOptDescrs =
58+
[ Option ['h'] ["help"] (NoArg $ \o -> Left [usageInfo "gfci" replOptDescrs]) "Display help."
59+
, Option [] ["no-prelude"] (flag $ \o -> o { noPrelude = True }) "Don't load the prelude."
60+
]
61+
where
62+
flag f = NoArg $ \o -> pure (f o)
63+
64+
getReplOpts :: [String] -> Errs ReplOpts
65+
getReplOpts args = case errs of
66+
[] -> foldM (&) defaultReplOpts flags <&> \o -> o { inputFiles = inputFiles }
67+
_ -> Left errs
68+
where
69+
(flags, inputFiles, errs) = getOpt RequireOrder replOptDescrs args
70+
71+
execCheck :: MonadIO m => Check a -> (a -> InputT m ()) -> InputT m ()
72+
execCheck c k = case runCheck c of
73+
Ok (a, warn) -> do
74+
unless (null warn) $ outputStrLn warn
75+
k a
76+
Bad err -> outputStrLn err
77+
78+
replModNameStr :: String
79+
replModNameStr = "<repl>"
80+
81+
replModName :: ModuleName
82+
replModName = moduleNameS replModNameStr
83+
84+
parseThen :: MonadIO m => Grammar -> String -> (Term -> InputT m ()) -> InputT m ()
85+
parseThen g s k = case runLangP GF pTerm (BS.pack s) of
86+
Left (Pn l c, err) -> outputStrLn $ err ++ " (" ++ show l ++ ":" ++ show c ++ ")"
87+
Right t -> execCheck (renameSourceTerm g replModName t) $ \t -> k t
88+
89+
runRepl' :: Globals -> IO ()
90+
runRepl' gl@(Gl g _) = do
91+
historyFile <- getAppUserDataDirectory "gfci_history"
92+
runInputT (Settings noCompletion (Just historyFile) True) repl -- TODO tab completion
93+
where
94+
repl = do
95+
getInputLine "gfci> " >>= \case
96+
Nothing -> repl
97+
Just (':' : l) -> let (cmd, arg) = break isSpace l in command cmd (dropWhile isSpace arg)
98+
Just code -> evalPrintLoop code
99+
100+
command "t" arg = do
101+
parseThen g arg $ \main ->
102+
execCheck (inferLType gl main) $ \(_, ty) ->
103+
outputStrLn $ render (ppTerm Unqualified 0 ty)
104+
outputStrLn "" >> repl
105+
106+
command "q" _ = outputStrLn "Bye!"
107+
108+
command cmd _ = do
109+
outputStrLn $ "Unknown REPL command: " ++ cmd
110+
outputStrLn "" >> repl
111+
112+
evalPrintLoop code = do -- TODO bindings
113+
parseThen g code $ \main ->
114+
execCheck (inferLType gl main >>= \(t, _) -> normalFlatForm gl t) $ \nfs ->
115+
forM_ (zip [1..] nfs) $ \(i, nf) ->
116+
outputStrLn $ show i ++ ". " ++ render (ppTerm Unqualified 0 nf)
117+
outputStrLn "" >> repl
118+
119+
runRepl :: ReplOpts -> IO ()
120+
runRepl (ReplOpts noPrelude inputFiles) = do
121+
-- TODO accept an ngf grammar
122+
let toLoad = if noPrelude then inputFiles else "prelude/Predef.gfo" : inputFiles
123+
(g0, opens) <- case toLoad of
124+
[] -> pure (mGrammar [], [])
125+
_ -> do
126+
(_, (_, g0)) <- batchCompile noOptions Nothing toLoad
127+
pure (g0, OSimple . moduleNameS . justModuleName <$> toLoad)
128+
let
129+
modInfo = ModInfo
130+
{ mtype = MTResource
131+
, mstatus = MSComplete
132+
, mflags = noOptions
133+
, mextend = []
134+
, mwith = Nothing
135+
, mopens = opens
136+
, mexdeps = []
137+
, msrc = replModNameStr
138+
, mseqs = Nothing
139+
, jments = Map.empty
140+
}
141+
runRepl' (Gl (prependModule g0 (replModName, modInfo)) (if noPrelude then Map.empty else stdPredef))

0 commit comments

Comments
 (0)