forked from GitHub/gf-core
392 lines
16 KiB
Haskell
392 lines
16 KiB
Haskell
----------------------------------------------------------------------
|
|
-- |
|
|
-- Module : GF.Grammar.Printer
|
|
-- Maintainer : Krasimir Angelov
|
|
-- Stability : (stable)
|
|
-- Portability : (portable)
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
module GF.Grammar.Printer
|
|
( -- ** Pretty printing
|
|
TermPrintQual(..)
|
|
, ppModule
|
|
, ppJudgement
|
|
, ppParams
|
|
, ppTerm
|
|
, ppPatt
|
|
, ppValue
|
|
, ppConstrs
|
|
, ppQIdent
|
|
, ppMeta
|
|
, getAbs
|
|
) where
|
|
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
|
|
|
|
import PGF2(Literal(..))
|
|
import PGF2.Transactions(LIndex,LParam,Symbol(..))
|
|
import GF.Infra.Ident
|
|
import GF.Infra.Option
|
|
import GF.Grammar.Values
|
|
import GF.Grammar.Grammar
|
|
|
|
import GF.Text.Pretty
|
|
import Data.Maybe (isNothing)
|
|
import Data.List (intersperse)
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Array.IArray as Array
|
|
import qualified GHC.Show
|
|
|
|
data TermPrintQual
|
|
= Terse | Unqualified | Qualified | Internal
|
|
deriving Eq
|
|
|
|
instance Pretty Grammar where
|
|
pp = vcat . map (ppModule Qualified) . modules
|
|
|
|
ppModule :: TermPrintQual -> SourceModule -> Doc
|
|
ppModule q (mn, ModInfo mtype mstat opts exts with opens _ _ jments) =
|
|
hdr $$
|
|
nest 2 (ppOptions opts $$
|
|
vcat (map (ppJudgement q) (Map.toList jments))) $$
|
|
ftr
|
|
where
|
|
hdr = complModDoc <+> modTypeDoc <+> '=' <+>
|
|
hsep (intersperse (pp "**") $
|
|
filter (not . isEmpty) $ [ commaPunct ppExtends exts
|
|
, maybe empty ppWith with
|
|
, if null opens
|
|
then pp '{'
|
|
else "open" <+> commaPunct ppOpenSpec opens <+> "in" <+> '{'
|
|
])
|
|
|
|
ftr = '}'
|
|
|
|
complModDoc =
|
|
case mstat of
|
|
MSComplete -> empty
|
|
MSIncomplete -> pp "incomplete"
|
|
|
|
modTypeDoc =
|
|
case mtype of
|
|
MTAbstract -> "abstract" <+> mn
|
|
MTResource -> "resource" <+> mn
|
|
MTConcrete abs -> "concrete" <+> mn <+> "of" <+> abs
|
|
MTInterface -> "interface" <+> mn
|
|
MTInstance ie -> "instance" <+> mn <+> "of" <+> ppExtends ie
|
|
|
|
ppExtends (id,MIAll ) = pp id
|
|
ppExtends (id,MIOnly incs) = id <+> brackets (commaPunct pp incs)
|
|
ppExtends (id,MIExcept incs) = id <+> '-' <+> brackets (commaPunct pp incs)
|
|
|
|
ppWith (id,ext,opens) = ppExtends (id,ext) <+> "with" <+> commaPunct ppInstSpec opens
|
|
|
|
ppOptions opts =
|
|
"flags" $$
|
|
nest 2 (vcat [option <+> '=' <+> ppLit value <+> ';' | (option,value) <- optionsGFO opts])
|
|
|
|
ppJudgement q (id, AbsCat pcont ) =
|
|
"cat" <+> id <+>
|
|
(case pcont of
|
|
Just (L _ cont) -> hsep (map (ppDecl q) cont)
|
|
Nothing -> empty) <+> ';'
|
|
ppJudgement q (id, AbsFun ptype _ pexp poper) =
|
|
let kind | isNothing pexp = "data"
|
|
| poper == Just False = "oper"
|
|
| otherwise = "fun"
|
|
in
|
|
(case ptype of
|
|
Just (L _ typ) -> kind <+> id <+> ':' <+> ppTerm q 0 typ <+> ';'
|
|
Nothing -> empty) $$
|
|
(case pexp of
|
|
Just [] -> empty
|
|
Just eqs -> "def" <+> vcat [id <+> hsep (map (ppPatt q 2) ps) <+> '=' <+> ppTerm q 0 e <+> ';' | L _ (ps,e) <- eqs]
|
|
Nothing -> empty)
|
|
ppJudgement q (id, ResParam pparams _) =
|
|
"param" <+> id <+>
|
|
(case pparams of
|
|
Just (L _ ps) -> '=' <+> ppParams q ps
|
|
_ -> empty) <+> ';'
|
|
ppJudgement q (id, ResValue pvalue idx) =
|
|
"-- param constructor" <+> id <+> ':' <+>
|
|
(case pvalue of
|
|
(L _ ty) -> ppTerm q 0 ty) <+> ';' <+> parens (pp "index = " <> pp idx)
|
|
ppJudgement q (id, ResOper ptype pexp) =
|
|
"oper" <+> id <+>
|
|
(case ptype of {Just (L _ t) -> ':' <+> ppTerm q 0 t; Nothing -> empty} $$
|
|
case pexp of {Just (L _ e) -> '=' <+> ppTerm q 0 e; Nothing -> empty}) <+> ';'
|
|
ppJudgement q (id, ResOverload ids defs) =
|
|
"oper" <+> id <+> '=' <+>
|
|
("overload" <+> '{' $$
|
|
nest 2 (vcat [id <+> (':' <+> ppTerm q 0 ty $$ '=' <+> ppTerm q 0 e <+> ';') | (L _ ty,L _ e) <- defs]) $$
|
|
'}') <+> ';'
|
|
ppJudgement q (id, CncCat mtyp pdef pref pprn mpmcfg) =
|
|
(case mtyp of
|
|
Just (L _ typ) -> "lincat" <+> id <+> '=' <+> ppTerm q 0 typ <+> ';'
|
|
Nothing -> empty) $$
|
|
(case pdef of
|
|
Just (L _ exp) -> "lindef" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
|
|
Nothing -> empty) $$
|
|
(case pref of
|
|
Just (L _ exp) -> "linref" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
|
|
Nothing -> empty) $$
|
|
(case pprn of
|
|
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
|
|
Nothing -> empty) $$
|
|
(case (mtyp,mpmcfg,q) of
|
|
(Just (L _ typ),Just rules,Internal)
|
|
-> "pmcfg" <+> '{' $$
|
|
nest 2 (vcat (map (ppPmcfgRule id [] id) rules)) $$
|
|
'}'
|
|
_ -> empty)
|
|
ppJudgement q (id, CncFun mtyp pdef pprn mpmcfg) =
|
|
(case pdef of
|
|
Just (L _ e) -> let (xs,e') = getAbs e
|
|
in "lin" <+> id <+> hsep (map ppBind xs) <+> '=' <+> ppTerm q 0 e' <+> ';'
|
|
Nothing -> empty) $$
|
|
(case pprn of
|
|
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
|
|
Nothing -> empty) $$
|
|
(case (mtyp,mpmcfg,q) of
|
|
(Just (args,res,_,_),Just rules,Internal)
|
|
-> "pmcfg" <+> '{' $$
|
|
nest 2 (vcat (map (ppPmcfgRule id args res) rules)) $$
|
|
'}'
|
|
_ -> empty)
|
|
ppJudgement q (id, AnyInd cann mid) =
|
|
case q of
|
|
Internal -> "ind" <+> id <+> '=' <+> (if cann then pp "canonical" else empty) <+> mid <+> ';'
|
|
_ -> empty
|
|
|
|
ppPmcfgRule id arg_cats res_cat (PMCFGRule res args lins) =
|
|
pp id <+> (':' <+> hsep (intersperse (pp '*') (zipWith ppPmcfgCat arg_cats args)) <+> "->" <+> ppPmcfgCat res_cat res $$
|
|
'=' <+> brackets (vcat (map (hsep . map ppSymbol) lins)))
|
|
|
|
ppPmcfgCat :: Ident -> PMCFGCat -> Doc
|
|
ppPmcfgCat cat (PMCFGCat r rs) = pp cat <> parens (ppLinFun ppLParam r rs)
|
|
|
|
instance Pretty Term where pp = ppTerm Unqualified 0
|
|
|
|
ppTerm q d (Abs b v e) = let (xs,e') = getAbs (Abs b v e)
|
|
in prec d 0 ('\\' <> commaPunct ppBind xs <+> "->" <+> ppTerm q 0 e')
|
|
ppTerm q d (T TRaw xs) = case getCTable (T TRaw xs) of
|
|
([],_) -> "table" <+> '{' $$
|
|
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
|
|
'}'
|
|
(vs,e) -> prec d 0 ("\\\\" <> commaPunct pp vs <+> "=>" <+> ppTerm q 0 e)
|
|
ppTerm q d (T (TTyped t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
|
|
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
|
|
'}'
|
|
ppTerm q d (T (TComp t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
|
|
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
|
|
'}'
|
|
ppTerm q d (T (TWild t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
|
|
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
|
|
'}'
|
|
ppTerm q d (Prod bt x a b)= if x == identW && bt == Explicit
|
|
then prec d 0 (ppTerm q 4 a <+> "->" <+> ppTerm q 0 b)
|
|
else prec d 0 (parens (ppBind (bt,x) <+> ':' <+> ppTerm q 0 a) <+> "->" <+> ppTerm q 0 b)
|
|
ppTerm q d (Table kt vt)=prec d 0 (ppTerm q 3 kt <+> "=>" <+> ppTerm q 0 vt)
|
|
ppTerm q d (Let l e) = let (ls,e') = getLet e
|
|
in prec d 0 ("let" <+> vcat (map (ppLocDef q) (l:ls)) $$ "in" <+> ppTerm q 0 e')
|
|
ppTerm q d (Example e s)=prec d 0 ("in" <+> ppTerm q 5 e <+> str s)
|
|
ppTerm q d (C e1 e2) =prec d 1 (hang (ppTerm q 2 e1) 2 ("++" <+> ppTerm q 1 e2))
|
|
ppTerm q d (Glue e1 e2) =prec d 2 (ppTerm q 3 e1 <+> '+' <+> ppTerm q 2 e2)
|
|
ppTerm q d (S x y) = case x of
|
|
T annot xs -> let e = case annot of
|
|
TRaw -> y
|
|
TTyped t -> Typed y t
|
|
TComp t -> Typed y t
|
|
TWild t -> Typed y t
|
|
in "case" <+> ppTerm q 0 e <+>"of" <+> '{' $$
|
|
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
|
|
'}'
|
|
_ -> prec d 3 (hang (ppTerm q 3 x) 2 ("!" <+> ppTerm q 4 y))
|
|
ppTerm q d (ExtR x y) = prec d 3 (ppTerm q 3 x <+> "**" <+> ppTerm q 4 y)
|
|
ppTerm q d (App x y) = prec d 4 (ppTerm q 4 x <+> ppTerm q 5 y)
|
|
ppTerm q d (V e es) = hang "table" 2 (sep [ppTerm q 6 e,brackets (fsep (punctuate ';' (map (ppTerm q 0) es)))])
|
|
ppTerm q d (FV es) = prec d 4 ("variants" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es))))
|
|
ppTerm q d (AdHocOverload es) = "overload" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
|
|
ppTerm q d (Alts e xs) = prec d 4 ("pre" <+> braces (ppTerm q 0 e <> ';' <+> fsep (punctuate ';' (map (ppAltern q) xs))))
|
|
ppTerm q d (Strs es) = "strs" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
|
|
ppTerm q d (EPatt _ _ p)=prec d 4 ('#' <+> ppPatt q 2 p)
|
|
ppTerm q d (EPattType t)=prec d 4 ("pattern" <+> ppTerm q 0 t)
|
|
ppTerm q d (P t l) = prec d 5 (ppTerm q 5 t <> '.' <> l)
|
|
ppTerm q d (Cn id) = pp id
|
|
ppTerm q d (Vr id) = pp id
|
|
ppTerm q d (Q id) = ppQIdent q id
|
|
ppTerm q d (QC id) = ppQIdent q id
|
|
ppTerm q d (Sort id) = pp id
|
|
ppTerm q d (K s) = str s
|
|
ppTerm q d (EInt n) = pp n
|
|
ppTerm q d (EFloat f) = pp f
|
|
ppTerm q d (Meta i) = ppMeta i
|
|
ppTerm q d (Empty) = pp "[]"
|
|
ppTerm q d (R []) = pp "<>" -- to distinguish from {} empty RecType
|
|
ppTerm q d (R xs) = braces (fsep (punctuate ';' [l <+>
|
|
fsep [case mb_t of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty},
|
|
'=' <+> ppTerm q 0 e] | (l,(mb_t,e)) <- xs]))
|
|
ppTerm q d (RecType xs)
|
|
| q == Terse = case [cat | (l,_) <- xs, let (p,cat) = splitAt 5 (showIdent (label2ident l)), p == "lock_"] of
|
|
[cat] -> pp cat
|
|
_ -> doc
|
|
| otherwise = doc
|
|
where
|
|
doc = braces (fsep (punctuate ';' [l <+> ':' <+> ppTerm q 0 t | (l,t) <- xs]))
|
|
ppTerm q d (Typed e t) = '<' <> ppTerm q 0 e <+> ':' <+> ppTerm q 0 t <> '>'
|
|
ppTerm q d (ImplArg e) = braces (ppTerm q 0 e)
|
|
ppTerm q d (ELincat cat t) = prec d 4 ("lincat" <+> cat <+> ppTerm q 5 t)
|
|
ppTerm q d (ELin cat t) = prec d 4 ("lin" <+> cat <+> ppTerm q 5 t)
|
|
ppTerm q d (TSymCat i r rs) = pp '<' <> pp i <> pp ',' <> ppLinFun pp r rs <> pp '>'
|
|
|
|
ppEquation q (ps,e) = hcat (map (ppPatt q 2) ps) <+> "->" <+> ppTerm q 0 e
|
|
|
|
ppCase q (p,e) = ppPatt q 0 p <+> "=>" <+> ppTerm q 0 e
|
|
|
|
instance Pretty Patt where pp = ppPatt Unqualified 0
|
|
|
|
ppPatt q d (PAlt p1 p2) = prec d 0 (ppPatt q 0 p1 <+> '|' <+> ppPatt q 1 p2)
|
|
ppPatt q d (PSeq _ _ p1 _ _ p2) = prec d 0 (ppPatt q 0 p1 <+> '+' <+> ppPatt q 1 p2)
|
|
ppPatt q d (PC f ps) = if null ps
|
|
then pp f
|
|
else prec d 1 (f <+> hsep (map (ppPatt q 3) ps))
|
|
ppPatt q d (PP f ps) = if null ps
|
|
then ppQIdent q f
|
|
else prec d 1 (ppQIdent q f <+> hsep (map (ppPatt q 3) ps))
|
|
ppPatt q d (PRep _ _ p) = prec d 1 (ppPatt q 3 p <> '*')
|
|
ppPatt q d (PAs f p) = prec d 2 (f <> '@' <> ppPatt q 3 p)
|
|
ppPatt q d (PNeg p) = prec d 2 ('-' <> ppPatt q 3 p)
|
|
ppPatt q d (PChar) = pp '?'
|
|
ppPatt q d (PChars s) = brackets (str s)
|
|
ppPatt q d (PMacro id) = '#' <> id
|
|
ppPatt q d (PM id) = '#' <> ppQIdent q id
|
|
ppPatt q d PW = pp '_'
|
|
ppPatt q d (PV id) = pp id
|
|
ppPatt q d (PInt n) = pp n
|
|
ppPatt q d (PFloat f) = pp f
|
|
ppPatt q d (PString s) = str s
|
|
ppPatt q d (PR xs) = braces (hsep (punctuate ';' [l <+> '=' <+> ppPatt q 0 e | (l,e) <- xs]))
|
|
ppPatt q d (PImplArg p) = braces (ppPatt q 0 p)
|
|
ppPatt q d (PTilde t) = prec d 2 ('~' <> ppTerm q 6 t)
|
|
|
|
ppValue :: TermPrintQual -> Int -> Val -> Doc
|
|
ppValue q d (VGen i x) = x <> "{-" <> i <> "-}" ---- latter part for debugging
|
|
ppValue q d (VApp u v) = prec d 4 (ppValue q 4 u <+> ppValue q 5 v)
|
|
ppValue q d (VCn (_,c)) = pp c
|
|
ppValue q d (VClos env e) = case e of
|
|
Meta _ -> ppTerm q d e <> ppEnv env
|
|
_ -> ppTerm q d e ---- ++ prEnv env ---- for debugging
|
|
ppValue q d (VRecType xs) = braces (hsep (punctuate ',' [l <> '=' <> ppValue q 0 v | (l,v) <- xs]))
|
|
ppValue q d VType = pp "Type"
|
|
|
|
ppConstrs :: Constraints -> [Doc]
|
|
ppConstrs = map (\(v,w) -> braces (ppValue Unqualified 0 v <+> "<>" <+> ppValue Unqualified 0 w))
|
|
|
|
ppEnv :: Env -> Doc
|
|
ppEnv e = hcat (map (\(x,t) -> braces (x <> ":=" <> ppValue Unqualified 0 t)) e)
|
|
|
|
str s = doubleQuotes (pp (foldr showLitChar "" s))
|
|
where
|
|
showLitChar c
|
|
| c == '"' = showString "\\\""
|
|
| c > '\DEL' = showChar c
|
|
| otherwise = GHC.Show.showLitChar c
|
|
|
|
ppDecl q (_,id,typ)
|
|
| id == identW = ppTerm q 3 typ
|
|
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
|
|
|
|
ppDDecl q (_,id,typ)
|
|
| id == identW = ppTerm q 6 typ
|
|
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
|
|
|
|
ppQIdent :: TermPrintQual -> QIdent -> Doc
|
|
ppQIdent q (m,id) =
|
|
case q of
|
|
Terse -> pp id
|
|
Unqualified -> pp id
|
|
Qualified -> m <> '.' <> id
|
|
Internal -> m <> '.' <> id
|
|
|
|
|
|
instance Pretty Label where pp = pp . label2ident
|
|
|
|
ppOpenSpec (OSimple id) = pp id
|
|
ppOpenSpec (OQualif id n) = parens (id <+> '=' <+> n)
|
|
|
|
ppInstSpec (id,n) = parens (id <+> '=' <+> n)
|
|
|
|
ppLocDef q (id, (mbt, e)) =
|
|
id <+>
|
|
(case mbt of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty} <+> '=' <+> ppTerm q 0 e) <+> ';'
|
|
|
|
ppBind (Explicit,v) = pp v
|
|
ppBind (Implicit,v) = braces v
|
|
|
|
ppAltern q (x,y) = ppTerm q 0 x <+> '/' <+> ppTerm q 0 y
|
|
|
|
ppParams q ps = fsep (intersperse (pp '|') (map (ppParam q) ps))
|
|
ppParam q (id,cxt) = id <+> hsep (map (ppDDecl q) cxt)
|
|
|
|
commaPunct f ds = (hcat (punctuate "," (map f ds)))
|
|
|
|
prec d1 d2 doc
|
|
| d1 > d2 = parens doc
|
|
| otherwise = doc
|
|
|
|
getAbs :: Term -> ([(BindType,Ident)], Term)
|
|
getAbs (Abs bt v e) = let (xs,e') = getAbs e
|
|
in ((bt,v):xs,e')
|
|
getAbs e = ([],e)
|
|
|
|
getCTable :: Term -> ([Ident], Term)
|
|
getCTable (T TRaw [(PV v,e)]) = let (vs,e') = getCTable e
|
|
in (v:vs,e')
|
|
getCTable (T TRaw [(PW, e)]) = let (vs,e') = getCTable e
|
|
in (identW:vs,e')
|
|
getCTable e = ([],e)
|
|
|
|
getLet :: Term -> ([LocalDef], Term)
|
|
getLet (Let l e) = let (ls,e') = getLet e
|
|
in (l:ls,e')
|
|
getLet e = ([],e)
|
|
|
|
ppMeta :: Int -> Doc
|
|
ppMeta n
|
|
| n == 0 = pp '?'
|
|
| otherwise = pp '?' <> pp n
|
|
|
|
ppLit (LStr s) = pp (show s)
|
|
ppLit (LInt n) = pp n
|
|
ppLit (LFlt d) = pp d
|
|
|
|
ppSymbol (SymCat d r rs)= pp '<' <> pp d <> pp ',' <> ppLinFun ppLParam r rs <> pp '>'
|
|
ppSymbol (SymLit d r) = pp '{' <> pp d <> pp ',' <> pp r <> pp '}'
|
|
ppSymbol (SymVar d r) = pp '<' <> pp d <> pp ',' <> pp '$' <> pp r <> pp '>'
|
|
ppSymbol (SymKS t) = doubleQuotes (pp t)
|
|
ppSymbol SymNE = pp "nonExist"
|
|
ppSymbol SymBIND = pp "BIND"
|
|
ppSymbol SymSOFT_BIND = pp "SOFT_BIND"
|
|
ppSymbol SymSOFT_SPACE = pp "SOFT_SPACE"
|
|
ppSymbol SymCAPIT = pp "CAPIT"
|
|
ppSymbol SymALL_CAPIT = pp "ALL_CAPIT"
|
|
ppSymbol (SymKP syms alts) = pp "pre" <+> braces (hsep (punctuate (pp ';') (hsep (map ppSymbol syms) : map ppAlt alts)))
|
|
|
|
ppLinFun ppParam r rs
|
|
| r == 0 && not (null rs) = hcat (intersperse (pp '+') ( map ppTerm rs))
|
|
| otherwise = hcat (intersperse (pp '+') (pp r : map ppTerm rs))
|
|
where
|
|
ppTerm (i,p)
|
|
| i == 1 = ppParam p
|
|
| otherwise = pp i <> pp '*' <> ppParam p
|
|
|
|
ppLParam p
|
|
| i == 0 = pp (chars !! j)
|
|
| otherwise = pp (chars !! j : show i)
|
|
where
|
|
chars = "ijklmnopqr"
|
|
(i,j) = p `divMod` (length chars)
|
|
|
|
ppAlt (syms,ps) = hsep (map ppSymbol syms) <+> pp '/' <+> hsep (map (doubleQuotes . pp) ps)
|