mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-23 03:32:51 -06:00
Translating linearization functions to Haskell: support for variants
By adding the flag -haskell=variants to the command line, GF will now generate
linearization functions in Haskell that support variants. Variants are
represented as lists in Haskell.
Variants inside pre { ... } expressions are still ignored.
TODO: apply some monad laws to generate more compact code (using an
intermediate representation of the generated Haskell code, instead of
pretty printing directly from the GF code).
This commit is contained in:
@@ -8,7 +8,7 @@ import GF.Data.Utilities(mapSnd)
|
|||||||
import GF.Text.Pretty
|
import GF.Text.Pretty
|
||||||
import GF.Grammar.Grammar
|
import GF.Grammar.Grammar
|
||||||
import GF.Grammar.Lookup(lookupFunType,lookupOrigInfo,allOrigInfos)--,allParamValues
|
import GF.Grammar.Lookup(lookupFunType,lookupOrigInfo,allOrigInfos)--,allParamValues
|
||||||
import GF.Grammar.Macros(typeForm,collectOp,mkAbs,mkApp)
|
import GF.Grammar.Macros(typeForm,collectOp,collectPattOp,mkAbs,mkApp)
|
||||||
import GF.Grammar.Lockfield(isLockLabel)
|
import GF.Grammar.Lockfield(isLockLabel)
|
||||||
import GF.Grammar.Predef(cPredef,cInts)
|
import GF.Grammar.Predef(cPredef,cInts)
|
||||||
import GF.Compile.Compute.Predef(predef)
|
import GF.Compile.Compute.Predef(predef)
|
||||||
@@ -28,7 +28,7 @@ concretes2haskell opts absname gr =
|
|||||||
|
|
||||||
concrete2haskell opts gr cenv absname cnc modinfo =
|
concrete2haskell opts gr cenv absname cnc modinfo =
|
||||||
render $
|
render $
|
||||||
haskPreamble absname cnc $$ "" $$
|
haskPreamble va absname cnc $$ "" $$
|
||||||
"--- Parameter types ---" $$
|
"--- Parameter types ---" $$
|
||||||
vcat (neededParamTypes S.empty (params defs)) $$ "" $$
|
vcat (neededParamTypes S.empty (params defs)) $$ "" $$
|
||||||
"--- Type signatures for linearization functions ---" $$
|
"--- Type signatures for linearization functions ---" $$
|
||||||
@@ -54,7 +54,9 @@ concrete2haskell opts gr cenv absname cnc modinfo =
|
|||||||
M.toList $
|
M.toList $
|
||||||
jments modinfo
|
jments modinfo
|
||||||
|
|
||||||
signature c = "lin"<>c<+>"::"<+>"A."<>gId c<+>"->"<+>"Lin"<>c
|
-- signature c = "lin"<>c<+>"::"<+>"A."<>gId c<+>"->"<+>"Lin"<>c
|
||||||
|
-- signature c = "--lin"<>c<+>":: (Applicative f,Monad f) =>"<+>"A."<>gId c<+>"->"<+>"f Lin"<>c
|
||||||
|
signature c = "lin"<>c<+>"::"<+>"A."<>gId c<+>"->"<+>pure ("Lin"<>c)
|
||||||
|
|
||||||
emptydefs = map emptydef (S.toList emptyCats)
|
emptydefs = map emptydef (S.toList emptyCats)
|
||||||
emptydef c = "lin"<>c<+>"_"<+>"="<+>"undefined"
|
emptydef c = "lin"<>c<+>"_"<+>"="<+>"undefined"
|
||||||
@@ -67,38 +69,44 @@ concrete2haskell opts gr cenv absname cnc modinfo =
|
|||||||
params1 (Nothing,(_,rhs)) = paramTypes gr rhs
|
params1 (Nothing,(_,rhs)) = paramTypes gr rhs
|
||||||
params1 (_,(_,rhs)) = tableTypes gr [rhs]
|
params1 (_,(_,rhs)) = tableTypes gr [rhs]
|
||||||
|
|
||||||
ppDef (Nothing,(lhs,rhs)) = hang (lhs<+>"=") 4 (convType gId rhs)
|
ppDef (Nothing,(lhs,rhs)) = hang (lhs<+>"=") 4 (convType va gId rhs)
|
||||||
ppDef (_,(lhs,rhs)) = hang (lhs<+>"=") 4 (convert gId gr rhs)
|
ppDef (_,(lhs,rhs)) = hang (lhs<+>"=") 4 (convert va gId gr rhs)
|
||||||
|
|
||||||
gId :: Ident -> Doc
|
gId :: Ident -> Doc
|
||||||
gId = if haskellOption opts HaskellNoPrefix then pp else ("G"<>).pp
|
gId = if haskellOption opts HaskellNoPrefix then pp else ("G"<>).pp
|
||||||
|
va = haskellOption opts HaskellVariants
|
||||||
|
pure = if va then brackets else pp
|
||||||
|
|
||||||
neededParamTypes have [] = []
|
neededParamTypes have [] = []
|
||||||
neededParamTypes have (q:qs) =
|
neededParamTypes have (q:qs) =
|
||||||
if q `S.member` have
|
if q `S.member` have
|
||||||
then neededParamTypes have qs
|
then neededParamTypes have qs
|
||||||
else let ((got,need),def) = paramType gId gr q
|
else let ((got,need),def) = paramType va gId gr q
|
||||||
in def:neededParamTypes (S.union got have) (S.toList need++qs)
|
in def:neededParamTypes (S.union got have) (S.toList need++qs)
|
||||||
|
|
||||||
haskPreamble :: ModuleName -> ModuleName -> Doc
|
haskPreamble :: Bool -> ModuleName -> ModuleName -> Doc
|
||||||
haskPreamble absname cncname =
|
haskPreamble va absname cncname =
|
||||||
"{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, LambdaCase #-}" $$
|
"{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, LambdaCase #-}" $$
|
||||||
"module" <+> cncname <+> "where" $$
|
"module" <+> cncname <+> "where" $$
|
||||||
"import Prelude hiding (Ordering(..))" $$
|
"import Prelude hiding (Ordering(..))" $$
|
||||||
"import Control.Applicative((<$>),(<*>))" $$
|
"import Control.Applicative(Applicative,pure,empty,(<$>),(<*>))" $$
|
||||||
|
--"import Data.Foldable(asum)" $$
|
||||||
|
--"import Control.Monad(join)" $$
|
||||||
"import qualified Data.Map as M" $$
|
"import qualified Data.Map as M" $$
|
||||||
"import Data.Map((!))" $$
|
"import Data.Map((!))" $$
|
||||||
"import PGF.Haskell" $$
|
"import PGF.Haskell" $$
|
||||||
"import qualified" <+> absname <+> "as A" $$
|
"import qualified" <+> absname <+> "as A" $$
|
||||||
"" $$
|
"" $$
|
||||||
"--- Standard definitions ---" $$
|
"--- Standard definitions ---" $$
|
||||||
"linString (A.GString s) = R_s [TK s]" $$
|
"linString (A.GString s) ="<+>pure "R_s [TK s]" $$
|
||||||
"linInt (A.GInt i) = R_s [TK (show i)]" $$
|
"linInt (A.GInt i) ="<+>pure "R_s [TK (show i)]" $$
|
||||||
"linFloat (A.GFloat x) = R_s [TK (show x)]" $$
|
"linFloat (A.GFloat x) ="<+>pure "R_s [TK (show x)]" $$
|
||||||
"" $$
|
"" $$
|
||||||
"----------------------------------------------------" $$
|
"----------------------------------------------------" $$
|
||||||
"-- Automatic translation from GF to Haskell follows" $$
|
"-- Automatic translation from GF to Haskell follows" $$
|
||||||
"----------------------------------------------------"
|
"----------------------------------------------------"
|
||||||
|
where
|
||||||
|
pure = if va then brackets else pp
|
||||||
|
|
||||||
toHaskell gId gr absname cenv (name,jment) =
|
toHaskell gId gr absname cenv (name,jment) =
|
||||||
case jment of
|
case jment of
|
||||||
@@ -195,32 +203,36 @@ coerce env ty t =
|
|||||||
extend env (x,(Just ty,rhs)) = (x,ty):env
|
extend env (x,(Just ty,rhs)) = (x,ty):env
|
||||||
extend env _ = env
|
extend env _ = env
|
||||||
|
|
||||||
convert gId = convert' False gId
|
convert va gId = convert' False va gId []
|
||||||
convertA gId = convert' True gId
|
convertA va gId = convert' True va gId []
|
||||||
|
|
||||||
convert' atomic gId gr = if atomic then ppA else ppT
|
convert' atomic va gId vs gr = if atomic then ppA else ppT
|
||||||
where
|
where
|
||||||
|
ppT0 = convert' False False gId vs gr
|
||||||
|
ppA0 = convert' True False gId vs gr
|
||||||
|
ppTv vs' = convert' atomic va gId vs' gr
|
||||||
|
|
||||||
ppT = ppT' False
|
ppT = ppT' False
|
||||||
ppT' loop t =
|
ppT' loop t =
|
||||||
case t of
|
case t of
|
||||||
Let (x,(_,xt)) t -> sep ["let"<+>x<+>"="<+>ppT xt,"in"<+>ppT t]
|
Let (x,(_,xt)) t -> sep ["let"<+>x<+>"="<+>ppT0 xt,"in"<+>ppT t]
|
||||||
Abs b x t -> "\\"<+>x<+>"->"<+>ppT t
|
-- Abs b x t -> "\\"<+>x<+>"->"<+>ppT t
|
||||||
-- V ty ts -> hang "table" 4 (sep [list (enumAll ty),list ts])
|
-- V ty ts -> hang "table" 4 (sep [list (enumAll ty),list ts])
|
||||||
V ty ts -> hang "table" 4 (dedup ts)
|
V ty ts -> pure (hang "table" 4 (dedup ts))
|
||||||
T (TTyped ty) cs -> hang "\\case" 2 (vcat (map ppCase cs))
|
T (TTyped ty) cs -> pure (hang "\\case" 2 (vcat (map ppCase cs)))
|
||||||
S t p -> hang (ppB t) 4 (ppA p)
|
S t p -> join (ap t p)
|
||||||
C t1 t2 -> hang (ppA t1<+>"++") 4 (ppA t2)
|
C t1 t2 -> hang (ppA t1<+>concat) 4 (ppA t2)
|
||||||
_ -> ppB' loop t
|
_ -> ppB' loop t
|
||||||
|
|
||||||
ppCase (p,t) = hang (ppP p <+> "->") 4 (ppT t)
|
ppCase (p,t) = hang (ppP p <+> "->") 4 (ppTv (patVars p++vs) t)
|
||||||
|
|
||||||
ppB = ppB' False
|
ppB = ppB' False
|
||||||
ppB' loop t =
|
ppB' loop t =
|
||||||
case t of
|
case t of
|
||||||
App f a -> ppB f<+>ppA a
|
App f a -> ap f a
|
||||||
R r -> rcon (map fst r)<+>fsep (fields r)
|
R r -> aps (ppA (rcon (map fst r))) (fields r)
|
||||||
P t l -> ppB (proj l)<+>ppA t
|
P t l -> ap (proj l) t
|
||||||
FV [] -> "error"<+>doubleQuotes "empty variant"
|
FV [] -> empty
|
||||||
_ -> ppA' loop t
|
_ -> ppA' loop t
|
||||||
|
|
||||||
ppA = ppA' False
|
ppA = ppA' False
|
||||||
@@ -228,19 +240,19 @@ convert' atomic gId gr = if atomic then ppA else ppT
|
|||||||
ppA' True t = error $ "Missing case in convert': "++show t
|
ppA' True t = error $ "Missing case in convert': "++show t
|
||||||
ppA' loop t =
|
ppA' loop t =
|
||||||
case t of
|
case t of
|
||||||
Vr x -> pp x
|
Vr x -> if x `elem` vs then pureA (pp x) else pp x
|
||||||
Cn x -> pp x
|
Cn x -> pureA (pp x)
|
||||||
Con c -> gId c
|
Con c -> pureA (gId c)
|
||||||
Sort k -> pp k
|
Sort k -> pureA (pp k)
|
||||||
EInt n -> pp n
|
EInt n -> pureA (pp n)
|
||||||
Q (m,n) -> if m==cPredef
|
Q (m,n) -> if m==cPredef
|
||||||
then ppPredef n
|
then pureA (ppPredef n)
|
||||||
else pp (qual m n)
|
else pp (qual m n)
|
||||||
QC (m,n) -> gId (qual m n)
|
QC (m,n) -> pureA (gId (qual m n))
|
||||||
K s -> token s
|
K s -> pureA (token s)
|
||||||
Empty -> pp "[]"
|
Empty -> pureA (pp "[]")
|
||||||
FV (t:ts) -> "{-variants-}"<>ppA t -- !!
|
FV ts@(_:_) -> variants ts
|
||||||
Alts t' vs -> alts t' vs
|
Alts t' vs -> pureA (alts t' vs)
|
||||||
_ -> parens (ppT' True t)
|
_ -> parens (ppT' True t)
|
||||||
|
|
||||||
ppPredef n =
|
ppPredef n =
|
||||||
@@ -270,9 +282,9 @@ convert' atomic gId gr = if atomic then ppA else ppT
|
|||||||
|
|
||||||
token s = brackets ("TK"<+>doubleQuotes s)
|
token s = brackets ("TK"<+>doubleQuotes s)
|
||||||
|
|
||||||
alts t' vs = brackets ("TP" <+> list' (map alt vs) <+> ppT t')
|
alts t' vs = brackets ("TP" <+> list' (map alt vs) <+> ppA0 t')
|
||||||
where
|
where
|
||||||
alt (t,p) = parens (show (pre p)<>","<>ppT t)
|
alt (t,p) = parens (show (pre p)<>","<>ppT0 t)
|
||||||
|
|
||||||
pre (K s) = [s]
|
pre (K s) = [s]
|
||||||
pre (Strs ts) = concatMap pre ts
|
pre (Strs ts) = concatMap pre ts
|
||||||
@@ -285,6 +297,22 @@ convert' atomic gId gr = if atomic then ppA else ppT
|
|||||||
|
|
||||||
fields = map (ppA.snd.snd) . sort . filter (not.isLockLabel.fst)
|
fields = map (ppA.snd.snd) . sort . filter (not.isLockLabel.fst)
|
||||||
|
|
||||||
|
concat = if va then "+++" else "++"
|
||||||
|
-- pure = if va then \ x -> "pure"<+>parens x else id
|
||||||
|
-- pureA = if va then \ x -> parens ("pure"<+>x) else id
|
||||||
|
pure = if va then \ x -> brackets x else id -- forcing the list monad
|
||||||
|
pureA = pure
|
||||||
|
ap = if va then \ f x -> hang (ppA f<+>"<*>") 4 (ppA x)
|
||||||
|
else \ f x -> hang (ppB f) 4 (ppA x)
|
||||||
|
join = if va then \ x -> parens ("concat"<+>parens x) else id
|
||||||
|
-- sequence = if va then \ x -> parens ("sequence"<+>parens x) else id
|
||||||
|
empty = if va then pp "[]" else "error"<+>doubleQuotes "empty variant"
|
||||||
|
variants = if va then \ ts -> "concat"<+>list ts
|
||||||
|
else \ (t:_) -> "{-variants-}"<>ppA t -- !!
|
||||||
|
|
||||||
|
aps f [] = f
|
||||||
|
aps f (a:as) = aps (if va then hang (f<+>"<*>") 4 a else hang f 4 a) as
|
||||||
|
|
||||||
-- enumAll ty = case allParamValues gr ty of Ok ts -> ts
|
-- enumAll ty = case allParamValues gr ty of Ok ts -> ts
|
||||||
|
|
||||||
list = brackets . fsep . punctuate "," . map ppT
|
list = brackets . fsep . punctuate "," . map ppT
|
||||||
@@ -306,15 +334,22 @@ convert' atomic gId gr = if atomic then ppA else ppT
|
|||||||
m = fmap sort (M.fromListWith (++) (zip ts [[i]|i<-is]))
|
m = fmap sort (M.fromListWith (++) (zip ts [[i]|i<-is]))
|
||||||
is = [0..]::[Int]
|
is = [0..]::[Int]
|
||||||
|
|
||||||
|
patVars p =
|
||||||
|
case p of
|
||||||
|
PV x -> [x]
|
||||||
|
PAs x p -> x:patVars p
|
||||||
|
_ -> collectPattOp patVars p
|
||||||
|
|
||||||
convType = convType' False
|
convType = convType' False
|
||||||
convTypeA = convType' True
|
convTypeA = convType' True
|
||||||
|
|
||||||
convType' atomic gId = if atomic then ppA else ppT
|
convType' atomic va gId = if atomic then ppA else ppT
|
||||||
where
|
where
|
||||||
ppT = ppT' False
|
ppT = ppT' False
|
||||||
ppT' loop t =
|
ppT' loop t =
|
||||||
case t of
|
case t of
|
||||||
Table ti tv -> ppB ti <+> "->" <+> ppT tv
|
Table ti tv -> ppB ti <+> "->" <+>
|
||||||
|
if va then brackets (ppT tv) else ppT tv
|
||||||
_ -> ppB' loop t
|
_ -> ppB' loop t
|
||||||
|
|
||||||
ppB = ppB' False
|
ppB = ppB' False
|
||||||
@@ -372,7 +407,7 @@ labelClass l =
|
|||||||
hang ("class"<+>"Has_"<>l<+>"r"<+>"a"<+>"| r -> a"<+>"where") 4
|
hang ("class"<+>"Has_"<>l<+>"r"<+>"a"<+>"| r -> a"<+>"where") 4
|
||||||
(proj l<+>"::"<+>"r -> a")
|
(proj l<+>"::"<+>"r -> a")
|
||||||
|
|
||||||
paramType gId gr q@(_,n) =
|
paramType va gId gr q@(_,n) =
|
||||||
case lookupOrigInfo gr q of
|
case lookupOrigInfo gr q of
|
||||||
Ok (m,ResParam (Just (L _ ps)) _)
|
Ok (m,ResParam (Just (L _ ps)) _)
|
||||||
{- - | m/=cPredef && m/=moduleNameS "Prelude"-} ->
|
{- - | m/=cPredef && m/=moduleNameS "Prelude"-} ->
|
||||||
@@ -389,10 +424,10 @@ paramType gId gr q@(_,n) =
|
|||||||
"type"<+>gId (qual m n)<+>"n = Int")
|
"type"<+>gId (qual m n)<+>"n = Int")
|
||||||
| otherwise ->
|
| otherwise ->
|
||||||
((S.singleton (m,n),paramTypes gr t),
|
((S.singleton (m,n),paramTypes gr t),
|
||||||
"type"<+>gId (qual m n)<+>"="<+>convType gId t)
|
"type"<+>gId (qual m n)<+>"="<+>convType va gId t)
|
||||||
_ -> ((S.empty,S.empty),empty)
|
_ -> ((S.empty,S.empty),empty)
|
||||||
where
|
where
|
||||||
param m (n,ctx) = gId (qual m n)<+>[convTypeA gId t|(_,_,t)<-ctx]
|
param m (n,ctx) = gId (qual m n)<+>[convTypeA va gId t|(_,_,t)<-ctx]
|
||||||
argTypes = S.unions . map argTypes1
|
argTypes = S.unions . map argTypes1
|
||||||
argTypes1 (n,ctx) = S.unions [paramTypes gr t|(_,_,t)<-ctx]
|
argTypes1 (n,ctx) = S.unions [paramTypes gr t|(_,_,t)<-ctx]
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ data CFGTransform = CFGNoLR
|
|||||||
deriving (Show,Eq,Ord)
|
deriving (Show,Eq,Ord)
|
||||||
|
|
||||||
data HaskellOption = HaskellNoPrefix | HaskellGADT | HaskellLexical
|
data HaskellOption = HaskellNoPrefix | HaskellGADT | HaskellLexical
|
||||||
| HaskellConcrete
|
| HaskellConcrete | HaskellVariants
|
||||||
deriving (Show,Eq,Ord)
|
deriving (Show,Eq,Ord)
|
||||||
|
|
||||||
data Warning = WarnMissingLincat
|
data Warning = WarnMissingLincat
|
||||||
@@ -521,7 +521,8 @@ haskellOptionNames =
|
|||||||
[("noprefix", HaskellNoPrefix),
|
[("noprefix", HaskellNoPrefix),
|
||||||
("gadt", HaskellGADT),
|
("gadt", HaskellGADT),
|
||||||
("lexical", HaskellLexical),
|
("lexical", HaskellLexical),
|
||||||
("concrete", HaskellConcrete)]
|
("concrete", HaskellConcrete),
|
||||||
|
("variants", HaskellVariants)]
|
||||||
|
|
||||||
-- | This is for bacward compatibility. Since GHC 6.12 we
|
-- | This is for bacward compatibility. Since GHC 6.12 we
|
||||||
-- started using the native Unicode support in GHC but it
|
-- started using the native Unicode support in GHC but it
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-- with @gf -output-format=haskell -haskell=concrete@
|
-- with @gf -output-format=haskell -haskell=concrete@
|
||||||
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
|
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
|
||||||
module PGF.Haskell where
|
module PGF.Haskell where
|
||||||
import Control.Applicative((<$>))
|
import Control.Applicative((<$>),(<*>))
|
||||||
import Data.Char(toUpper)
|
import Data.Char(toUpper)
|
||||||
import Data.List(isPrefixOf)
|
import Data.List(isPrefixOf)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
@@ -54,9 +54,15 @@ class Has_s r a | r -> a where proj_s :: r -> a
|
|||||||
|
|
||||||
-- | Haskell representation of the GF record type @{s:t}@
|
-- | Haskell representation of the GF record type @{s:t}@
|
||||||
data R_s t = R_s t deriving (Eq,Ord,Show)
|
data R_s t = R_s t deriving (Eq,Ord,Show)
|
||||||
instance (EnumAll t) => EnumAll (R_s t) where
|
instance (EnumAll t) => EnumAll (R_s t) where enumAll = R_s <$> enumAll
|
||||||
enumAll = (R_s <$> enumAll)
|
|
||||||
instance Has_s (R_s t) t where proj_s (R_s t) = t
|
instance Has_s (R_s t) t where proj_s (R_s t) = t
|
||||||
|
|
||||||
-- | Coerce from any record type @{...,s:t,...}@ field to the supertype @{s:t}@
|
-- | Coerce from any record type @{...,s:t,...}@ field to the supertype @{s:t}@
|
||||||
to_R_s r = R_s (proj_s r)
|
to_R_s r = R_s (proj_s r)
|
||||||
|
|
||||||
|
|
||||||
|
-- *** Variants
|
||||||
|
|
||||||
|
infixr 5 +++
|
||||||
|
|
||||||
|
xs +++ ys = (++) <$> xs <*> ys
|
||||||
Reference in New Issue
Block a user