module BrownPLT.JavaScript.Contracts.Template
( JavaScriptTemplate
, exprTemplate
, stmtTemplate
, substVar
, substVarList
, substIdList
, substFieldList
, expandCall
, templateExpression
, noPos
, thunkExpr
, renderTemplate
, renameVar
, templateStatements
) where
import Data.Data
import Data.Generics
import Text.ParserCombinators.Parsec (parse, many1)
import Text.ParserCombinators.Parsec.Pos (initialPos, SourcePos)
import Text.PrettyPrint.HughesPJ (render)
import BrownPLT.JavaScript.PrettyPrint (renderExpression, renderStatements)
import BrownPLT.JavaScript.Parser
import BrownPLT.JavaScript.Syntax
import BrownPLT.JavaScript.Instances()
import BrownPLT.JavaScript.Crawl()
noPos :: SourcePos
noPos = initialPos "template"
thunkExpr :: ParsedExpression -> ParsedExpression
thunkExpr expr = FuncExpr noPos [] (ReturnStmt noPos (Just expr))
data JavaScriptTemplate
= ExpressionTemplate ParsedExpression
| StatementTemplate [ParsedStatement]
exprTemplate :: String -> JavaScriptTemplate
exprTemplate str = case parse assignExpr "expression template" str of
Left err -> error ("Error parsing template: " ++ show err ++
"; template:\n\n" ++ str)
Right expr -> ExpressionTemplate expr
stmtTemplate :: String -> JavaScriptTemplate
stmtTemplate str = case parse (many1 parseStatement) "statement template" str of
Left err -> error ("Error parsing template: " ++ show err ++
"; template:\n\n" ++ str)
Right stmts -> StatementTemplate stmts
renderTemplate :: JavaScriptTemplate -> String
renderTemplate (ExpressionTemplate expr) = renderExpression expr
renderTemplate (StatementTemplate stmts) = renderStatements stmts
templateExpression :: JavaScriptTemplate -> ParsedExpression
templateExpression (ExpressionTemplate expr) = expr
templateStatements :: JavaScriptTemplate -> [ParsedStatement]
templateStatements (StatementTemplate stmts) = stmts
expandCall :: String
-> ([ParsedExpression] -> [ParsedExpression])
-> JavaScriptTemplate
-> JavaScriptTemplate
expandCall functionId expander (StatementTemplate body) =
StatementTemplate (everywhere (mkT subst) body) where
subst (CallExpr p1 fn@(VarRef p2 (Id p3 id')) args)
| id' == functionId = CallExpr p1 fn (expander args)
subst expr = expr
renameVar :: String
-> String
-> JavaScriptTemplate
-> JavaScriptTemplate
renameVar idOld idNew body =
let
substExpr :: ParsedExpression -> ParsedExpression
substExpr (VarRef p1 (Id p2 thisId))
| thisId == idOld = VarRef p1 (Id p2 idNew)
substExpr v = v
substDecl :: VarDecl SourcePos -> VarDecl SourcePos
substDecl (VarDecl p1 (Id p2 thisId) val)
| thisId == idOld = VarDecl p1 (Id p2 idNew) val
substDecl v = v
in case body of
ExpressionTemplate body -> ExpressionTemplate $
everywhere ((mkT substDecl) . (mkT substExpr)) body
StatementTemplate stmts -> StatementTemplate $
everywhere (mkT substDecl . mkT substExpr) stmts
substVar :: String
-> ParsedExpression
-> JavaScriptTemplate
-> JavaScriptTemplate
substVar id expr body =
let subst (VarRef _ (Id _ id')) | id' == id = expr
subst expr = expr
in case body of
ExpressionTemplate body ->
ExpressionTemplate (everywhere (mkT subst) body)
StatementTemplate stmts ->
StatementTemplate (everywhere (mkT subst) stmts)
substVarList :: String
-> [ParsedExpression]
-> JavaScriptTemplate
-> JavaScriptTemplate
substVarList id exprs (ExpressionTemplate body) =
ExpressionTemplate (everywhere (mkT subst) body) where
subst [VarRef _ (Id _ id')] | id' == id = exprs
subst lst = lst
substIdList :: String
-> [String]
-> JavaScriptTemplate
-> JavaScriptTemplate
substIdList id ids (ExpressionTemplate body) =
ExpressionTemplate (everywhere (mkT subst) body) where
subst [Id _ id'] | id' == id = map (Id noPos) ids
subst lst = lst
substFieldList :: String
-> [(String,ParsedExpression)]
-> JavaScriptTemplate
-> JavaScriptTemplate
substFieldList fieldId fields (ExpressionTemplate body) =
ExpressionTemplate (everywhere (mkT subst) body) where
fields' = map (\(name,expr) -> (PropId noPos (Id noPos name),expr)) fields
subst [(PropId _ (Id _ id'), _)] | id' == fieldId = fields'
subst lst = lst