packages feed

reflex-dom-th-0.2.0.0: src/Reflex/Dom/TH/Parser.hs

-- | 

module Reflex.Dom.TH.Parser
( TElement(..),
  AttributeType(..),
  parseTemplate
  )
where

import Data.Char
import Data.List


import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L 
import Data.Void
import Control.Monad
import Language.Haskell.TH.Syntax

type Parser = Parsec Void String
type TTag = String
data AttributeType = Static | Dynamic deriving (Show, Lift)
type Attribute = (AttributeType, String, String)
type Ref = Int
data TElement = TElement { tTag :: TTag
                         , tRef :: Maybe Ref
                         , tAttrs :: [Attribute]
                         , tChilds :: [TElement] }
               | TText String
               | TComment String
               | TWidget String (Maybe Ref)
               deriving Show

refOpt :: Parser (Maybe Int)
refOpt = optional . try $ do
      space1
      void $ char '#'
      L.decimal <* space

openTag :: Parser (String, Maybe Int, [Attribute])
openTag =  
     between (char '<') (char '>') $ do
       tag <- many (alphaNumChar <|> char '-')
       ref <- refOpt
       space
       attrs <- attributes
       space
       return (tag, ref, attrs)

closeTag :: String -> Parser ()
closeTag tag = void $ between (string "</" >> space) (char '>') (string tag >> space)

comment :: Parser TElement
comment = TComment <$> ((string "<!--") *> (manyTill anySingle (string "-->")))


stringLiteral :: Parser String
stringLiteral = char '\"' *> manyTill L.charLiteral (char '\"')

attribute :: Parser Attribute
attribute = (Static,,) <$>  (some (alphaNumChar <|> char '-') <* char '=') <*> stringLiteral


attributes :: Parser [Attribute]
attributes = sepBy attribute space1 <* space
                     

node :: Parser TElement
node = do
  (tag, ref, attrs) <- openTag
  space
  childs <- manyTill element (closeTag tag)
  return $ TElement tag ref attrs childs

varName :: Parser String
varName = (:) <$> lowerChar <*> many alphaNumChar

widget :: Parser TElement
widget =  TWidget <$> (string "{{" *> space *> varName) <*> (refOpt <* (string "}}"))

text :: Parser TElement
text =  TText <$>  dropWhileEnd isSpace <$>  someTill anySingle (lookAhead (char '<' *> return () <|> string "{{" *> return () ))

element :: Parser TElement     
element = (comment <|>  node <|> widget <|> text) <* space
  
template :: Parser [TElement]
template = do
  space
  result <- many element
  eof
  return result


parseTemplate :: FilePath -> String -> Either (ParseErrorBundle String Void) [TElement]
parseTemplate fn = runParser template fn