module Fay.Compiler.Print where
import Fay.Compiler.PrimOp
import qualified Fay.Exts.NoAnnotation as N
import Fay.Types
import Control.Monad
import Control.Monad.State
import Data.Aeson.Encode
import qualified Data.ByteString.Lazy.UTF8 as UTF8
import Data.Default
import Data.List
import Data.String
import Language.Haskell.Exts.Annotated hiding (alt, name, op, sym)
import Prelude hiding (exp)
import SourceMap.Types
printJSString :: Printable a => a -> String
printJSString x = concat $ reverse $ psOutput $ execState (runPrinter (printJS x)) def
printJSPretty :: Printable a => a -> String
printJSPretty x = concat $ reverse $ psOutput $ execState (runPrinter (printJS x)) def { psPretty = True }
instance Printable JsLit where
printJS typ = write $
let u8 = UTF8.toString . encode
in case typ of
(JsChar char) -> u8 [char]
(JsStr str) -> u8 str
(JsInt int) -> show int
(JsFloating rat) -> show rat
(JsBool b) -> if b then "true" else "false"
instance Printable N.QName where
printJS qname =
case qname of
Qual _ (ModuleName _ "Fay$") name -> "Fay$$" +> name
Qual _ moduleName name -> moduleName +> "." +> name
UnQual _ name -> printJS name
Special _ con -> printJS con
instance Printable N.ModuleName where
printJS (ModuleName _ "Fay$") =
write "Fay$"
printJS (ModuleName _ moduleName) = write $ go moduleName
where go ('.':xs) = '.' : go xs
go (x:xs) = normalizeName [x] ++ go xs
go [] = []
instance Printable N.SpecialCon where
printJS specialCon =
printJS $ fayBuiltin () $
case specialCon of
UnitCon _ -> "unit"
Cons _ -> "cons"
_ -> error $ "Special constructor not supported: " ++ show specialCon
instance Printable N.Name where
printJS name = write $
case name of
Ident _ idn -> encodeName idn
Symbol _ sym -> encodeName sym
instance Printable [JsStmt] where
printJS = mapM_ printJS
instance Printable JsStmt where
printJS (JsExpStmt e) =
printJS e +> ";" +> newline
printJS (JsBlock stmts) =
"{ " +> stmts +> "}"
printJS (JsVar name expr) =
"var " +> name +> " = " +> expr +> ";" +> newline
printJS (JsUpdate name expr) =
name +> " = " +> expr +> ";" +> newline
printJS (JsSetProp name prop expr) =
name +> "." +> prop +> " = " +> expr +> ";" +> newline
printJS (JsSetQName msrcloc name expr) = do
maybe (return ()) mapping msrcloc
name +> " = " +> expr +> ";" +> newline
printJS (JsSetConstructor name expr) =
printCons name +> " = " +> expr +> ";" +> newline +>
printCons name +> ".prototype.instance = \"" +> printConsUnQual name +> "\";" +> newline
printJS (JsSetModule mp expr) =
mp +> " = " +> expr +> ";" +> newline
printJS (JsSetPropExtern name prop expr) =
name +> "['" +> prop +> "'] = " +> expr +> ";" +> newline
printJS (JsIf exp thens elses) =
"if (" +> exp +> ") {" +> newline +>
indented (printJS thens) +>
"}" +>
(when (length elses > 0) $ " else {" +>
indented (printJS elses) +>
"}") +> newline
printJS (JsEarlyReturn exp) =
"return " +> exp +> ";" +> newline
printJS (JsThrow exp) = do
"throw " +> exp +> ";" +> newline
printJS (JsWhile cond stmts) =
"while (" +> cond +> ") {" +> newline +>
indented (printJS stmts) +>
"}" +> newline
printJS JsContinue =
printJS "continue;" +> newline
instance Printable ModulePath where
printJS (unModulePath -> l) = write $ intercalate "." l
instance Printable JsExp where
printJS (JsSeq es) = "(" +> intercalateM "," (map printJS es) +> ")"
printJS (JsRawExp e) = write e
printJS (JsName name) = printJS name
printJS (JsThrowExp exp) =
"(function(){ throw (" +> exp +> "); })()"
printJS JsNull =
printJS "null"
printJS JsUndefined =
printJS "undefined"
printJS (JsLit lit) =
printJS lit
printJS (JsParen exp) =
"(" +> exp +> ")"
printJS (JsList exps) =
"[" +> intercalateM "," (map printJS exps) +> printJS "]"
printJS (JsNew name args) =
"new " +> (JsApp (JsName name) args)
printJS (JsIndex i exp) =
"(" +> exp +> ")[" +> show i +> "]"
printJS (JsEq exp1 exp2) =
exp1 +> " === " +> exp2
printJS (JsNeq exp1 exp2) =
exp1 +> " !== " +> exp2
printJS (JsGetProp exp prop) = exp +> "." +> prop
printJS (JsLookup exp1 exp2) =
exp1 +> "[" +> exp2 +> "]"
printJS (JsUpdateProp name prop expr) =
"(" +> name +> "." +> prop +> " = " +> expr +> ")"
printJS (JsInfix op x y) =
x +> " " +> op +> " " +> y
printJS (JsGetPropExtern exp prop) =
exp +> "[" +> (JsLit . JsStr) prop +> "]"
printJS (JsUpdatePropExtern name prop expr) =
"(" +> name +> "['" +> prop +> "'] = " +> expr +> ")"
printJS (JsTernaryIf cond conseq alt) =
cond +> " ? " +> conseq +> " : " +> alt
printJS (JsInstanceOf exp classname) =
exp +> " instanceof " +> classname
printJS (JsObj assoc) =
"{" +> (intercalateM "," (map cons assoc)) +> "}"
where cons (key,value) = "\"" +> key +> "\": " +> value
printJS (JsLitObj assoc) =
"{" +> (intercalateM "," (map cons assoc)) +> "}"
where
cons :: (N.Name, JsExp) -> Printer ()
cons (key,value) = "\"" +> key +> "\": " +> value
printJS (JsFun nm params stmts ret) =
"function"
+> maybe (return ()) ((" " +>) . printJS . ident) nm
+> "("
+> (intercalateM "," (map printJS params))
+> "){" +> newline
+> indented (stmts +>
case ret of
Just ret' -> "return " +> ret' +> ";" +> newline
Nothing -> return ())
+> "}"
printJS (JsApp op args) =
(if isFunc op then JsParen op else op)
+> "("
+> (intercalateM "," (map printJS args))
+> ")"
where isFunc JsFun{..} = True; isFunc _ = False
printJS (JsNegApp args) =
"(-(" +> printJS args +> "))"
printJS (JsAnd a b) =
printJS a +> "&&" +> printJS b
printJS (JsOr a b) =
printJS a +> "||" +> printJS b
ident :: JsName -> JsName
ident n = case n of
JsConstructor (Qual _ _ s) -> JsNameVar $ UnQual () s
a -> a
instance Printable JsName where
printJS name =
case name of
JsNameVar qname -> printJS qname
JsThis -> write "this"
JsThunk -> write "Fay$$$"
JsForce -> write "Fay$$_"
JsApply -> write "Fay$$__"
JsParam i -> write ("$p" ++ show i)
JsTmp i -> write ("$tmp" ++ show i)
JsConstructor qname -> printCons qname
JsBuiltIn qname -> "Fay$$" +> printJS qname
JsParametrizedType -> write "type"
JsModuleName (ModuleName _ m) -> write m
printCons :: N.QName -> Printer ()
printCons (UnQual _ n) = printConsName n
printCons (Qual _ (ModuleName _ m) n) = printJS m +> "." +> printConsName n
printCons (Special {}) = error "qname2String Special"
printConsUnQual :: N.QName -> Printer ()
printConsUnQual (UnQual _ x) = printJS x
printConsUnQual (Qual _ _ n) = printJS n
printConsUnQual (Special {}) = error "printConsUnqual Special"
printConsName :: N.Name -> Printer ()
printConsName n = write "_" >> printJS n
instance Printable String where
printJS = write
instance Printable (Printer ()) where
printJS = id
reservedWords :: [String]
reservedWords =
["abstract","boolean","break","byte","case","catch","char","class"
,"comment","const","continue","debugger","default","delete","do","double"
,"else","enum","export","extends","false","final","finally","float"
,"for","function","global","goto","if","implements","import","in"
,"instanceOf","instanceof","int","interface","label","long","native"
,"new","null","package","private","protected","public","return","short"
,"static","super","switch","synchronized","this","throw","throws"
,"transient","true","try","typeof","undefined","var","void","while"
,"window","with","yield"]
++ ["__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "constructor", "force", "forced", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "value", "valueOf"]
encodeName :: String -> String
encodeName ('$':'g':'e':'n':name) = "$gen_" ++ normalizeName name
encodeName name
| name `elem` reservedWords = "$_" ++ normalizeName name
| otherwise = normalizeName name
normalizeName :: String -> String
normalizeName name =
concatMap encodeChar name
where
encodeChar c | c `elem` allowed = [c]
| otherwise = escapeChar c
allowed = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_"
escapeChar c = "$" ++ charId c ++ "$"
charId c = show (fromEnum c)
indented :: Printer a -> Printer ()
indented p = do
PrintState{..} <- get
if psPretty
then do modify $ \s -> s { psIndentLevel = psIndentLevel + 1 }
p
modify $ \s -> s { psIndentLevel = psIndentLevel }
else p >> return ()
newline :: Printer ()
newline = do
PrintState{..} <- get
when psPretty $ do
write "\n"
modify $ \s -> s { psNewline = True }
write :: String -> Printer ()
write x = do
PrintState{..} <- get
let out = if psNewline then replicate (2*psIndentLevel) ' ' ++ x else x
modify $ \s -> s { psOutput = out : psOutput
, psLine = psLine + additionalLines
, psColumn = if additionalLines > 0
then length (concat (take 1 (reverse srclines)))
else psColumn + length x
, psNewline = False
}
return (error "Nothing to return for writer string.")
where srclines = lines x
additionalLines = length (filter (=='\n') x)
mapping :: SrcSpan -> Printer ()
mapping SrcSpan{..} = do
modify $ \s -> s { psMappings = m s : psMappings s }
return ()
where m ps = Mapping { mapGenerated = Pos (fromIntegral (psLine ps))
(fromIntegral (psColumn ps))
, mapOriginal = Just (Pos (fromIntegral srcSpanStartLine)
(fromIntegral srcSpanStartColumn 1))
, mapSourceFile = Just srcSpanFilename
, mapName = Nothing
}
intercalateM :: String -> [Printer a] -> Printer ()
intercalateM _ [] = return ()
intercalateM _ [x] = x >> return ()
intercalateM str (x:xs) = do
x
write str
intercalateM str xs
(+>) :: (Printable a, Printable b) => a -> b -> Printer ()
pa +> pb = printJS pa >> printJS pb