packages feed

reflex-jsx (empty) → 0.1.0.0

raw patch · 6 files changed

+308/−0 lines, 6 filesdep +basedep +containersdep +haskell-src-metasetup-changed

Dependencies added: base, containers, haskell-src-meta, parsec, reflex, reflex-dom, template-haskell, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright David Ackerman (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ reflex-jsx.cabal view
@@ -0,0 +1,50 @@+name:                reflex-jsx+version:             0.1.0.0+stability:           Experimental+synopsis:            Use jsx-like syntax in Reflex+description:         `reflex-jsx` is a library for writing jsx-like syntax in+                     reflex code. This can be useful for situations where you+                     have a large block of HTML with some styles, and it would+                     be easier to read as actual HTML than various reflex+                     functions.++                     It's implemented as a "QuasiQuoter", so you just import the+                     `jsx` function from ReflexJsx and generate the equivalent+                     functions that would run inside of a "MonadWidget t m".++                     Not only can you generate a block of static HTML/CSS, but you+                     can also splice in your own nodes that implement+                     "MonadWidget t m", string attribute values, or even entire+                     dynamic attribute maps.++homepage:            https://github.com/dackerman/reflex-jsx+bug-reports:         https://github.com/dackerman/reflex-jsx/issues+license:             BSD3+license-file:        LICENSE+author:              David Ackerman+maintainer:          david.w.ackerman@gmail.com+copyright:           2016 David Ackerman+category:            FRP, Web, GUI, JSX, Language+build-type:          Simple+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     ReflexJsx+                     , ReflexJsx.Parser+                     , ReflexJsx.QQ+  ghc-options:         -Wall -fwarn-tabs -funbox-strict-fields -ferror-spans+  build-depends:       base >= 4.8 && < 5+                     , parsec >= 3.1 && < 3.2+                     , reflex+                     , reflex-dom+                     , template-haskell+                     , haskell-src-meta+                     , containers+                     , text+  default-language:    Haskell2010+++source-repository head+  type:     git+  location: https://github.com/dackerman/reflex-jsx
+ src/ReflexJsx.hs view
@@ -0,0 +1,7 @@+module ReflexJsx+       ( module ReflexJsx.Parser+       , module ReflexJsx.QQ+       ) where++import ReflexJsx.Parser+import ReflexJsx.QQ
+ src/ReflexJsx/Parser.hs view
@@ -0,0 +1,147 @@+{-| 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
+ src/ReflexJsx/QQ.hs view
@@ -0,0 +1,72 @@+{-| The Quasiquoter for the reflex-jsx language++    The only import you need is "jsx", which is the quasiquoter. See the README+    for more information.+-}++{-# LANGUAGE TemplateHaskell #-}++module ReflexJsx.QQ+       ( jsx+       ) where++import qualified Language.Haskell.TH as TH+import Language.Haskell.TH.Quote+import Language.Haskell.Meta (parseExp)++import qualified Data.List as List+import qualified Data.Map as Map++import qualified Reflex.Dom as Dom++import ReflexJsx.Parser++import Prelude hiding (exp)++{-| Quasiquoter for jsx-like expressions++    Used like "[jsx| <div /> |]"+-}+jsx :: QuasiQuoter+jsx = QuasiQuoter+  { quoteExp = quoteJsxExpression+  , quotePat = undefined+  , quoteDec = undefined+  , quoteType = undefined+  }+++quoteJsxExpression :: String -> TH.ExpQ+quoteJsxExpression str = do+  exp <- parseJsx str+  outputWidgetCode exp+++outputWidgetCode :: Node -> TH.ExpQ+outputWidgetCode node =+  case node of+    Node tag attrs children -> outputNode tag attrs children+    Text content -> [| Dom.text content |]+    SplicedNode varName -> do+      let Right exp = parseExp varName+      return exp+++outputNode :: String -> Attrs -> [Node] -> TH.ExpQ+outputNode tag attrs children =+  let renderedChildren = TH.listE $ List.map outputWidgetCode children+  in case attrs of+    StaticAttrs staticAttrs ->+      let stringAttrs = TH.listE $ List.map toStringAttr staticAttrs+      in [| Dom.elAttr tag (Map.fromList $(stringAttrs)) $ sequence_ $(renderedChildren) |]+    SplicedAttrs attrExpr -> do+      let Right exp = parseExp attrExpr+      [| Dom.elDynAttr tag $(return exp) $ sequence_ $(renderedChildren) |]+++toStringAttr :: (String, AttrValue) -> TH.ExpQ+toStringAttr (key, value) = case value of+  TextVal content -> [| (key, content) |]+  ExprVal exprString -> do+    let Right exp = parseExp exprString+    [| (key, $(return exp)) |]