module Fay.Compiler.Print where
import Fay.Compiler.Prelude
import Fay.Compiler.PrimOp
import qualified Fay.Exts.NoAnnotation as N
import Fay.Types
import Control.Monad.State
import Data.Aeson.Encode
import qualified Data.ByteString.Lazy.UTF8 as UTF8
import Language.Haskell.Exts.Annotated hiding (alt, name, op, sym)
import SourceMap.Types
printJSString :: Printable a => a -> String
printJSString x = concat . reverse . psOutput $ execState (runPrinter (printJS x)) defaultPrintState
printJSPretty :: Printable a => a -> String
printJSPretty x = concat . reverse . psOutput $ execState (runPrinter (printJS x)) defaultPrintState { 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) +>
"}" +>
(unless (null elses) $ " else {" +>
indented (printJS elses) +>
"}") +> newline
printJS (JsEarlyReturn exp) =
"return " +> exp +> ";" +> newline
printJS (JsThrow exp) =
"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 = concatMap encodeChar
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 }
void p
modify $ \s -> s { psIndentLevel = psIndentLevel }
else void p
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] = void x
intercalateM str (x:xs) = do
void x
write str
intercalateM str xs
(+>) :: (Printable a, Printable b) => a -> b -> Printer ()
pa +> pb = printJS pa >> printJS pb