summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormikesol <>2020-05-22 17:32:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2020-05-22 17:32:00 (GMT)
commit417879ef184b978e7f41976801d0891cff144301 (patch)
tree2bfc8456059abc1f19eeeda36145b8690f7cb003
parent1928b8c10d660fe8e697f1825f54eaa4ac666e6f (diff)
version 0.0.0.10HEAD0.0.0.10master
-rwxr-xr-xChangeLog.md94
-rwxr-xr-xREADME.md138
-rw-r--r--plzwrk.cabal8
-rw-r--r--src/Web/Framework/Plzwrk.hs13
-rw-r--r--src/Web/Framework/Plzwrk/TH/HSX.hs146
-rw-r--r--src/Web/Framework/Plzwrk/TH/PWX.hs173
-rw-r--r--src/Web/Framework/Plzwrk/TH/QuotePWX.hs (renamed from src/Web/Framework/Plzwrk/TH/QuoteHSX.hs)232
-rw-r--r--test/HSXSpec.hs78
-rw-r--r--test/PWXSpec.hs116
-rw-r--r--test/Spec.hs4
10 files changed, 560 insertions, 442 deletions
diff --git a/ChangeLog.md b/ChangeLog.md
index f53989e..2a40e52 100755
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,45 +1,49 @@
-# Changelog for plzwrk
-
-## 0.0.0.9
-
-- Renames `Browserful` to `JSEnv`.
-- Fixes garbage collection bug for callback functions.
-
-## 0.0.0.8
-
-- Fixes a bug in the HSX parser that rejected certain valid text nodes
-
-## 0.0.0.7
-
-- Removes spurious dependencies for faster build.
-
-## 0.0.0.6
-
-- Adds `#el` mixin for element lists.
-
-## 0.0.0.5
-
-- Adds bounds for cabal packages
-- Explicity declares `Control.Monad.Fail` in `HSX.hs` to allow automated haddock builds.
-
-## 0.0.0.4
-
-- Adds `hsx` and `hsx'` for `jsx`-like manipulation.
-
-## 0.0.0.3
-
-- Adds bindings for `fetch`
-- Simplifies `Browserful` API to only contain primitives and uses utility functions to build on top of the primitives.
-
-## 0.0.0.2
-
-- Adds server side rendering via `toHTML` and `plzwrkSSR`.
-- Adds more documentation.
-
-## 0.0.0.1
-
-- Adds util functions for attribute creation like `wClass`, `wId` etc.
-
-## 0.0.0.0
-
-- Initial release.
+# Changelog for plzwrk
+
+## 0.0.0.10
+
+- Renames `hsx` to `pwx`.
+
+## 0.0.0.9
+
+- Renames `Browserful` to `JSEnv`.
+- Fixes garbage collection bug for callback functions.
+
+## 0.0.0.8
+
+- Fixes a bug in the PWX parser that rejected certain valid text nodes
+
+## 0.0.0.7
+
+- Removes spurious dependencies for faster build.
+
+## 0.0.0.6
+
+- Adds `#el` mixin for element lists.
+
+## 0.0.0.5
+
+- Adds bounds for cabal packages
+- Explicity declares `Control.Monad.Fail` in `PWX.hs` to allow automated haddock builds.
+
+## 0.0.0.4
+
+- Adds `pwx` and `pwx'` for `jsx`-like manipulation.
+
+## 0.0.0.3
+
+- Adds bindings for `fetch`
+- Simplifies `Browserful` API to only contain primitives and uses utility functions to build on top of the primitives.
+
+## 0.0.0.2
+
+- Adds server side rendering via `toHTML` and `plzwrkSSR`.
+- Adds more documentation.
+
+## 0.0.0.1
+
+- Adds util functions for attribute creation like `wClass`, `wId` etc.
+
+## 0.0.0.0
+
+- Initial release.
diff --git a/README.md b/README.md
index 40ff289..7cd6480 100755
--- a/README.md
+++ b/README.md
@@ -1,8 +1,48 @@
# plzwrk
+[![Chat on Gitter](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/meeshkan/community)
+
A Haskell front-end framework.
-## Hello world
+Available as a Hackage package: [`plzwrk`](https://hackage.haskell.org/package/plzwrk)
+
+📖 Looking for an overview? [Read our announcement blog post](https://meeshkan.com/blog/introducing-plzwrk/).
+
+## In this document:
+
+* [When to use `plzwrk`](#when-to-use-plzwrk)
+* [Examples using `plzwrk`](examples-using-plzwrk)
+ * [Hello world](#hello-world)
+ * [Aphorism machine](#aphorism-machine)
+* [Making a webpage](#making-a-webpage)
+* [Documentation](#documentation)
+* [Design of `plzwrk`](#design-of-plzwrk)
+ * [PWX](#pwx)
+ * [Hydrating with a state](#hydrating-with-a-state)
+ * [Event handlers](#event-handlers)
+* [Server-side rendering](#server-side-rendering)
+* [Testing your code](#testing-your-code)
+* [Contributing](#contributing)
+ * [Local development](#local-development)
+
+## When to use `plzwrk`
+
+`plzwrk` may be a good fit if you enjoy the benefits of programming in Haskell and you'd like to create a web app.
+
+⚠️ Warning: `plzwrk` is experimental. It is unfit for production and the syntax will change frequently, often in non-backward-compatible ways. We will try to document all of these changes in the [changelog](ChangeLog.md).
+
+Some alternatives to `plzwrk`:
+
+- [Elm](https://elm-lang.org/), a delightful language for reliable web apps.
+- [Purescript react basic](https://github.com/lumihq/purescript-react-basic), an opinionated set of bindings to the React library, optimizing for the most basic use cases.
+
+## Examples using `plzwrk`
+
+<!-- TODO: Update when PR#5 is merged -->
+
+### Hello world
+
+An example web page that says 'Hello world!'
```haskell
{-# LANGUAGE QuasiQuotes #-}
@@ -13,22 +53,22 @@ import Web.Framework.Plzwrk.Asterius
main :: IO ()
main = do
browser <- asteriusBrowser
- plzwrk'_ [hsx|<p>Hello world!</p>|] browser
+ plzwrk'_ [pwx|<p>Hello world!</p>|] browser
```
-See it [live](https://plzwrk-hello-world.surge.sh).
+[See the Hello World example live](https://plzwrk-hello-world.surge.sh).
-## Kitchen sink
+### Aphorism machine
-Check out the code [here](./kitchen-sink/Main.hs).
+An Aphorism Machine that spits out and hides universal truths on demand.
-See it [live](https://plzwrk-kitchen-sink.surge.sh).
+[Check out the source code in the `kitchen-sink` directory](./kitchen-sink/Main.hs). Or [see the Aphorism Machine live](https://plzwrk-kitchen-sink.surge.sh).
## Making a webpage
`plzwrk` uses [Asterius](https://github.com/tweag/asterius) as its backend for web development.
-A minimal flow is shown below. It assumes that you have a file called `Main.hs` in the present working directory with a function `main :: IO ()` inside of it, not unlike the Hello World above.
+A minimal flow is shown below. It assumes that you have a file called `Main.hs` in the present working directory with a function `main :: IO ()` inside of it, not unlike in our [hello world example](#hello-world).
```bash
username@hostname:~/my-dir$ docker run --rm -it -v $(pwd):/project -w /project meeshkan/plzwrk
@@ -39,81 +79,88 @@ If you're using `ahc-cabal`, compiling an application using `plzwrk` is no diffe
## Documentation
-The main documentation for `plzwrk` is on [hackage](https://hackage.haskell.org/package/plzwrk). The four importable modules are:
+The main documentation for `plzwrk` is on [Hackage](https://hackage.haskell.org/package/plzwrk).
-- `Web.Frameworks.Plzwrk` for the basic functions
-- `Web.Frameworks.Plzwrk.Tag` for helper functions to make takes like `input` or `br` if you are not using `hsx`.
-- `Web.Frameworks.Plzwrk.MockJSVal` to use a mock browser.
-- `Web.Frameworks.Plzwrk.Asterius` to use a bindings for a real browser courtesy of [Asterius](https://github.com/tweag/asterius).
+The four importable modules are:
-## Design
+- [`Web.Frameworks.Plzwrk`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk.html) for the basic functions
+- [`Web.Frameworks.Plzwrk.Tag`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk-Tag.html) for helper functions to make takes like `input` or `br` if you are not using `pwx`.
+- [`Web.Frameworks.Plzwrk.MockJSVal`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk-MockJSVal.html) to use a mock browser.
+- [`Web.Frameworks.Plzwrk.Asterius`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk-Asterius.html) to use a bindings for a real browser courtesy of [Asterius](https://github.com/tweag/asterius).
-`plzwrk` is inspired by [redux](https://redux.js.org/) for its state management. The main idea is that you have a HTML-creation function that is composed, via `<*>`, with getters from a state.
+## Design of `plzwrk`
-```haskell
+`plzwrk` is inspired by [Redux](https://redux.js.org/) for its state management. The main idea is that you have an HTML-creation function that is composed, via `<*>`, with getters from a state.
+```haskell
-- State
data MyState = MkMyState { _name :: Text, age :: Int, _tags :: [Text] }
-- Function hydrating a DOM with elementse from the state
makeP = (\name age ->
- [hsx'|<p>#t{concat [name, " is the name and ", show age, " is my age."]}#</p>|])
+ [pwx'|<p>#t{concat [name, " is the name and ", show age, " is my age."]}#</p>|])
<$> _name
<*> _age
--- The same function using functional tags instead of hsx
+-- The same function using functional tags instead of pwx
makeP = (\name age ->
p'__ concat [name, " is the name and ", show age, " is my age."])
<$> _name
<*> _age
```
-HTML-creation functions can be nested, allowing for powerful abstractions.
+HTML-creation functions can be nested, allowing for powerful abstractions:
```haskell
nested = div_ (take 10 $ repeat makeP)
```
-### HSX
+### PWX
-`hsx` is not unlike `jsx`. The main difference is that instead of using just `{}`, `hsx` uses three different varieties of `#{}#`
+`pwx` is similar to [`jsx`](https://reactjs.org/docs/introducing-jsx.html). The main difference is that instead of only using `{}`, `pwx` uses four different varieties of `#{}#`:
-- `#e{}#` for an element
-- `#el{}#` for a list of elements
-- `#t{}#` for a text node or text attribute
-- `#c{}#` for a callback attribute
+- `#e{}#` for a single element.
+- `#el{}#` for a list of elements.
+- `#t{}#` for a single piece of text, either as a node in the body of an element or as a text attribute.
+- `#c{}#` for a callback attribute.
### Hydrating with a state
-HTML-creation functions use an apostrophe after the tag name (ie `div'`) if they accept arguments from a state and no apostrophe (ie `div`) if they don't. The same is true of `hsx`, ie `[hsx|<br />|]` versus `(s -> [hsx'|<br />|])`. Additionally, HTML-creation functions for tags that do not have any attributes (class, style etc) are marked with a trailing underscore (`div_ [p__ "hello"]`), and tags that only accept text are marked with two trailing underscores (`p__ "hello"`).
+HTML-creation functions use an apostrophe after the tag name (ie `div'`) if they accept arguments from a state and no apostrophe (ie `div`) if they don't. The same is true of `pwx`, ie `[pwx|<br />|]` versus `(s -> [pwx'|<br />|])`.
+
+Additionally, HTML-creation functions for tags that don't have any attributes (class, style, etc) are marked with a trailing underscore (ie `div_ [p__ "hello"]`), and tags that only accept text are marked with two trailing underscores (ie `p__ "hello"`).
### Event handlers
-Event handlers take two arguments - an opaque pointer to the event and the current state - and return a new state (which could just be the original state) in the `IO` monad. For example, if the state is an integer, a valid event handler could be:
+Event handlers take two arguments - an opaque pointer to the event and the current state. Then, it returns a new state (which could also be the original state) in the `IO` monad.
-```
+For example, if the state is an integer, a valid event handler could be:
+
+```haskell
eh :: opq -> Int -> IO Int
eh _ i = pure $ i + 1
-dom = [hsx|<button click=#c{eh}#>Click here</button>|]
+dom = [pwx|<button click=#c{eh}#>Click here</button>|]
```
-To handle events (ie extract values from input events, etc) you can use one of the functions exported by `Web.Framework.Plzwrk`. Please see the [hackage documentation](https://hackage.haskell.org/package/plzwrk) for more information.
+To handle events, you can use one of the functions exported by `Web.Framework.Plzwrk`. This could be useful to extract values from input events, for instance. Please see the [Hackage documentation](https://hackage.haskell.org/package/plzwrk) for more information.
-> If you are using the Asterius backend, callback functions are still quite fragile and subject to breakage. The less third-party libraries you use in them, the better. For example, avoid using `Data.Text` and `aeson` if possible.
+## Server-side rendering
-## Server side rendering
+`plzwrk` supports server-side rendering. To do this, you have to compile your site twice:
+- Once using `ahc-cabal`. This uses the [procedure outlined in the last section](#design-of-plzwrk) to create any JavaScript you need (ie event handlers), and
+- Once using plain old `cabal` to create the inital HTML.
-Plzwrk supports server side rendering. To do this, you have to compile your site twice:
-- once using `ahc-cabal` using the procedure above to create any JavaScript you need (ie event handlers), and
-- once using plain old `cabal` to create the inital HTML.
+When compiling using `ahc-cabal`, make sure to use the [`plzwrkSSR`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk.html#v:plzwrkSSR) family of functions. These functions will look for pre-existing elements in the DOM and attach event listeners to them instead of creating elements from scratch.
-When compiling using `ahc-cabal`, make sure to use the `plzwrkSSR` family of functions. These functions will look for pre-existing elements in the DOM and attach event listeners to them instead of creating elements from scratch. Additionally, if the static website needs to be initialized with data (ie using the result of an HTTP response made on the server), you'll need to pass these values dynamically to the function that calls `plzwrkSSR`. You can do this using the `foreign export` syntax as described in the [Asterius documentation](https://asterius.netlify.app/jsffi.html#jsffi-static-exports).
+There may also be times that the static website needs to be initialized with data (ie using the result of an HTTP response made on the server). In this case, you'll need to pass these values dynamically to the function that calls `plzwrkSSR`. You can do this using the `foreign export` syntax as described in the [Asterius documentation](https://asterius.netlify.app/jsffi.html#jsffi-static-exports).
-When compiling with `cabal`, you'll likely be using it to output an HTML document or build a server that serves your website as `text/html`. Regardless of the approach, you should use `toHTML` to create the part of the initial DOM controlled by plzwrk. Also, in your HTML, make sure to include a link to the script(s) produced by `ahc-dist` and, if needed, make sure to call your exported functions.
+When compiling with `cabal`, you'll likely be using it to output an HTML document or build a server that serves your website as `text/html`. Regardless of the approach, you should use [`toHTML`](https://hackage.haskell.org/package/plzwrk-0.0.0.9/docs/Web-Framework-Plzwrk.html#v:toHTML) to create the part of the initial DOM controlled by `plzwrk`. In your HTML, make sure to include a link to the script(s) produced by `ahc-dist`. Also, if needed, make sure to call your exported functions.
## Testing your code
-Plzwrk comes with a mock browser that can act as a drop-in replacement for your browser. Use this in your tests.
+`plzwrk` comes with a mock browser that can act as a drop-in replacement for your browser.
+
+You can use this in your tests:
```haskell
import Web.Framework.Plzwrk.MockJSVal
@@ -123,17 +170,14 @@ main :: IO ()
print "Now I'm using the mock browser."
```
-## When to use
-
-Plzwrk may be a good fit if you enjoy the benefits of programming in Haskell and would like to create a web app.
+## Contributing
-Plzwrk is experimental. It is unfit for production and the syntax will change frequently, often in non-backward-compatible ways. We will try to document all of these changes in the [changelog](ChangeLog.md).
+Thanks for your interest in contributing! If you have a bug or feature request, please file an [issue](https://github.com/meeshkan/plzwrk/issues). Or if you'd like to hack at the code base, open a [pull request](https://github.com/meeshkan/plzwrk/issues).
-Some alternatives to `plzwrk`:
+Please note that this project is governed by the [Meeshkan Community Code of Conduct](https://github.com/meeshkan/code-of-conduct). By participating, you agree to abide by its terms.
-- [Elm](https://elm-lang.org/), a delightful language for reliable web apps.
-- [Purescript react basic](https://github.com/lumihq/purescript-react-basic), an opinionated set of bindings to the React library, optimizing for the most basic use cases.
-
-## Contributing
+### Local development
-Thanks for your interest in contributing! If you have a bug or feature request, please file an [issue](https://github.com/meeshkan/plzwrk/issues), or if you'd like to hack at the code base, please propose a [pull request](https://github.com/meeshkan/plzwrk/issues).
+1. Clone this repository: `git clone https://github.com/meeshkan/plzwrk.git`
+1. Move into the directory: `cd plzwrk`
+1. Set up your local environment: You can use this guide from [The Haskell Tool Stack](https://docs.haskellstack.org/en/stable/README/) for reference.
diff --git a/plzwrk.cabal b/plzwrk.cabal
index 0d01e4d..5681c7c 100644
--- a/plzwrk.cabal
+++ b/plzwrk.cabal
@@ -7,7 +7,7 @@ cabal-version: 1.12
-- hash: 8099ceb0d406f862d89306de053aba4c75999a9e0d74ac6a502458d503e7dcc1
name: plzwrk
-version: 0.0.0.9
+version: 0.0.0.10
category: Web
synopsis: A front-end framework
description: Please see the README on GitHub at <https://github.com/meeshkan/plzwrk#readme>
@@ -44,8 +44,8 @@ library
, Web.Framework.Plzwrk.JSEnv
, Web.Framework.Plzwrk.Domify
, Web.Framework.Plzwrk.Util
- , Web.Framework.Plzwrk.TH.HSX
- , Web.Framework.Plzwrk.TH.QuoteHSX
+ , Web.Framework.Plzwrk.TH.PWX
+ , Web.Framework.Plzwrk.TH.QuotePWX
hs-source-dirs:
src
build-depends:
@@ -74,7 +74,7 @@ test-suite plzwrk-test
main-is: Spec.hs
other-modules:
DOMSpec
- , HSXSpec
+ , PWXSpec
, Paths_plzwrk
hs-source-dirs:
test
diff --git a/src/Web/Framework/Plzwrk.hs b/src/Web/Framework/Plzwrk.hs
index bd522a4..06cd9fe 100644
--- a/src/Web/Framework/Plzwrk.hs
+++ b/src/Web/Framework/Plzwrk.hs
@@ -22,10 +22,14 @@ module Web.Framework.Plzwrk
, PwNode(..)
, PwAttribute(..)
, JSEnv(..)
- -- hsx
- , hsx
- , hsx'
+ -- pwx
+ , pwx
+ , pwx'
, plusplus
+ , parsePWX
+ , parsePWX_
+ , PWX(..)
+ , PWXAttribute(..)
-- util
, pF
, pT
@@ -57,4 +61,5 @@ import Web.Framework.Plzwrk.Base
import Web.Framework.Plzwrk.JSEnv
import Web.Framework.Plzwrk.Domify
import Web.Framework.Plzwrk.Util
-import Web.Framework.Plzwrk.TH.QuoteHSX \ No newline at end of file
+import Web.Framework.Plzwrk.TH.PWX
+import Web.Framework.Plzwrk.TH.QuotePWX
diff --git a/src/Web/Framework/Plzwrk/TH/HSX.hs b/src/Web/Framework/Plzwrk/TH/HSX.hs
deleted file mode 100644
index 3454fde..0000000
--- a/src/Web/Framework/Plzwrk/TH/HSX.hs
+++ /dev/null
@@ -1,146 +0,0 @@
-module Web.Framework.Plzwrk.TH.HSX
- ( HSXAttribute(..)
- , HSX(..)
- , parseHSX
- ------------ for debugging
- , endTag
- , elementHSXBody
- , attribute
- , tag
- , text
- , haskellCodeNodes
- , haskellTxtAttr
- , haskellTxtNode
- , haskellCodeNode
- )
-where
-
-import Control.Applicative ( (<*)
- , (*>)
- , (<$>)
- , (<$)
- )
-import Control.Monad ( void )
-import qualified Control.Monad.Fail as MF
-import Data.Char
-import Data.List ( foldl' )
-import Text.Parsec
-import Text.Parsec.String
-
-data HSXAttribute = HSXStringAttribute String
- | HSXHaskellCodeAttribute String
- | HSXHaskellTxtAttribute String deriving (Show, Eq)
-
-data HSX = HSXElement String [(String, HSXAttribute)] [HSX]
- | HSXSelfClosingTag String [(String, HSXAttribute)]
- | HSXHaskellCode String
- | HSXHaskellCodeList String
- | HSXHaskellText String
- | HSXBody String
- deriving (Show, Eq)
-
-hsx :: Parser HSX
-hsx = tag
-
-tag = do
- char '<'
- ws
- name <- many (letter <|> digit)
- ws
- attr <- many attribute
- ws
- close <- try (string "/>" <|> string ">")
- if length close == 2
- then return (HSXSelfClosingTag name attr)
- else do
- elementBody <- many elementHSXBody
- endTag name
- ws
- return (HSXElement name attr elementBody)
-
-elementHSXBody =
- ws
- *> ( try tag
- <|> try haskellCodeNode
- <|> try haskellCodeNodes
- <|> try haskellTxtNode
- <|> text
- <?> "A tag, a piece of code or some text"
- )
-
-endTag :: String -> Parser String
-endTag str = string "</" *> string str <* char '>'
-
-text = HSXBody <$> many1 (noneOf "><")
-
-stringAttribute = do
- char '"'
- value <- many (noneOf ['"'])
- char '"'
- return $ HSXStringAttribute value
-
-haskellTxtAttr = do
- string "#t{"
- value <- manyTill anyChar (string "}#")
- ws
- return $ HSXHaskellTxtAttribute value
-
-makeBracketed cmd contain = do
- let start = "#" <> cmd <> "{"
- let end = "}#"
- string start
- value <- manyTill anyChar (string end)
- ws
- return $ if contain then start <> value <> end else value
-
-haskellCodeAttr = do
- value <- makeBracketed "c" False
- return $ HSXHaskellCodeAttribute value
-
-haskellCodeNode :: Parser HSX
-haskellCodeNode = do
- value <- makeBracketed "e" False
- return $ HSXHaskellCode value
-
-haskellCodeNodes :: Parser HSX
-haskellCodeNodes = do
- value <- makeBracketed "el" False
- return $ HSXHaskellCodeList value
-
-haskellTxtNode :: Parser HSX
-haskellTxtNode = do
- value <- makeBracketed "t" False
- return $ HSXHaskellText value
-
-attribute = do
- name <- many (noneOf "= />")
- ws
- char '='
- ws
- value <- stringAttribute <|> try haskellCodeAttr <|> haskellTxtAttr
- ws
- return (name, value)
-
-ws :: Parser ()
-ws = void $ many $ oneOf " \t\r\n"
-
-parseHSX :: MF.MonadFail m => (String, Int, Int) -> String -> m HSX
-parseHSX (file, line, col) s = case runParser p () "" s of
- Left err -> MF.fail $ show err
- Right e -> return e
- where
- p = do
- updatePosition file line col
- ws
- e <- hsx
- ws
- eof
- return e
-
-updatePosition file line col = do
- pos <- getPosition
- setPosition
- $ (flip setSourceName) file
- $ (flip setSourceLine) line
- $ (flip setSourceColumn) col
- $ pos
diff --git a/src/Web/Framework/Plzwrk/TH/PWX.hs b/src/Web/Framework/Plzwrk/TH/PWX.hs
new file mode 100644
index 0000000..4731059
--- /dev/null
+++ b/src/Web/Framework/Plzwrk/TH/PWX.hs
@@ -0,0 +1,173 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Web.Framework.Plzwrk.TH.PWX
+ ( PWXAttribute(..)
+ , PWX(..)
+ , parsePWX
+ , parsePWX_
+ ------------ for debugging
+ , endTag
+ , elementPWXBody
+ , attribute
+ , tag
+ , text
+ , haskellCodeNodes
+ , haskellTxtAttr
+ , haskellTxtNode
+ , haskellCodeNode
+ )
+where
+
+import Control.Applicative ( (<*)
+ , (*>)
+ , (<$>)
+ , (<$)
+ )
+import Control.Monad ( void )
+import qualified Control.Monad.Fail as MF
+import Data.Char
+import Data.List ( foldl' )
+import Text.Parsec
+import Text.Parsec.String
+
+type PWXParser = ParsecT String ()
+
+data PWXAttribute = PWXStringAttribute String
+ | PWXHaskellCodeAttribute String
+ | PWXHaskellTxtAttribute String deriving (Show, Eq)
+
+data PWX = PWXElement
+ { _pwxElement_tag :: String
+ , _pwxElement_attributes :: [(String, PWXAttribute)]
+ , _pwxElement_children :: [PWX]
+ }
+ | PWXSelfClosingTag
+ { _pwxSelfClosingTag_tag :: String
+ , _pwxSelfClosingTag_attributes :: [(String, PWXAttribute)]
+ }
+ | PWXHaskellCode { _pwxHaskellCode_code :: String }
+ | PWXHaskellCodeList { _pwxHaskellCodeList_codeList :: String }
+ | PWXHaskellText { _pwxHaskellText_text :: String }
+ | PWXBody { _pwxBody_body :: String }
+ deriving (Show, Eq)
+
+pwx :: (Monad m) => PWXParser m PWX
+pwx = tag
+
+tag :: (Monad m) => PWXParser m PWX
+tag = do
+ char '<'
+ ws
+ name <- many (letter <|> digit)
+ ws
+ attr <- many attribute
+ ws
+ close <- try (string "/>" <|> string ">")
+ if length close == 2
+ then return (PWXSelfClosingTag name attr)
+ else do
+ elementBody <- many elementPWXBody
+ endTag name
+ ws
+ return (PWXElement name attr elementBody)
+
+elementPWXBody :: (Monad m) => PWXParser m PWX
+elementPWXBody =
+ ws
+ *> ( try tag
+ <|> try haskellCodeNode
+ <|> try haskellCodeNodes
+ <|> try haskellTxtNode
+ <|> text
+ <?> "A tag, a piece of code or some text"
+ )
+
+endTag :: (Monad m) => String -> PWXParser m String
+endTag str = string "</" *> string str <* char '>'
+
+text :: (Monad m) => PWXParser m PWX
+text = PWXBody <$> many1 (noneOf "><")
+
+stringAttribute :: (Monad m) => PWXParser m PWXAttribute
+stringAttribute = do
+ char '"'
+ value <- many (noneOf ['"'])
+ char '"'
+ return $ PWXStringAttribute value
+
+makeBracketed :: (Monad m) => String -> Bool -> PWXParser m String
+makeBracketed cmd contain = do
+ let start = "#" <> cmd <> "{"
+ let end = "}#"
+ string start
+ value <- manyTill anyChar (try (string end))
+ ws
+ return $ if contain then start <> value <> end else value
+
+haskellCodeAttr :: (Monad m) => PWXParser m PWXAttribute
+haskellCodeAttr = do
+ value <- makeBracketed "c" False
+ return $ PWXHaskellCodeAttribute value
+
+haskellCodeNode :: (Monad m) => PWXParser m PWX
+haskellCodeNode = do
+ value <- makeBracketed "e" False
+ return $ PWXHaskellCode value
+
+haskellCodeNodes :: (Monad m) => PWXParser m PWX
+haskellCodeNodes = do
+ value <- makeBracketed "el" False
+ return $ PWXHaskellCodeList value
+
+haskellTxtNode :: (Monad m) => PWXParser m PWX
+haskellTxtNode = do
+ value <- makeBracketed "t" False
+ return $ PWXHaskellText value
+
+haskellTxtAttr :: (Monad m) => PWXParser m PWXAttribute
+haskellTxtAttr = do
+ value <- makeBracketed "t" False
+ return $ PWXHaskellTxtAttribute value
+
+attribute :: (Monad m) => PWXParser m (String, PWXAttribute)
+attribute = do
+ name <- many (noneOf "= />")
+ ws
+ char '='
+ ws
+ value <- stringAttribute <|> try haskellCodeAttr <|> haskellTxtAttr
+ ws
+ return (name, value)
+
+ws :: (Monad m) => PWXParser m ()
+ws = void $ many $ oneOf " \t\r\n"
+
+parsePWX_ :: (Monad m) => String -> m PWX
+parsePWX_ s = do
+ res <- runParserT pwx () "" s
+ case res of
+ Left err -> error $ show err
+ Right e -> return e
+
+parsePWX :: (Monad m) => (String, Int, Int) -> String -> m PWX
+parsePWX (file, line, col) s = do
+ res <- runParserT p () "" s
+ case res of
+ Left err -> error $ show err
+ Right e -> return e
+ where
+ p = do
+ updatePosition file line col
+ ws
+ e <- pwx
+ ws
+ eof
+ return e
+
+updatePosition file line col = do
+ pos <- getPosition
+ setPosition
+ $ (flip setSourceName) file
+ $ (flip setSourceLine) line
+ $ (flip setSourceColumn) col
+ $ pos
diff --git a/src/Web/Framework/Plzwrk/TH/QuoteHSX.hs b/src/Web/Framework/Plzwrk/TH/QuotePWX.hs
index 1d146ed..5343ac9 100644
--- a/src/Web/Framework/Plzwrk/TH/QuoteHSX.hs
+++ b/src/Web/Framework/Plzwrk/TH/QuotePWX.hs
@@ -1,116 +1,116 @@
-module Web.Framework.Plzwrk.TH.QuoteHSX
- ( hsx
- , hsx'
- , plusplus
- )
-where
-
-import Data.List.Split
-import qualified Data.Hashable as H
-import qualified Language.Haskell.TH as TH
-import Language.Haskell.Meta.Parse ( parseExp )
-import Language.Haskell.TH.Quote
-import Language.Haskell.TH.Syntax
-import Web.Framework.Plzwrk.TH.HSX
-import Web.Framework.Plzwrk.Base
-import qualified Data.HashMap.Strict as HM
-import qualified Data.Set as S
-
-
-hsx :: QuasiQuoter
-hsx = QuasiQuoter { quoteExp = quoteExprExp True
- , quotePat = undefined
- , quoteDec = undefined
- , quoteType = undefined
- }
-
-hsx' :: QuasiQuoter
-hsx' = QuasiQuoter { quoteExp = quoteExprExp False
- , quotePat = undefined
- , quoteDec = undefined
- , quoteType = undefined
- }
-
-haskize y = either
- (\s -> TH.appE
- (TH.varE (TH.mkName "error"))
- (TH.litE (TH.StringL $ "Could not parse: " <> y <> " due to error " <> s))
- )
- returnQ
- (parseExp y)
-
-plusplus :: [a] -> [a] -> [a]
-plusplus = (++)
-
-hsxAttributeToExpQ :: (String, HSXAttribute) -> TH.Q TH.Exp
-hsxAttributeToExpQ (k, HSXStringAttribute v) = TH.tupE
- [ TH.litE (TH.StringL k)
- , TH.lamE
- [TH.varP (TH.mkName "_")]
- (TH.appE (TH.conE (TH.mkName "PwTextAttribute")) (TH.litE (TH.StringL v)))
- ]
-hsxAttributeToExpQ (k, HSXHaskellCodeAttribute v) = TH.tupE
- [ TH.litE (TH.StringL k)
- , TH.lamE [TH.varP (TH.mkName "_")]
- (TH.appE (TH.conE (TH.mkName "PwFunctionAttribute")) (haskize v))
- ]
-hsxAttributeToExpQ (k, HSXHaskellTxtAttribute v) = TH.tupE
- [ TH.litE (TH.StringL k)
- , TH.lamE [TH.varP (TH.mkName "_")]
- (TH.appE (TH.conE (TH.mkName "PwTextAttribute")) (haskize v))
- ]
-
-
-wrapInLambda :: Bool -> TH.Q TH.Exp -> TH.Q TH.Exp
-wrapInLambda True e = TH.lamE [TH.varP (TH.mkName "_")] e
-wrapInLambda False e = e
-
-asList :: Bool -> TH.Q TH.Exp -> TH.Q TH.Exp
-asList b e = if b then TH.listE [e] else e
-
-hsxToExpQ :: Bool -> Bool -> HSX -> TH.Q TH.Exp
-hsxToExpQ lam returnAsList (HSXHaskellCode y) = asList returnAsList (haskize y)
-hsxToExpQ lam returnAsList (HSXHaskellCodeList y) = haskize y
-hsxToExpQ lam returnAsList (HSXHaskellText y) = asList
- returnAsList
- (wrapInLambda True $ TH.appE (TH.conE (TH.mkName "PwTextNode")) (haskize y))
-hsxToExpQ lam returnAsList (HSXElement tag attrs elts) = asList
- returnAsList
- (wrapInLambda lam $ foldl
- TH.appE
- (TH.conE (TH.mkName "PwElement"))
- [ TH.litE (TH.StringL tag)
- , TH.listE (fmap hsxAttributeToExpQ attrs)
- , foldl
- TH.appE
- (TH.varE (TH.mkName "foldr"))
- [ TH.varE (TH.mkName "plusplus")
- , TH.conE (TH.mkName "[]")
- , TH.listE (fmap (hsxToExpQ True True) elts)
- ]
- ]
- )
-hsxToExpQ lam returnAsList (HSXSelfClosingTag tag attrs) = asList
- returnAsList
- (wrapInLambda lam $ foldl
- TH.appE
- (TH.conE (TH.mkName "PwElement"))
- [ TH.litE (TH.StringL tag)
- , TH.listE (fmap hsxAttributeToExpQ attrs)
- , TH.conE (TH.mkName "[]")
- ]
- )
-hsxToExpQ lam returnAsList (HSXBody b) = asList
- returnAsList
- ( wrapInLambda lam
- $ TH.appE (TH.conE (TH.mkName "PwTextNode")) (TH.litE (TH.StringL b))
- )
-
-quoteExprExp b s = do
- pos <- getPosition
- result <- parseHSX pos s
- hsxToExpQ b False result
-
-getPosition = fmap transPos TH.location where
- transPos loc =
- (TH.loc_filename loc, fst (TH.loc_start loc), snd (TH.loc_start loc))
+module Web.Framework.Plzwrk.TH.QuotePWX
+ ( pwx
+ , pwx'
+ , plusplus
+ )
+where
+
+import Data.List.Split
+import qualified Data.Hashable as H
+import qualified Language.Haskell.TH as TH
+import Language.Haskell.Meta.Parse ( parseExp )
+import Language.Haskell.TH.Quote
+import Language.Haskell.TH.Syntax
+import Web.Framework.Plzwrk.TH.PWX
+import Web.Framework.Plzwrk.Base
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Set as S
+
+
+pwx :: QuasiQuoter
+pwx = QuasiQuoter { quoteExp = quoteExprExp True
+ , quotePat = undefined
+ , quoteDec = undefined
+ , quoteType = undefined
+ }
+
+pwx' :: QuasiQuoter
+pwx' = QuasiQuoter { quoteExp = quoteExprExp False
+ , quotePat = undefined
+ , quoteDec = undefined
+ , quoteType = undefined
+ }
+
+haskize y = either
+ (\s -> TH.appE
+ (TH.varE (TH.mkName "error"))
+ (TH.litE (TH.StringL $ "Could not parse: " <> y <> " due to error " <> s))
+ )
+ returnQ
+ (parseExp y)
+
+plusplus :: [a] -> [a] -> [a]
+plusplus = (++)
+
+pwxAttributeToExpQ :: (String, PWXAttribute) -> TH.Q TH.Exp
+pwxAttributeToExpQ (k, PWXStringAttribute v) = TH.tupE
+ [ TH.litE (TH.StringL k)
+ , TH.lamE
+ [TH.varP (TH.mkName "_")]
+ (TH.appE (TH.conE (TH.mkName "PwTextAttribute")) (TH.litE (TH.StringL v)))
+ ]
+pwxAttributeToExpQ (k, PWXHaskellCodeAttribute v) = TH.tupE
+ [ TH.litE (TH.StringL k)
+ , TH.lamE [TH.varP (TH.mkName "_")]
+ (TH.appE (TH.conE (TH.mkName "PwFunctionAttribute")) (haskize v))
+ ]
+pwxAttributeToExpQ (k, PWXHaskellTxtAttribute v) = TH.tupE
+ [ TH.litE (TH.StringL k)
+ , TH.lamE [TH.varP (TH.mkName "_")]
+ (TH.appE (TH.conE (TH.mkName "PwTextAttribute")) (haskize v))
+ ]
+
+
+wrapInLambda :: Bool -> TH.Q TH.Exp -> TH.Q TH.Exp
+wrapInLambda True e = TH.lamE [TH.varP (TH.mkName "_")] e
+wrapInLambda False e = e
+
+asList :: Bool -> TH.Q TH.Exp -> TH.Q TH.Exp
+asList b e = if b then TH.listE [e] else e
+
+pwxToExpQ :: Bool -> Bool -> PWX -> TH.Q TH.Exp
+pwxToExpQ lam returnAsList (PWXHaskellCode y) = asList returnAsList (haskize y)
+pwxToExpQ lam returnAsList (PWXHaskellCodeList y) = haskize y
+pwxToExpQ lam returnAsList (PWXHaskellText y) = asList
+ returnAsList
+ (wrapInLambda True $ TH.appE (TH.conE (TH.mkName "PwTextNode")) (haskize y))
+pwxToExpQ lam returnAsList (PWXElement tag attrs elts) = asList
+ returnAsList
+ (wrapInLambda lam $ foldl
+ TH.appE
+ (TH.conE (TH.mkName "PwElement"))
+ [ TH.litE (TH.StringL tag)
+ , TH.listE (fmap pwxAttributeToExpQ attrs)
+ , foldl
+ TH.appE
+ (TH.varE (TH.mkName "foldr"))
+ [ TH.varE (TH.mkName "plusplus")
+ , TH.conE (TH.mkName "[]")
+ , TH.listE (fmap (pwxToExpQ True True) elts)
+ ]
+ ]
+ )
+pwxToExpQ lam returnAsList (PWXSelfClosingTag tag attrs) = asList
+ returnAsList
+ (wrapInLambda lam $ foldl
+ TH.appE
+ (TH.conE (TH.mkName "PwElement"))
+ [ TH.litE (TH.StringL tag)
+ , TH.listE (fmap pwxAttributeToExpQ attrs)
+ , TH.conE (TH.mkName "[]")
+ ]
+ )
+pwxToExpQ lam returnAsList (PWXBody b) = asList
+ returnAsList
+ ( wrapInLambda lam
+ $ TH.appE (TH.conE (TH.mkName "PwTextNode")) (TH.litE (TH.StringL b))
+ )
+
+quoteExprExp b s = do
+ pos <- getPosition
+ result <- parsePWX pos s
+ pwxToExpQ b False result
+
+getPosition = fmap transPos TH.location where
+ transPos loc =
+ (TH.loc_filename loc, fst (TH.loc_start loc), snd (TH.loc_start loc))
diff --git a/test/HSXSpec.hs b/test/HSXSpec.hs
deleted file mode 100644
index 6115ee3..0000000
--- a/test/HSXSpec.hs
+++ /dev/null
@@ -1,78 +0,0 @@
-{-# LANGUAGE QuasiQuotes #-}
-
-module HSXSpec
- ( hsxSpec
- )
-where
-
-import Control.Monad
-import qualified Data.HashMap.Strict as HM
-import Control.Monad.Reader
-import Data.IORef
-import Test.Hspec
-import Web.Framework.Plzwrk
-
-hsxSpec = describe "HSXParser" $ do
- it "Parses simple hsx" $ do
- let dom = [hsx|<p>Hello world!</p>|]
- -- we use () for an empty state
-
- _elt_tag (dom ()) `shouldBe` "p"
- _tn_text (head (_elt_children (dom ())) ()) `shouldBe` "Hello world!"
- it "Parses hsx with an event listener" $ do
- let dom = [hsx|
- <h1 id="foo" style="position:absolute">
- <a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>
- </h1>
- |]
- _elt_tag (dom 3) `shouldBe` "h1"
- _elt_tag (((_elt_children (dom 5)) !! 0) 3) `shouldBe` "a"
- let attrs = (_elt_attrs (((_elt_children (dom 1)) !! 0) 1))
- let clickAttr = (filter (\(x, _) -> x == "click") attrs) !! 0
- let mf (PwFunctionAttribute f) = f
- let cf = mf ((snd clickAttr) 0)
- res <- cf () 1
- res `shouldBe` 42
- it "Parses hsx with sub-hsx" $ do
- let mylink = [hsx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
- let dom = [hsx|
- <h1 id="foo" style="position:absolute">
- #e{mylink}#
- #t{"hello world"}#
- </h1>
- |]
- _elt_tag (dom 3) `shouldBe` "h1"
- _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
- _tn_text ((_elt_children (dom 5) !! 1) 3) `shouldBe` "hello world"
- 1 `shouldBe` 1
- it "Parses hsx with a list of elements" $ do
- let mylink = [hsx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
- let dom = [hsx|
- <h1 id="foo" style="position:absolute">
- #el{take 10 $ repeat mylink}#
- #t{"hello world"}#
- </h1>
- |]
- _elt_tag (dom 3) `shouldBe` "h1"
- _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
- _elt_tag ((_elt_children (dom 5) !! 6) 3) `shouldBe` "a"
- _tn_text ((_elt_children (dom 5) !! 10) 3) `shouldBe` "hello world"
- it "Parses hsx mixing text and not text" $ do
- let mylink = [hsx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
- let dom = [hsx|
- <h1 id="foo" style="position:absolute">
- <div>Hello <span>world</span> </div>
- </h1>
- |]
- _elt_tag (dom 3) `shouldBe` "h1"
- _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "div"
- _elt_tag ((_elt_children (head (_elt_children (dom 5)) 3) !! 1) 5) `shouldBe` "span"
- it "Parses hsx'" $ do
- let mylink = [hsx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
- let dom = (\st -> [hsx'|
- <h1 id="foo" style=#t{"position:absolute"}#>
- #e{mylink}#
- </h1>
- |])
- _elt_tag (dom 3) `shouldBe` "h1"
- _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
diff --git a/test/PWXSpec.hs b/test/PWXSpec.hs
new file mode 100644
index 0000000..edb1296
--- /dev/null
+++ b/test/PWXSpec.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module PWXSpec
+ ( pwxSpec
+ )
+where
+
+import Control.Monad
+import qualified Data.HashMap.Strict as HM
+import Control.Monad.Reader
+import Data.IORef
+import Test.Hspec
+import Web.Framework.Plzwrk
+
+pwxSpec = describe "PWXParser" $ do
+ it "Parses simple pwx" $ do
+ let dom = [pwx|<p>Hello world!</p>|]
+ -- we use () for an empty state
+
+ _elt_tag (dom ()) `shouldBe` "p"
+ _tn_text (head (_elt_children (dom ())) ()) `shouldBe` "Hello world!"
+ it "Parses pwx with an event listener" $ do
+ let dom = [pwx|
+ <h1 id="foo" style="position:absolute">
+ <a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>
+ </h1>
+ |]
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (((_elt_children (dom 5)) !! 0) 3) `shouldBe` "a"
+ let attrs = (_elt_attrs (((_elt_children (dom 1)) !! 0) 1))
+ let clickAttr = (filter (\(x, _) -> x == "click") attrs) !! 0
+ let mf (PwFunctionAttribute f) = f
+ let cf = mf ((snd clickAttr) 0)
+ res <- cf () 1
+ res `shouldBe` 42
+ it "Parses pwx with sub-pwx" $ do
+ let mylink = [pwx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
+ let dom = [pwx|
+ <h1 id="foo" style="position:absolute">
+ #e{mylink}#
+ #t{"hello world"}#
+ </h1>
+ |]
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
+ _tn_text ((_elt_children (dom 5) !! 1) 3) `shouldBe` "hello world"
+ 1 `shouldBe` 1
+ it "Parses terse pwx with sub-pwx" $ do
+ let mylink = [pwx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
+ let
+ dom
+ = [pwx|<h1 id="foo" style="position:absolute">#e{mylink}##t{"hello world"}#</h1>|]
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
+ _tn_text ((_elt_children (dom 5) !! 1) 3) `shouldBe` "hello world"
+ 1 `shouldBe` 1
+ it "Parses pwx with a list of elements" $ do
+ let mylink = [pwx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
+ let dom = [pwx|
+ <h1 id="foo" style="position:absolute">
+ #el{take 10 $ repeat mylink}#
+ #t{"hello world"}#
+ </h1>
+ |]
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
+ _elt_tag ((_elt_children (dom 5) !! 6) 3) `shouldBe` "a"
+ _tn_text ((_elt_children (dom 5) !! 10) 3) `shouldBe` "hello world"
+ it "Parses pwx mixing text and not text" $ do
+ let mylink = [pwx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
+ let dom = [pwx|
+ <h1 id="foo" style="position:absolute">
+ <div>Hello <span>world</span> </div>
+ </h1>
+ |]
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "div"
+ _elt_tag ((_elt_children (head (_elt_children (dom 5)) 3) !! 1) 5)
+ `shouldBe` "span"
+ it "Parses pwx'" $ do
+ let mylink = [pwx|<a click=#c{(\_ x -> return $ x + 41)}#>Hello</a>|]
+ let dom =
+ (\st -> [pwx'|
+ <h1 id="foo" style=#t{"position:absolute"}#>
+ #e{mylink}#
+ </h1>
+ |]
+ )
+ _elt_tag (dom 3) `shouldBe` "h1"
+ _elt_tag (head (_elt_children (dom 5)) 3) `shouldBe` "a"
+ it "Handles <br />" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_ "<br />"
+ _pwxSelfClosingTag_tag v `shouldBe` "br"
+ _pwxSelfClosingTag_attributes v `shouldBe` []
+ it "Handles nested elements" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_ "<div> \n <span>a</span> hello <span></span>world </div>"
+ _pwxElement_tag v `shouldBe` "div"
+ it "Handles terse nested elements" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_ "<div><span>a</span>hello<span></span>world</div>"
+ _pwxElement_tag v `shouldBe` "div"
+ it "Handles terse nested elements with dynamic text" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_
+ "<div><span>a</span>hello<span>#t{\"there\"}#</span>#t{\"world\"}#</div>"
+ _pwxElement_tag v `shouldBe` "div"
+ it "Handles double back-to-back brackets" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_ "<div click=#c{\\_ s -> s { _foo=1 } }# />"
+ _pwxSelfClosingTag_tag v `shouldBe` "div"
+ it "Handles double back-to-back brackets with no whitespace" $ do
+ -- we can check this later, for now we just make sure it parses
+ v <- parsePWX_ "<div click=#c{\\_ s -> s { _foo=1 }}# />"
+ _pwxSelfClosingTag_tag v `shouldBe` "div"
diff --git a/test/Spec.hs b/test/Spec.hs
index 368f1c4..ada8ec5 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,8 +1,8 @@
import DOMSpec ( domSpec )
-import HSXSpec ( hsxSpec )
+import PWXSpec ( pwxSpec )
import Test.Hspec
main :: IO ()
main = hspec $ do
domSpec
- hsxSpec \ No newline at end of file
+ pwxSpec \ No newline at end of file