packages feed

mulang-3.5.2: src/Language/Mulang/Parsers/JavaScript.hs

module Language.Mulang.Parsers.JavaScript (js, parseJavaScript) where

import Language.Mulang.Ast
import Language.Mulang.Builder
import Language.Mulang.Parsers

import Language.JavaScript.Parser.Parser (parse)
import Language.JavaScript.Parser.AST

import Data.Either ()

import Control.Fallible

js :: Parser
js = orFail . parseJavaScript'

parseJavaScript :: MaybeParser
parseJavaScript = orNothing . parseJavaScript'

parseJavaScript' :: String -> Either String Expression
parseJavaScript' = fmap (normalize . mu) . (`parse` "src")

mu :: JSNode -> Expression
mu = compact . muNode . gc
  where
    muNode (JSSourceElementsTop statements)                  = [compactMapMu statements]
    muNode (JSIdentifier "undefined")                        = [MuNull]
    muNode (JSIdentifier n)                                  = [Reference n]
    muNode (JSDecimal val)                                   = [MuNumber (read val)]
    muNode (JSExpression xs)                                 = [compactMapMu xs]
    muNode (JSLiteral "null")                                = [MuNull]
    muNode (JSLiteral "true")                                = [MuTrue]
    muNode (JSLiteral "false")                               = [MuFalse]
    muNode (JSLiteral _)                                     = []
    muNode (JSElision _)                                     = []
    muNode (JSHexInteger v)                                  = muNode (JSStringLiteral '\'' v)
    muNode (JSOctal v)                                       = muNode (JSStringLiteral '"' v)
    muNode (JSExpressionPostfix "++" [var] _)                = [muVarAssignment var (muPlus var (MuNumber 1))]
    muNode (JSExpressionPostfix "--" [var] _)                = [muVarAssignment var (muMinus var (MuNumber 1))]
    muNode (JSStringLiteral _ v)                             = [MuString v]
    muNode (JSVariables _ decls _)                           = mapMu decls
    muNode (JSArrayLiteral _ es _)                           = [MuList (mapMu es)]
    muNode (JSVarDecl var initial)                           = [muVarDeclaration var initial]
    muNode (JSWith _ _ _ _ _)                                = [Other]
    muNode (JSExpressionTernary cond _ true _ false)         = [muIf cond   true false]
    muNode (JSIf _ _ cond _ true false)                      = [muIf [cond] true false]
    muNode (JSWhile _ _ cond _ action)                       = [muWhile cond action]
    muNode (JSBlock _ exps _)                                = [compactMapMu exps]
    muNode (JSFunctionExpression _ [] _ params _ body)       = [Lambda (muParams params) (mu body)]
    muNode (JSFunctionExpression _ [name] _ params _ body)   = [muFunction  name params body]
    muNode (JSReturn _ e _)                                  = [Return (compactMapMu e)]
    muNode (JSExpressionParen _ e _)                         = [mu e]
    muNode (JSObjectLiteral _ es _)                          = [MuObject (compactMapMu es)]
    muNode (JSLabelled _ _ e)                                = [mu e]
    muNode (JSPropertyNameandValue var _ initial)            = [muVarDeclaration var initial]
    muNode (JSFunction _ name _ params _ body)               = [muFunction name params body]
    muNode (JSExpressionBinary op l _ r)                     = [Application (muOp op) [compactMapMu l, compactMapMu r]]
    muNode (JSMemberDot receptor _ selector)                 = [Send (compactMapMu receptor) (mu selector) []]
    muNode (JSThrow _ e)                                     = [Raise (mu e)]
    muNode e                                                 = [Other]



    mapMu :: [JSNode] -> [Expression]
    mapMu [] = []
    mapMu (x:y:xs)   | (JSArguments _ args _) <- gc y =  [Application (mu x) (mapMu args)] ++ mapMu xs
    mapMu (x:y:z:xs) | (JSOperator (NT (JSLiteral "=") _ _))  <- gc y =  [muVarAssignment x (mu z)] ++ mapMu xs
                     | (JSOperator (NT (JSLiteral "+=") _ _))  <- gc y =  [muVarAssignment x (muPlus x (mu z))] ++ mapMu xs
    mapMu (x:xs) = (muNode.gc) x ++ mapMu xs

    muParams :: [JSNode] -> [Pattern]
    muParams params = concatMap (muPattern.gc) params

    muOp :: String -> Expression
    muOp "=="  = Equal
    muOp "===" = Equal
    muOp "!="  = NotEqual
    muOp "!==" = NotEqual
    muOp v    = Reference v

    muPattern (JSLiteral _)    = []
    muPattern (JSIdentifier i) = [VariablePattern i]
    muPattern e                = error (show e)

    gc (NN n) = n
    gc (NT n _ _) = n

    muId = f.gc
           where f (JSIdentifier id) = id


    muEquation params body = [SimpleEquation params body]

    compactMapMu :: [JSNode] -> Expression
    compactMapMu  = compact . mapMu

    muIf :: [JSNode] -> [JSNode] -> [JSNode] -> Expression
    muIf cond true false = If (compactMapMu cond) (compactMapMu true) (compactMapMu false)

    muWhile cond action  = While (mu cond) (mu action)

    muVarDeclaration var initial = Variable (muId var) (compactMapMu initial)
    muVarAssignment  var value  = Assignment (muId var)  value

    muPlus var delta = (Application (Reference "+") [Reference (muId var), delta])
    muMinus var delta = (Application (Reference "-") [Reference (muId var), delta])

    muFunction :: JSNode -> [JSNode] -> JSNode -> Expression
    muFunction name params body = muComputation (muId name) (muParams params) (mu body)
                            where
                                muComputation name params body = (computationFor body) name (muEquation params body)

                                computationFor :: Expression -> Identifier -> [Equation] -> Expression
                                computationFor body | containsReturn body = Function
                                                    | otherwise = Procedure

                                containsReturn :: Expression -> Bool
                                containsReturn (Return _)    = True
                                containsReturn (Sequence xs) = any containsReturn xs
                                containsReturn _             = False

