1
0
forked from GitHub/gf-core

the JSON dump now supports the entire GF language

This commit is contained in:
Krasimir Angelov
2025-03-26 14:56:08 +01:00
parent 65e4ca309c
commit e6c4775ade
9 changed files with 227 additions and 1340 deletions

View File

@@ -3,91 +3,76 @@ module GF.Compile.ConcreteToHaskell(concretes2haskell,concrete2haskell) where
import PGF2(Literal(..))
import Data.List(isPrefixOf,sort,sortOn)
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Map as Map
import GF.Text.Pretty
--import GF.Grammar.Predef(cPredef,cInts)
--import GF.Compile.Compute.Predef(predef)
--import GF.Compile.Compute.Value(Predefined(..))
import GF.Infra.Ident(Ident,identC,identS,identW,prefixIdent,showRawIdent,rawIdentS)
import GF.Grammar.Predef
import GF.Grammar.Grammar
import GF.Grammar.Macros
import GF.Infra.Ident
import GF.Infra.Option
import GF.Haskell as H
import GF.Grammar.Canonical as C
import GF.Compile.GrammarToCanonical
import Debug.Trace(trace)
-- | Generate Haskell code for the all concrete syntaxes associated with
-- the named abstract syntax in given the grammar.
concretes2haskell opts absname gr = do
Grammar abstr cncs <- grammar2canonical opts absname gr
return [(filename,render80 $ concrete2haskell opts abstr cncmod)
| cncmod<-cncs,
let ModId name = concName cncmod
filename = showRawIdent name ++ ".hs" :: FilePath
gr <- grammar2canonical opts absname gr
let abstr:concrs = modules gr
return [(filename,render80 $ concrete2haskell opts abstr concr)
| concr@(MN mn,_) <- concrs,
let filename = showIdent mn ++ ".hs" :: FilePath
]
-- | Generate Haskell code for the given concrete module.
-- The only options that make a difference are
-- @-haskell=noprefix@ and @-haskell=variants@.
concrete2haskell opts
abstr@(Abstract _ _ cats funs)
modinfo@(Concrete cnc absname _ ps lcs lns) =
haskPreamble absname cnc $$
concrete2haskell opts abstr@(absname,_) concr@(cncname,mi) =
haskPreamble absname cncname $$
vcat (
nl:Comment "--- Parameter types ---":
map paramDef ps ++
[paramDef id ps | (id,ResParam (Just (L _ ps)) _) <- Map.toList (jments mi)] ++
nl:Comment "--- Type signatures for linearization functions ---":
map signature cats ++
nl:Comment "--- Linearization functions for empty categories ---":
emptydefs ++
[signature id | (id,CncCat _ _ _ _ _) <- Map.toList (jments mi)] ++
nl:Comment "--- Linearization types ---":
map lincatDef lcs ++
[lincatDef id ty | (id,CncCat (Just (L _ ty)) _ _ _ _) <- Map.toList (jments mi)] ++
nl:Comment "--- Linearization functions ---":
lindefs ++
concat (Map.elems lindefs) ++
nl:Comment "--- Type classes for projection functions ---":
map labelClass (S.toList labels) ++
-- map labelClass (S.toList labels) ++
nl:Comment "--- Record types ---":
concatMap recordType recs)
[] -- concatMap recordType recs
)
where
nl = Comment ""
recs = S.toList (S.difference (records (lcs,lns)) common_records)
labels = S.difference (S.unions (map S.fromList recs)) common_labels
common_records = S.fromList [[label_s]]
common_labels = S.fromList [label_s]
label_s = LabelId (rawIdentS "s")
signature (CatDef c _) = TypeSig lf (Fun abs (pure lin))
signature c = TypeSig lf (Fun abs (pure lin))
where
abs = tcon0 (prefixIdent "A." (gId c))
lin = tcon0 lc
lf = linfunName c
lc = lincatName c
emptydefs = map emptydef (S.toList emptyCats)
emptydef c = Eqn (linfunName c,[WildP]) (Const "undefined")
emptyCats = allcats `S.difference` linfuncats
where
--funcats = S.fromList [c | FunDef f (C.Type _ (TypeApp c _))<-funs]
allcats = S.fromList [c | CatDef c _<-cats]
gId :: ToIdent i => i -> Ident
gId :: Ident -> Ident
gId = (if haskellOption opts HaskellNoPrefix then id else prefixIdent "G")
. toIdent
va = haskellOption opts HaskellVariants
pure = if va then ListT else id
haskPreamble :: ModId -> ModId -> Doc
haskPreamble :: ModuleName -> ModuleName -> Doc
haskPreamble absname cncname =
"{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, LambdaCase #-}" $$
"module" <+> cncname <+> "where" $$
"import Prelude hiding (Ordering(..))" $$
"import Control.Applicative((<$>),(<*>))" $$
"import PGF.Haskell" $$
"import qualified" <+> absname <+> "as A" $$
"" $$
"-- | Token sequences, output form linearization functions" $$
"type Str = [Tok] -- token sequence" $$
"" $$
"-- | Tokens" $$
"data Tok = TK String | TP [([Prefix],Str)] Str | BIND | SOFT_BIND | SOFT_SPACE | CAPIT | ALL_CAPIT" $$
" deriving (Eq,Ord,Show)" $$
"" $$
"--- Standard definitions ---" $$
"linString (A.GString s) ="<+>pure "R_s [TK s]" $$
"linInt (A.GInt i) ="<+>pure "R_s [TK (show i)]" $$
@@ -99,319 +84,122 @@ concrete2haskell opts
where
pure = if va then brackets else pp
paramDef pd =
case pd of
ParamAliasDef p t -> H.Type (conap0 (gId p)) (convLinType t)
ParamDef p pvs -> Data (conap0 (gId p)) (map paramCon pvs) derive
where
paramCon (Param c cs) = ConAp (gId c) (map (tcon0.gId) cs)
derive = ["Eq","Ord","Show"]
convLinType = ppT
paramDef id pvs = Data (conap0 (gId id)) (map paramCon pvs) derive
where
ppT t =
case t of
FloatType -> tcon0 (identS "Float")
IntType -> tcon0 (identS "Int")
ParamType (ParamTypeId p) -> tcon0 (gId p)
RecordType rs -> tcon (rcon' ls) (map ppT ts)
where (ls,ts) = unzip $ sortOn fst [(l,t)|RecordRow l t<-rs]
StrType -> tcon0 (identS "Str")
TableType pt lt -> Fun (ppT pt) (ppT lt)
-- TupleType lts ->
paramCon (id,ctxt) = ConAp (gId id) [tcon0 (gId cat) | (_,_,QC (_,cat)) <- ctxt]
derive = ["Eq","Ord","Show"]
lincatDef (LincatDef c t) = tsyn0 (lincatName c) (convLinType t)
convLinType (Sort s)
| s == cStr = tcon0 (identS "Str")
convLinType (QC (_,p)) = tcon0 (gId p)
convLinType (RecType lbls) = tcon (rcon' ls) (map convLinType ts)
where (ls,ts) = unzip $ sortOn fst lbls
convLinType (Table pt lt) = Fun (convLinType pt) (convLinType lt)
linfuncats = S.fromList linfuncatl
(linfuncatl,lindefs) = unzip (linDefs lns)
lincatDef c ty = tsyn0 (lincatName c) (convLinType ty)
linDefs = map eqn . sortOn fst . map linDef
where eqn (cat,(f,(ps,rhs))) = (cat,Eqn (f,ps) rhs)
lindefs =
Map.fromListWith (++)
[linDef id absctx cat lincat rhs |
(id,CncFun (Just (absctx,cat,_,lincat)) (Just (L _ rhs)) _ _) <- Map.toList (jments mi)]
linDef (LinDef f xs rhs0) =
(cat,(linfunName cat,(lhs,rhs)))
linDef f absctx cat lincat rhs0 =
(cat,[Eqn (linfunName cat,lhs) rhs'])
where
lhs = [ConP (aId f) (map VarP abs_args)]
aId f = prefixIdent "A." (gId f)
[lincat] = [lincat | LincatDef c lincat<-lcs,c==cat]
[C.Type absctx (TypeApp cat _)] = [t | FunDef f' t<-funs, f'==f]
--[C.Type absctx (TypeApp cat _)] = [t | FunDef f' t<-funs, f'==f]
(xs,rhs) = termFormCnc rhs0
abs_args = map abs_arg args
abs_arg = prefixIdent "abs_"
args = map (prefixIdent "g" . toIdent) xs
args = map (prefixIdent "g" . snd) xs
rhs = lets (zipWith letlin args absctx)
(convert vs (coerce env lincat rhs0))
rhs' = lets (zipWith letlin args absctx)
(convert rhs)
where
vs = [(VarValueId (Unqual x),a)|(VarId x,a)<-zip xs args]
env= [(VarValueId (Unqual x),lc)|(VarId x,lc)<-zip xs (map arglincat absctx)]
vs = [(x,a)|((_,x),a)<-zip xs args]
letlin a (TypeBinding _ (C.Type _ (TypeApp acat _))) =
letlin a acat =
(a,Ap (Var (linfunName acat)) (Var (abs_arg a)))
arglincat (TypeBinding _ (C.Type _ (TypeApp acat _))) = lincat
where
[lincat] = [lincat | LincatDef c lincat<-lcs,c==acat]
convert = convert' va
convert' va vs = ppT
convert (Vr v) = Var (gId v)
convert (EInt n) = lit n
convert (EFloat d) = lit d
convert (K s) = single (Const "TK" `Ap` lit s)
convert Empty = List []
convert (App t1 t2) = Ap (convert t1) (convert t2)
convert (R lbls) = aps (rcon ls) (map (convert.snd) ts)
where (ls,ts) = unzip (sortOn fst lbls)
convert (P t lbl) = ap (proj lbl) (convert t)
convert (ExtR t1 t2) = Const "ExtR" -- TODO
convert (T _ cs) = LambdaCase (map ppCase cs)
where
ppT0 = convert' False vs
ppTv vs' = convert' va vs'
ppCase (p,t) = (convertPatt p,convert t)
convert (V _ ts) = Const "V" -- TODO
convert (S t p)
| va = select_va (convert t) (convert p)
| otherwise = Ap (convert t) (convert p)
where
select_va (List [t]) (List [p]) = Op t "!" p
select_va (List [t]) p = Op t "!$" p
select_va t p = Op t "!*" p
convert (Q (_,id)) = single (Var id)
convert (QC (_,id)) = single (Var id)
convert (C t1 t2)
| va = concat_va (convert t1) (convert t2)
| otherwise = plusplus (convert t1) (convert t2)
where
concat_va (List [List ts1]) (List [List ts2]) = List [List (ts1++ts2)]
concat_va t1 t2 = Op t1 "+++" t2
convert (Glue t1 t2) = Const "Glue"
convert (FV ts)
| va = join (List (map convert ts))
| otherwise = case ts of
[] -> Const "error" `Ap` Const (show "empty variant")
(t:ts) -> convert t
where
join (List [x]) = x
join x = Const "concat" `Ap` x
convert (Alts def alts) = single (Const "TP" `Ap` List (map convAlt alts) `Ap` convert def)
where
convAlt (t1,t2) = Pair (convert t1) (convert t2)
convert (Strs ss) = List (map lit ss)
convert t = error (show t)
pure = if va then single else id
convertPatt (PC c ps) = ConP (gId c) (map convertPatt ps)
convertPatt (PP (_,c) ps) = ConP (gId c) (map convertPatt ps)
convertPatt (PV v) = VarP v
convertPatt PW = WildP
convertPatt (PR lbls) = ConP (rcon' ls) (map convertPatt ps)
where (ls,ps) = unzip $ sortOn fst lbls
convertPatt (PString s) = Lit s
convertPatt (PT _ p) = convertPatt p
convertPatt (PAs v p) = AsP v (convertPatt p)
convertPatt (PImplArg p) = convertPatt p
convertPatt (PTilde _) = WildP
convertPatt (PAlt _ _) = WildP -- TODO
convertPatt p = error (show p)
ppT t =
case t of
TableValue ty cs -> pure (table cs)
Selection t p -> select (ppT t) (ppT p)
ConcatValue t1 t2 -> concat (ppT t1) (ppT t2)
RecordValue r -> aps (rcon ls) (map ppT ts)
where (ls,ts) = unzip $ sortOn fst [(l,t)|RecordRow l t<-r]
PredefValue p -> single (Var (toIdent p)) -- hmm
Projection t l -> ap (proj l) (ppT t)
VariantValue [] -> empty
VariantValue ts@(_:_) -> variants ts
VarValue x -> maybe (Var (gId x)) (pure . Var) $ lookup x vs
PreValue vs t' -> pure (alts t' vs)
ParamConstant (Param c vs) -> aps (Var (pId c)) (map ppT vs)
ErrorValue s -> ap (Const "error") (Const (show s)) -- !!
LiteralValue l -> ppL l
_ -> error ("convert "++show t)
lit s = Const (show s) -- hmm
ppL l =
case l of
LFlt x -> pure (lit x)
LInt n -> pure (lit n)
LStr s -> pure (token s)
ap = if va then ap' else Ap
where
ap' (List [f]) x = fmap f x
ap' f x = Op f "<*>" x
fmap f (List [x]) = Ap f x
fmap f x = Op f "<$>" x
pId p@(ParamId s) =
if "to_R_" `isPrefixOf` unqual s then toIdent p else gId p -- !! a hack
table cs =
if all (null.patVars) ps
then lets ds (LambdaCase [(ppP p,t')|(p,t')<-zip ps ts'])
else LambdaCase (map ppCase cs)
where
(ds,ts') = dedup ts
(ps,ts) = unzip [(p,t)|TableRow p t<-cs]
ppCase (TableRow p t) = (ppP p,ppTv (patVars p++vs) t)
{-
ppPredef n =
case predef n of
Ok BIND -> single (c "BIND")
Ok SOFT_BIND -> single (c "SOFT_BIND")
Ok SOFT_SPACE -> single (c "SOFT_SPACE")
Ok CAPIT -> single (c "CAPIT")
Ok ALL_CAPIT -> single (c "ALL_CAPIT")
_ -> Var n
-}
ppP p =
case p of
ParamPattern (Param c ps) -> ConP (gId c) (map ppP ps)
RecordPattern r -> ConP (rcon' ls) (map ppP ps)
where (ls,ps) = unzip $ sortOn fst [(l,p)|RecordRow l p<-r]
WildPattern -> WildP
token s = single (c "TK" `Ap` lit s)
alts t' vs = single (c "TP" `Ap` List (map alt vs) `Ap` ppT0 t')
where
alt (s,t) = Pair (List (pre s)) (ppT0 t)
pre s = map lit s
c = Const
lit s = c (show s) -- hmm
concat = if va then concat' else plusplus
where
concat' (List [List ts1]) (List [List ts2]) = List [List (ts1++ts2)]
concat' t1 t2 = Op t1 "+++" t2
pure' = single -- forcing the list monad
select = if va then select' else Ap
select' (List [t]) (List [p]) = Op t "!" p
select' (List [t]) p = Op t "!$" p
select' t p = Op t "!*" p
ap = if va then ap' else Ap
where
ap' (List [f]) x = fmap f x
ap' f x = Op f "<*>" x
fmap f (List [x]) = pure' (Ap f x)
fmap f x = Op f "<$>" x
-- join = if va then join' else id
join' (List [x]) = x
join' x = c "concat" `Ap` x
empty = if va then List [] else c "error" `Ap` c (show "empty variant")
variants = if va then \ ts -> join' (List (map ppT ts))
else \ (t:_) -> ppT t
aps f [] = f
aps f (a:as) = aps (ap f a) as
dedup ts =
if M.null dups
then ([],map ppT ts)
else ([(ev i,ppT t)|(i,t)<-defs],zipWith entry ts is)
where
entry t i = maybe (ppT t) (Var . ev) (M.lookup i dups)
ev i = identS ("e'"++show i)
defs = [(i1,t)|(t,i1:_:_)<-ms]
dups = M.fromList [(i2,i1)|(_,i1:is@(_:_))<-ms,i2<-i1:is]
ms = M.toList m
m = fmap sort (M.fromListWith (++) (zip ts [[i]|i<-is]))
is = [0..]::[Int]
--con = Cn . identS
class Records t where
records :: t -> S.Set [LabelId]
instance Records t => Records [t] where
records = S.unions . map records
instance (Records t1,Records t2) => Records (t1,t2) where
records (t1,t2) = S.union (records t1) (records t2)
instance Records LincatDef where
records (LincatDef _ lt) = records lt
instance Records LinDef where
records (LinDef _ _ lv) = records lv
instance Records LinType where
records t =
case t of
RecordType r -> rowRecords r
TableType pt lt -> records (pt,lt)
TupleType ts -> records ts
_ -> S.empty
rowRecords r = S.insert (sort ls) (records ts)
where (ls,ts) = unzip [(l,t)|RecordRow l t<-r]
instance Records LinValue where
records v =
case v of
ConcatValue v1 v2 -> records (v1,v2)
ParamConstant (Param c vs) -> records vs
RecordValue r -> rowRecords r
TableValue t r -> records (t,r)
TupleValue vs -> records vs
VariantValue vs -> records vs
PreValue alts d -> records (map snd alts,d)
Projection v l -> records v
Selection v1 v2 -> records (v1,v2)
_ -> S.empty
instance Records rhs => Records (TableRow rhs) where
records (TableRow _ v) = records v
-- | Record subtyping is converted into explicit coercions in Haskell
coerce env ty t =
case (ty,t) of
(_,VariantValue ts) -> VariantValue (map (coerce env ty) ts)
(TableType ti tv,TableValue _ cs) ->
TableValue ti [TableRow p (coerce env tv t)|TableRow p t<-cs]
(RecordType rt,RecordValue r) ->
RecordValue [RecordRow l (coerce env ft f) |
RecordRow l f<-r,ft<-[ft | RecordRow l' ft <- rt, l'==l]]
(RecordType rt,VarValue x)->
case lookup x env of
Just ty' | ty'/=ty -> -- better to compare to normal form of ty'
--trace ("coerce "++render ty'++" to "++render ty) $
app (to_rcon rt) [t]
| otherwise -> t -- types match, no coercion needed
_ -> trace (render ("missing type to coerce"<+>x<+>"to"<+>render ty
$$ "in" <+> map fst env))
t
_ -> t
where
app f ts = ParamConstant (Param f ts) -- !! a hack
to_rcon = ParamId . Unqual . rawIdentS . to_rcon' . labels
patVars p = []
labels r = [l | RecordRow l _ <- r]
aps f [] = f
aps f (a:as) = aps (ap f a) as
proj = Var . identS . proj'
proj' (LabelId l) = "proj_" ++ showRawIdent l
proj' (LIdent l) = "proj_" ++ showRawIdent l
rcon = Var . rcon'
rcon' = identS . rcon_name
rcon_name ls = "R"++concat (sort ['_':showRawIdent l | LabelId l <- ls])
to_rcon' = ("to_"++) . rcon_name
rcon_name ls = "R"++concat (sort ['_':showRawIdent l | LIdent l <- ls])
recordType ls =
Data lhs [app] ["Eq","Ord","Show"]:
enumAllInstance:
zipWith projection vs ls ++
[Eqn (identS (to_rcon' ls),[VarP r])
(foldl Ap (Var cn) [Var (identS (proj' l)) `Ap` Var r|l<-ls])]
where
r = identS "r"
cn = rcon' ls
-- Not all record labels are syntactically correct as type variables in Haskell
-- app = cn<+>ls
lhs = ConAp cn vs -- don't reuse record labels
app = fmap TId lhs
tapp = foldl TAp (TId cn) (map TId vs)
vs = [identS ('t':show i)|i<-[1..n]]
n = length ls
projection v l = Instance [] (TId name `TAp` tapp `TAp` TId v)
[((prj,[papp]),Var v)]
where
name = identS ("Has_"++render l)
prj = identS (proj' l)
papp = ConP cn (map VarP vs)
enumAllInstance =
Instance ctx (tEnumAll `TAp` tapp)[(lhs0 "enumAll",enumCon cn n)]
where
ctx = [tEnumAll `TAp` TId v|v<-vs]
tEnumAll = TId (identS "EnumAll")
labelClass l =
Class [] (ConAp name [r,a]) [([r],[a])]
[(identS (proj' l),TId r `Fun` TId a)]
where
name = identS ("Has_"++render l)
r = identS "r"
a = identS "a"
enumCon name arity =
if arity==0
then single (Var name)
else foldl ap (single (Var name)) (replicate arity (Const "enumAll"))
where
ap (List [f]) a = Op f "<$>" a
ap f a = Op f "<*>" a
lincatName,linfunName :: CatId -> Ident
lincatName c = prefixIdent "Lin" (toIdent c)
linfunName c = prefixIdent "lin" (toIdent c)
class ToIdent i where toIdent :: i -> Ident
instance ToIdent ParamId where toIdent (ParamId q) = qIdentC q
instance ToIdent PredefId where toIdent (PredefId s) = identC s
instance ToIdent CatId where toIdent (CatId s) = identC s
instance ToIdent C.FunId where toIdent (FunId s) = identC s
instance ToIdent VarValueId where toIdent (VarValueId q) = qIdentC q
qIdentC = identS . unqual
unqual (Qual (ModId m) n) = showRawIdent m++"_"++ showRawIdent n
unqual (Unqual n) = showRawIdent n
instance ToIdent VarId where
toIdent Anonymous = identW
toIdent (VarId s) = identC s
lincatName,linfunName :: Ident -> Ident
lincatName c = prefixIdent "Lin" c
linfunName c = prefixIdent "lin" c

View File

@@ -2,422 +2,104 @@
-- (a common intermediate representation to simplify export to other formats)
module GF.Compile.GrammarToCanonical(
grammar2canonical,abstract2canonical,concretes2canonical,
projection,selection
) where
import Data.List(nub,partition)
import qualified Data.Map as M
import Data.Maybe(fromMaybe)
import qualified Data.Set as S
import GF.Data.ErrM
import GF.Text.Pretty
import GF.Grammar.Grammar as G
import GF.Grammar.Lookup(lookupOrigInfo,allOrigInfos,allParamValues)
import GF.Grammar.Macros(typeForm,collectOp,collectPattOp,composSafeOp,mkAbs,mkApp,term2patt,sortRec)
import GF.Grammar.Lockfield(isLockLabel)
import GF.Grammar.Predef(cPredef,cInts)
import GF.Infra.Ident(ModuleName(..),Ident,identW,ident2raw,rawIdentS,showIdent)
import GF.Infra.Option(Options,optionsPGF)
import GF.Infra.CheckM
import PGF2(Literal(..))
import GF.Compile.Compute.Concrete(normalForm,Globals(..),stdPredef)
import GF.Grammar.Canonical as C
import System.FilePath ((</>), (<.>))
import qualified Debug.Trace as T
import GF.Data.ErrM
import GF.Grammar.Grammar
import GF.Grammar.Lookup(allOrigInfos,lookupOrigInfo)
import GF.Infra.Option(Options,noOptions)
import GF.Infra.CheckM
import GF.Compile.Compute.Concrete2
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Maybe(mapMaybe)
import Control.Monad (forM)
-- | Generate Canonical code for the named abstract syntax and all associated
-- concrete syntaxes
grammar2canonical :: Options -> ModuleName -> G.Grammar -> Check C.Grammar
grammar2canonical :: Options -> ModuleName -> Grammar -> Check Grammar
grammar2canonical opts absname gr = do
abs <- abstract2canonical absname gr
cncs <- concretes2canonical opts absname gr
return (Grammar abs (map snd cncs))
return (mGrammar (abs:cncs))
-- | Generate Canonical code for the named abstract syntax
abstract2canonical :: ModuleName -> G.Grammar -> Check Abstract
abstract2canonical absname gr =
return (Abstract (modId absname) (convFlags gr absname) cats funs)
where
cats = [CatDef (gId c) (convCtx ctx) | ((_,c),AbsCat ctx) <- adefs]
funs = [FunDef (gId f) (convType ty) |
((_,f),AbsFun (Just (L _ ty)) ma mdef _) <- adefs]
adefs = allOrigInfos gr absname
convCtx = maybe [] (map convHypo . unLoc)
convHypo (bt,name,t) =
case typeForm t of
([],(_,cat),[]) -> gId cat -- !!
tf -> error ("abstract2canonical convHypo: " ++ show tf)
convType t =
case typeForm t of
(hyps,(_,cat),args) -> Type bs (TypeApp (gId cat) as)
where
bs = map convHypo' hyps
as = map convType args
convHypo' (bt,name,t) = TypeBinding (gId name) (convType t)
abstract2canonical :: ModuleName -> Grammar -> Check Module
abstract2canonical absname gr = do
let infos = [(id,info) | ((mn,id),info) <- allOrigInfos gr absname]
return (absname, ModInfo {
mtype = MTAbstract,
mstatus = MSComplete,
mflags = convFlags gr absname,
mextend = [],
mwith = Nothing,
mopens = [],
mexdeps = [],
msrc = "",
mseqs = Nothing,
jments = Map.fromList infos
})
-- | Generate Canonical code for the all concrete syntaxes associated with
-- the named abstract syntax in given the grammar.
concretes2canonical :: Options -> ModuleName -> G.Grammar -> Check [(FilePath, Concrete)]
concretes2canonical :: Options -> ModuleName -> Grammar -> Check [Module]
concretes2canonical opts absname gr =
sequence
[fmap ((,) cncname) (concrete2canonical gr absname cnc cncmod)
[concrete2canonical gr absname cnc modinfo
| cnc<-allConcretes gr absname,
let cncname = "canonical" </> render cnc <.> "gf"
Ok cncmod = lookupModule gr cnc
let Ok modinfo = lookupModule gr cnc
]
-- | Generate Canonical GF for the given concrete module.
concrete2canonical :: G.Grammar -> ModuleName -> ModuleName -> ModuleInfo -> Check Concrete
concrete2canonical gr absname cnc modinfo = do
defs <- fmap concat $ mapM (toCanonical gr absname) (M.toList (jments modinfo))
return (Concrete (modId cnc) (modId absname) (convFlags gr cnc)
(neededParamTypes S.empty (params defs))
[lincat | (_,Left lincat) <- defs]
[lin | (_,Right lin) <- defs])
concrete2canonical :: Grammar -> ModuleName -> ModuleName -> ModuleInfo -> Check Module
concrete2canonical gr absname cncname modinfo = do
let g = Gl gr (stdPredef g)
infos <- mapM (convInfo g) (allOrigInfos gr cncname)
let pts = Set.unions (map fst infos)
pts <- closure pts (Set.toList pts)
return (cncname, ModInfo {
mtype = MTConcrete absname,
mstatus = MSComplete,
mflags = convFlags gr cncname,
mextend = [],
mwith = Nothing,
mopens = [],
mexdeps = [],
msrc = "",
mseqs = Nothing,
jments = Map.union (Map.fromList (mapMaybe snd infos))
pts
})
where
params = S.toList . S.unions . map fst
convInfo g ((mn,id), CncCat (Just (L loc typ)) lindef linref pprn mb_prods) = do
typ <- normalForm g typ
let pts = paramTypes typ
return (pts,Just (id,CncCat (Just (L loc typ)) lindef linref pprn mb_prods))
convInfo g ((mn,id), CncFun mb_ty@(Just r@(_,cat,ctx,lincat)) (Just (L loc def)) pprn mb_prods) = do
def <- normalForm g (eta_expand def ctx)
return (Set.empty,Just (id,CncFun mb_ty (Just (L loc def)) pprn mb_prods))
convInfo g _ = return (Set.empty,Nothing)
neededParamTypes have [] = []
neededParamTypes have (q:qs) =
if q `S.member` have
then neededParamTypes have qs
else let ((got,need),def) = paramType gr q
in def++neededParamTypes (S.union got have) (S.toList need++qs)
eta_expand t [] = t
eta_expand t ((Implicit,x,_):ctx) = Abs Implicit x (eta_expand (App t (ImplArg (Vr x))) ctx)
eta_expand t ((Explicit,x,_):ctx) = Abs Explicit x (eta_expand (App t (Vr x)) ctx)
-- toCanonical :: G.Grammar -> ModuleName -> (Ident, Info) -> [(S.Set QIdent, Either LincatDef LinDef)]
toCanonical gr absname (name,jment) =
case jment of
CncCat (Just (L loc typ)) _ _ pprn _ -> do
ntyp <- normalForm (Gl gr stdPredef) typ
let pts = paramTypes gr ntyp
return [(pts,Left (LincatDef (gId name) (convType ntyp)))]
CncFun (Just r@(_,cat,ctx,lincat)) (Just (L loc def)) pprn _ -> do
let params = [(b,x)|(b,x,_)<-ctx]
args = map snd params
e0 <- normalForm (Gl gr stdPredef) (mkAbs params (mkApp def (map Vr args)))
let e = cleanupRecordFields lincat (unAbs (length params) e0)
tts = tableTypes gr [e]
return [(tts,Right (LinDef (gId name) (map gId args) (convert gr e)))]
AnyInd _ m -> case lookupOrigInfo gr (m,name) of
Ok (m,jment) -> toCanonical gr absname (name,jment)
_ -> return []
_ -> return []
where
unAbs 0 t = t
unAbs n (Abs _ _ t) = unAbs (n-1) t
unAbs _ t = t
paramTypes (RecType fs) = Set.unions (map (paramTypes.snd) fs)
paramTypes (Table t1 t2) = Set.union (paramTypes t1) (paramTypes t2)
paramTypes (App tf ta) = Set.union (paramTypes tf) (paramTypes ta)
paramTypes (Sort _) = Set.empty
paramTypes (EInt _) = Set.empty
paramTypes (QC q) = Set.singleton q
paramTypes (FV ts) = Set.unions (map paramTypes ts)
paramTypes _ = Set.empty
tableTypes :: G.Grammar -> [Term] -> S.Set QIdent
tableTypes gr ts = S.unions (map tabtys ts)
where
tabtys t =
case t of
V t cc -> S.union (paramTypes gr t) (tableTypes gr cc)
T (TTyped t) cs -> S.union (paramTypes gr t) (tableTypes gr (map snd cs))
_ -> collectOp tabtys t
closure pts [] = return Map.empty
closure pts (q@(_,id):qs) = do
(_,info@(ResParam (Just (L _ ps)) _)) <- lookupOrigInfo gr q
let pts' = Set.unions [paramTypes ty | (_,ctx) <- ps, (_,_,ty) <- ctx]
new_pts = Set.difference pts' pts
infos <- closure (Set.union new_pts pts) (Set.toList new_pts++qs)
return (Map.insert id info infos)
paramTypes :: G.Grammar -> G.Type -> S.Set QIdent
paramTypes gr t =
case t of
RecType fs -> S.unions (map (paramTypes gr.snd) fs)
Table t1 t2 -> S.union (paramTypes gr t1) (paramTypes gr t2)
App tf ta -> S.union (paramTypes gr tf) (paramTypes gr ta)
Sort _ -> S.empty
EInt _ -> S.empty
Q q -> lookup q
QC q -> lookup q
FV ts -> S.unions (map (paramTypes gr) ts)
_ -> ignore
where
lookup q = case lookupOrigInfo gr q of
Ok (_,ResOper _ (Just (L _ t))) ->
S.insert q (paramTypes gr t)
Ok (_,ResParam {}) -> S.singleton q
_ -> ignore
ignore = T.trace ("Ignore: " ++ show t) S.empty
-- | Filter out record fields from definitions which don't appear in lincat.
cleanupRecordFields :: G.Type -> Term -> Term
cleanupRecordFields (RecType ls) (R as) =
let defnFields = M.fromList ls
in R
[ (lbl, (mty, t'))
| (lbl, (mty, t)) <- as
, M.member lbl defnFields
, let Just ty = M.lookup lbl defnFields
, let t' = cleanupRecordFields ty t
]
cleanupRecordFields ty t@(FV _) = composSafeOp (cleanupRecordFields ty) t
cleanupRecordFields _ t = t
convert :: G.Grammar -> Term -> LinValue
convert gr = convert' gr []
convert' :: G.Grammar -> [Ident] -> Term -> LinValue
convert' gr vs = ppT
where
ppT0 = convert' gr vs
ppTv vs' = convert' gr vs'
ppT t =
case t of
-- Abs b x t -> ...
-- V ty ts -> VTableValue (convType ty) (map ppT ts)
V ty ts -> TableValue (convType ty) [TableRow (ppP p) (ppT t)|(p,t)<-zip ps ts]
where
Ok pts = allParamValues gr ty
Ok ps = mapM term2patt pts
T (TTyped ty) cs -> TableValue (convType ty) (map ppCase cs)
S t p -> selection (ppT t) (ppT p)
C t1 t2 -> concatValue (ppT t1) (ppT t2)
App f a -> ap (ppT f) (ppT a)
R r -> RecordValue (fields (sortRec r))
P t l -> projection (ppT t) (lblId l)
Vr x -> VarValue (gId x)
Cn x -> VarValue (gId x) -- hmm
Con c -> ParamConstant (Param (gId c) [])
Sort k -> VarValue (gId k)
EInt n -> LiteralValue (LInt n)
Q (m,n) -> if m==cPredef then ppPredef n else VarValue (gQId m n)
QC (m,n) -> ParamConstant (Param (gQId m n) [])
K s -> LiteralValue (LStr s)
Empty -> LiteralValue (LStr "")
FV ts -> VariantValue (map ppT ts)
Alts t' vs -> alts vs (ppT t')
_ -> error $ "convert' ppT: " ++ show t
ppCase (p,t) = TableRow (ppP p) (ppTv (patVars p++vs) t)
ppPredef n = error "TODO: ppPredef" {-
case predef n of
Ok BIND -> p "BIND"
Ok SOFT_BIND -> p "SOFT_BIND"
Ok SOFT_SPACE -> p "SOFT_SPACE"
Ok CAPIT -> p "CAPIT"
Ok ALL_CAPIT -> p "ALL_CAPIT"
_ -> VarValue (gQId cPredef n) -- hmm
where
p = PredefValue . PredefId . rawIdentS
-}
ppP p =
case p of
PC c ps -> ParamPattern (Param (gId c) (map ppP ps))
PP (m,c) ps -> ParamPattern (Param (gQId m c) (map ppP ps))
PR r -> RecordPattern (fields r) {-
PW -> WildPattern
PV x -> VarP x
PString s -> Lit (show s) -- !!
PInt i -> Lit (show i)
PFloat x -> Lit (show x)
PT _ p -> ppP p
PAs x p -> AsP x (ppP p) -}
_ -> error $ "convert' ppP: " ++ show p
where
fields = map field . filter (not.isLockLabel.fst)
field (l,p) = RecordRow (lblId l) (ppP p)
-- patToParam p = case ppP p of ParamPattern pv -> pv
-- token s = single (c "TK" `Ap` lit s)
alts vs = PreValue (map alt vs)
where
alt (t,p) = (pre p,ppT0 t)
pre (K s) = [s]
pre Empty = [""] -- Empty == K ""
pre (Strs ts) = concatMap pre ts
pre (EPatt _ _ p) = pat p
pre t = error $ "convert' alts pre: " ++ show t
pat (PString s) = [s]
pat (PAlt p1 p2) = pat p1++pat p2
pat (PSeq _ _ p1 _ _ p2) = [s1++s2 | s1<-pat p1, s2<-pat p2]
pat p = error $ "convert' alts pat: "++show p
fields = map field . filter (not.isLockLabel.fst)
field (l,(_,t)) = RecordRow (lblId l) (ppT t)
--c = Const
--c = VarValue . VarValueId
--lit s = c (show s) -- hmm
ap f a = case f of
ParamConstant (Param p ps) ->
ParamConstant (Param p (ps++[a]))
_ -> error $ "convert' ap: "++render (ppA f <+> ppA a)
concatValue :: LinValue -> LinValue -> LinValue
concatValue v1 v2 =
case (v1,v2) of
(LiteralValue (LStr ""),_) -> v2
(_,LiteralValue (LStr "")) -> v1
_ -> ConcatValue v1 v2
-- | Smart constructor for projections
projection :: LinValue -> LabelId -> LinValue
projection r l = fromMaybe (Projection r l) (proj r l)
proj :: LinValue -> LabelId -> Maybe LinValue
proj r l =
case r of
RecordValue r -> case [v | RecordRow l' v <- r, l'==l] of
[v] -> Just v
_ -> Nothing
_ -> Nothing
-- | Smart constructor for selections
selection :: LinValue -> LinValue -> LinValue
selection t v =
-- Note: impossible cases can become possible after grammar transformation
case t of
TableValue tt r ->
case nub [rv | TableRow _ rv <- keep] of
[rv] -> rv
_ -> Selection (TableValue tt r') v
where
-- Don't introduce wildcard patterns, true to the canonical format,
-- annotate (or eliminate) rhs in impossible rows
r' = map trunc r
trunc r@(TableRow p e) = if mightMatchRow v r
then r
else TableRow p (impossible e)
{-
-- Creates smaller tables, but introduces wildcard patterns
r' = if null discard
then r
else keep++[TableRow WildPattern impossible]
-}
(keep,discard) = partition (mightMatchRow v) r
_ -> Selection t v
impossible :: LinValue -> LinValue
impossible = CommentedValue "impossible"
mightMatchRow :: LinValue -> TableRow rhs -> Bool
mightMatchRow v (TableRow p _) =
case p of
WildPattern -> True
_ -> mightMatch v p
mightMatch :: LinValue -> LinPattern -> Bool
mightMatch v p =
case v of
ConcatValue _ _ -> False
ParamConstant (Param c1 pvs) ->
case p of
ParamPattern (Param c2 pps) -> c1==c2 && length pvs==length pps &&
and [mightMatch v p|(v,p)<-zip pvs pps]
_ -> False
RecordValue rv ->
case p of
RecordPattern rp ->
and [maybe False (`mightMatch` p) (proj v l) | RecordRow l p<-rp]
_ -> False
_ -> True
patVars :: Patt -> [Ident]
patVars p =
case p of
PV x -> [x]
PAs x p -> x:patVars p
_ -> collectPattOp patVars p
convType :: Term -> LinType
convType = ppT
where
ppT t =
case t of
Table ti tv -> TableType (ppT ti) (ppT tv)
RecType rt -> RecordType (convFields rt)
-- App tf ta -> TAp (ppT tf) (ppT ta)
-- FV [] -> tcon0 (identS "({-empty variant-})")
Sort k -> convSort k
-- EInt n -> tcon0 (identS ("({-"++show n++"-})")) -- type level numeric literal
FV (t:ts) -> ppT t -- !!
QC (m,n) -> ParamType (ParamTypeId (gQId m n))
Q (m,n) -> ParamType (ParamTypeId (gQId m n))
_ -> error $ "convType ppT: " ++ show t
convFields = map convField . filter (not.isLockLabel.fst)
convField (l,r) = RecordRow (lblId l) (ppT r)
convSort k = case showIdent k of
"Float" -> FloatType
"Int" -> IntType
"Str" -> StrType
_ -> error $ "convType convSort: " ++ show k
toParamType :: Term -> ParamType
toParamType t = case convType t of
ParamType pt -> pt
_ -> error $ "toParamType: " ++ show t
toParamId :: Term -> ParamId
toParamId t = case toParamType t of
ParamTypeId p -> p
paramType :: G.Grammar
-> (ModuleName, Ident)
-> ((S.Set (ModuleName, Ident), S.Set QIdent), [ParamDef])
paramType gr q@(_,n) =
case lookupOrigInfo gr q of
Ok (m,ResParam (Just (L _ ps)) _)
{- - | m/=cPredef && m/=moduleNameS "Prelude"-} ->
((S.singleton (m,n),argTypes ps),
[ParamDef name (map (param m) ps)]
)
where name = gQId m n
Ok (m,ResOper _ (Just (L _ t)))
| m==cPredef && n==cInts ->
((S.empty,S.empty),[]) {-
((S.singleton (m,n),S.empty),
[Type (ConAp ((gQId m n)) [identS "n"]) (TId (identS "Int"))])-}
| otherwise ->
((S.singleton (m,n),paramTypes gr t),
[ParamAliasDef (gQId m n) (convType t)])
_ -> ((S.empty,S.empty),[])
where
param m (n,ctx) = Param (gQId m n) [toParamId t|(_,_,t)<-ctx]
argTypes = S.unions . map argTypes1
argTypes1 (n,ctx) = S.unions [paramTypes gr t|(_,_,t)<-ctx]
lblId :: Label -> C.LabelId
lblId (LIdent ri) = LabelId ri
lblId (LVar i) = LabelId (rawIdentS (show i)) -- hmm
modId :: ModuleName -> C.ModId
modId (MN m) = ModId (ident2raw m)
class FromIdent i where
gId :: Ident -> i
instance FromIdent VarId where
gId i = if i == identW then Anonymous else VarId (ident2raw i)
instance FromIdent C.FunId where gId = C.FunId . ident2raw
instance FromIdent CatId where gId = CatId . ident2raw
instance FromIdent ParamId where gId = ParamId . unqual
instance FromIdent VarValueId where gId = VarValueId . unqual
class FromIdent i => QualIdent i where
gQId :: ModuleName -> Ident -> i
instance QualIdent ParamId where gQId m n = ParamId (qual m n)
instance QualIdent VarValueId where gQId m n = VarValueId (qual m n)
qual :: ModuleName -> Ident -> QualId
qual m n = Qual (modId m) (ident2raw n)
unqual :: Ident -> QualId
unqual n = Unqual (ident2raw n)
convFlags :: G.Grammar -> ModuleName -> Flags
convFlags gr mn =
Flags [(rawIdentS n,v) |
(n,v)<-err (const []) (optionsPGF.mflags) (lookupModule gr mn)]
convFlags :: Grammar -> ModuleName -> Options
convFlags gr mn = err (const noOptions) mflags (lookupModule gr mn)

View File

@@ -39,7 +39,6 @@ grammar2haskell opts name gr = foldr (++++) [] $
where gr' = hSkeleton gr
gadt = haskellOption opts HaskellGADT
dataExt = haskellOption opts HaskellData
pgf2 = haskellOption opts HaskellPGF2
lexical cat = haskellOption opts HaskellLexical && isLexicalCat opts cat
gId | haskellOption opts HaskellNoPrefix = rmForbiddenChars
| otherwise = ("G"++) . rmForbiddenChars
@@ -54,8 +53,7 @@ grammar2haskell opts name gr = foldr (++++) [] $
extraImports | gadt = ["import Control.Monad.Identity", "import Data.Monoid"]
| dataExt = ["import Data.Data"]
| otherwise = []
pgfImports | pgf2 = ["import PGF2 hiding (Tree)", "", "showCId :: CId -> String", "showCId = id"]
| otherwise = ["import PGF hiding (Tree)"]
pgfImports = ["import PGF2", ""]
types | gadt = datatypesGADT gId lexical gr'
| otherwise = datatypes gId derivingClause lexical gr'
compos | gadt = prCompos gId lexical gr' ++ composClass
@@ -78,7 +76,7 @@ haskPreamble gadt name derivingClause imports =
"",
predefInst gadt derivingClause "GString" "String" "unStr" "mkStr",
"",
predefInst gadt derivingClause "GInt" "Int" "unInt" "mkInt",
predefInst gadt derivingClause "GInt" "Integer" "unInt" "mkInt",
"",
predefInst gadt derivingClause "GFloat" "Double" "unFloat" "mkFloat",
"",
@@ -234,14 +232,14 @@ hInstance gId lexical m (cat,rules)
| otherwise =
"instance Gf" +++ gId cat +++ "where\n" ++
unlines ([mkInst f xx | (f,xx) <- nonLexicalRules (lexical cat) rules]
++ if lexical cat then [" gf (" ++ lexicalConstructor cat +++ "x) = mkApp (mkCId x) []"] else [])
++ if lexical cat then [" gf (" ++ lexicalConstructor cat +++ "x) = mkApp x []"] else [])
where
ec = elemCat cat
baseVars = mkVars (baseSize (cat,rules))
mkInst f xx = let xx' = mkVars (length xx) in " gf " ++
(if null xx then gId f else prParenth (gId f +++ foldr1 (+++) xx')) +++
"=" +++ mkRHS f xx'
mkRHS f vars = "mkApp (mkCId \"" ++ f ++ "\")" +++
mkRHS f vars = "mkApp \"" ++ f ++ "\"" +++
"[" ++ prTList ", " ["gf" +++ x | x <- vars] ++ "]"
mkVars :: Int -> [String]
@@ -265,7 +263,7 @@ fInstance gId lexical m (cat,rules) =
mkInst f xx =
" Just (i," ++
"[" ++ prTList "," xx' ++ "])" +++
"| i == mkCId \"" ++ f ++ "\" ->" +++ mkRHS f xx'
"| i == \"" ++ f ++ "\" ->" +++ mkRHS f xx'
where
xx' = ["x" ++ show i | (_,i) <- zip xx [1..]]
mkRHS f vars