reflex-jsx-0.1.0.0: src/ReflexJsx/Parser.hs
{-| The Parser for the reflex-jsx language
Given a "String", @parseJsx@ outputs the AST for the language. Note that at
this point, we capture spliced expressions from the meta-language as
Strings, and parse them during the quasiquoting phase in @ReflexJsx.QQ@
-}
module ReflexJsx.Parser
( parseJsx
, Node(..)
, Attrs(..)
, AttrValue(..)
) where
import Text.Parsec (runParser, Parsec, try, eof, many, many1, between)
import Text.Parsec.Char (char, letter, noneOf, string, alphaNum, spaces)
import Control.Applicative ((<|>))
data AttrValue = TextVal String
| ExprVal String
data Attrs = SplicedAttrs String
| StaticAttrs [(String, AttrValue)]
data Node = Node String Attrs [Node]
| Text String
| SplicedNode String
parseJsx :: Monad m => String -> m Node
parseJsx s =
case runParser p () "" s of
Left err -> fail $ show err
Right e -> return e
where p = do
spaces
node <- jsxElement
spaces
eof
return node
jsxElement :: Parsec String u Node
jsxElement = do
try jsxSelfClosingElement <|> jsxNormalElement
jsxSelfClosingElement :: Parsec String u Node
jsxSelfClosingElement = do
_ <- char '<'
name <- jsxElementName
attrs <- jsxNodeAttrs
_ <- string "/>"
return (Node name attrs [])
jsxNormalElement :: Parsec String u Node
jsxNormalElement = do
(name, attrs) <- jsxOpeningElement
children <- many jsxChild
jsxClosingElement name
return (Node name attrs children)
jsxOpeningElement :: Parsec String u (String, Attrs)
jsxOpeningElement = do
_ <- char '<'
name <- jsxElementName
attrs <- jsxNodeAttrs
_ <- char '>'
return (name, attrs)
jsxNodeAttrs :: Parsec String u Attrs
jsxNodeAttrs = do
try jsxSplicedAttrMap <|> (StaticAttrs <$> many jsxNodeAttr)
jsxSplicedAttrMap :: Parsec String u Attrs
jsxSplicedAttrMap = do
name <- between (string "{...") (string "}") $ many (noneOf "}")
return $ SplicedAttrs name
jsxNodeAttr :: Parsec String u (String, AttrValue)
jsxNodeAttr = do
key <- jsxAttributeName
spaces
_ <- char '='
spaces
value <- jsxQuotedValue <|> jsxSplicedValue
spaces
return (key, value)
jsxAttributeName :: Parsec String u String
jsxAttributeName = do
many $ letter <|> char '-'
jsxQuotedValue :: Parsec String u AttrValue
jsxQuotedValue = do
contents <- between (char '"') (char '"') $ many (noneOf "\"")
return $ TextVal contents
jsxSplicedValue :: Parsec String u AttrValue
jsxSplicedValue = do
name <- between (char '{') (char '}') $ many (noneOf "}")
return $ ExprVal name
jsxClosingElement :: String -> Parsec String u ()
jsxClosingElement ele = do
_ <- string "</" *> string ele *> char '>'
return ()
jsxChild :: Parsec String u Node
jsxChild = do
try jsxText <|> try jsxSplicedNode <|> try jsxElement
jsxText :: Parsec String u Node
jsxText = do
contents <- many1 $ noneOf "{<>}"
return $ Text contents
jsxSplicedNode :: Parsec String u Node
jsxSplicedNode = do
exprString <- between (char '{') (char '}') $ many (noneOf "}")
return $ SplicedNode exprString
jsxElementName :: Parsec String u String
jsxElementName = jsxIdentifier
jsxIdentifier :: Parsec String u String
jsxIdentifier = do
name <- many1 alphaNum
spaces
return name