{-JSRegEx String
optional lb,optional block statements,optional rb
JSBreak JSNode [JSNode] JSNode
break, optional identifier, autosemi
JSCallExpression String [JSNode] [JSNode] [JSNode]
type : ., (), []; opening [ or ., contents, closing
JSCase JSNode JSNode JSNode [JSNode]
case,expr,colon,stmtlist
JSCatch JSNode JSNode JSNode [JSNode] JSNode JSNode
catch,lb,ident,[if,expr],rb,block
JSContinue JSNode [JSNode] JSNode
continue,optional identifier,autosemi
JSDefault JSNode JSNode [JSNode]
default,colon,stmtlist
JSDoWhile JSNode JSNode JSNode JSNode JSNode JSNode JSNode
do,stmt,while,lb,expr,rb,autosemi
JSExpressionPostfix String [JSNode] JSNode
type, expression, operator
JSExpressionTernary [JSNode] JSNode [JSNode] JSNode [JSNode]
cond, ?, trueval, :, falseval
JSFinally JSNode JSNode
finally,block
JSFor JSNode JSNode [JSNode] JSNode [JSNode] JSNode [JSNode] JSNode JSNode
for,lb,expr,semi,expr,semi,expr,rb.stmt
JSForIn JSNode JSNode [JSNode] JSNode JSNode JSNode JSNode
for,lb,expr,in,expr,rb,stmt
JSForVar JSNode JSNode JSNode [JSNode] JSNode [JSNode] JSNode [JSNode] JSNode JSNode
for,lb,var,vardecl,semi,expr,semi,expr,rb,stmt
JSForVarIn JSNode JSNode JSNode JSNode JSNode JSNode JSNode JSNode
for,lb,var,vardecl,in,expr,rb,stmt
JSMemberSquare [JSNode] JSNode JSNode JSNode
firstpart, lb, expr, rb
JSPropertyAccessor JSNode JSNode JSNode [JSNode] JSNode JSNode
(get|set), name, lb, params, rb, block
JSSwitch JSNode JSNode JSNode JSNode JSNode
switch,lb,expr,rb,caseblock
JSThrow JSNode JSNode
throw val
JSTry JSNode JSNode [JSNode]
try,block,rest
JSUnary String JSNode
type, operator
 -}