markup-parse 0.1.0.1 → 0.1.1
raw patch · 8 files changed
+142/−198 lines, 8 filesdep ~tastydep ~textPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: tasty, text
API changes (from Hackage documentation)
Files
- ChangeLog.md +17/−0
- markup-parse.cabal +3/−3
- other/line_.html +0/−48
- other/line_.svg +0/−52
- readme.org +54/−50
- src/MarkupParse.hs +46/−28
- src/MarkupParse/FlatParse.hs +21/−16
- src/MarkupParse/Patch.hs +1/−1
ChangeLog.md view
@@ -1,3 +1,20 @@+0.1.1+===++- fixed escape bug+- bumped tasty & text upper bounds++0.1.0+===++- Added RenderStyle+- rename: Warn <== Result+- refactored Markup structure+- created OpenTagType+- rename: NameTag <== TagName+- Element type synonym for Tree Token+- suffixed parsers with a 'P'+ 0.0.0 ===
markup-parse.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: markup-parse-version: 0.1.0.1+version: 0.1.1 license: BSD-3-Clause license-file: LICENSE copyright: Copyright, Tony Day, 2023-@@ -106,7 +106,7 @@ , deepseq >=1.4.4 && <1.6 , flatparse >=0.3.5 && <0.6 , string-interpolate >=0.3 && <0.4- , tasty >=1.2 && <1.5+ , tasty >=1.2 && <1.6 , tasty-golden >=2.3.1.1 && <2.4 , these >=1.1 && <1.3 , tree-diff >=0.3 && <0.4@@ -145,6 +145,6 @@ , markup-parse , optparse-applicative >=0.17 && <0.19 , perf >=0.12 && <0.13- , text >=1.2 && <2.1+ , text >=1.2 && <2.2 ghc-options: -O2 type: exitcode-stdio-1.0
− other/line_.html
@@ -1,48 +0,0 @@-<svg height="300" viewBox="-0.75 -0.5 1.5 1.0" width="450" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">- <style>- svg {- color-scheme: light dark;-}-{- .canvas g, .title g, .axisbar g, .ticktext g, .tickglyph g, .ticklines g, .legendContent g text {- fill: rgb(5%, 5%, 5%);- }- .ticklines g, .tickglyph g, .legendBorder g {- stroke: rgb(5%, 5%, 5%);- }- .legendBorder g {- fill: rgb(94%, 94%, 94%);- }-}-@media (prefers-color-scheme:dark) {- .canvas g, .title g, .axisbar g, .ticktext g, .tickglyph g, .ticklines g, .legendContent g text {- fill: rgb(94%, 94%, 94%);- }- .ticklines g, .tickglyph g, .legendBorder g {- stroke: rgb(94%, 94%, 94%);- }- .legendBorder g {- fill: rgb(5%, 5%, 5%);- }-}</style>- <g class="chart">- <g class="line">- <g fill="none" stroke="rgb(2%, 73%, 80%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="-0.6059,0.1875 -0.2077,0.1875 0.1905,-0.3537" /></g><g fill="none" stroke="rgb(2%, 29%, 48%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="-0.6059,0.3228 0.5091,-0.0831" /></g><g fill="none" stroke="rgb(66%, 7%, 55%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="-0.4068,-0.2184 -0.4068,0.3228" /></g></g></g><g class="hud"><g class="frame"><g fill="rgb(100%, 100%, 100%)" fill-opacity="0.02" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.6866" width="1.2006" x="-0.6118" y="-0.3588" /></g></g><g class="axis"><g class="ticks"><g class="tickglyph"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.4" stroke-width="0.0040"><g transform="rotate(-90.0, -0.6357, 0.3228)"><polyline points="-0.6357,0.3378--0.6357,0.3078" /></g><g transform="rotate(-90.0, -0.6357, 0.2551)"><polyline points="-0.6357,0.2701--0.6357,0.2401" /></g><g transform="rotate(-90.0, -0.6357, 0.1875)"><polyline points="-0.6357,0.2025--0.6357,0.1725" /></g><g transform="rotate(-90.0, -0.6357, 0.1198)"><polyline points="-0.6357,0.1348--0.6357,0.1048" /></g><g transform="rotate(-90.0, -0.6357, 0.0522)"><polyline points="-0.6357,0.0672--0.6357,0.0372" /></g><g transform="rotate(-90.0, -0.6357, -0.0155)"><polyline points="-0.6357,-0.0005--0.6357,-0.0305" /></g><g transform="rotate(-90.0, -0.6357, -0.0831)"><polyline points="-0.6357,-0.0681--0.6357,-0.0981" /></g><g transform="rotate(-90.0, -0.6357, -0.1508)"><polyline points="-0.6357,-0.1358--0.6357,-0.1658" /></g><g transform="rotate(-90.0, -0.6357, -0.2184)"><polyline points="-0.6357,-0.2034--0.6357,-0.2334" /></g><g transform="rotate(-90.0, -0.6357, -0.2861)"><polyline points="-0.6357,-0.2711--0.6357,-0.3011" /></g><g transform="rotate(-90.0, -0.6357, -0.3537)"><polyline points="-0.6357,-0.3387--0.6357,-0.3687" /></g></g></g><g class="ticktext"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="end"><text x="-0.6644" y="0.3321">0.0</text><text x="-0.6644" y="0.2644">0.5</text><text x="-0.6644" y="0.1968">1.0</text><text x="-0.6644" y="0.1291">1.5</text><text x="-0.6644" y="0.0615">2.0</text><text x="-0.6644" y="-0.0062">2.5</text><text x="-0.6644" y="-0.0738">3.0</text><text x="-0.6644" y="-0.1415">3.5</text><text x="-0.6644" y="-0.2091">4.0</text><text x="-0.6644" y="-0.2768">4.5</text><text x="-0.6644" y="-0.3444">5.0</text></g></g><g class="ticklines"><g fill="none" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.05" stroke-width="0.0050"><polyline points="-0.6059,0.3228 0.5887,0.3228" /><polyline points="-0.6059,0.2551 0.5887,0.2551" /><polyline points="-0.6059,0.1875 0.5887,0.1875" /><polyline points="-0.6059,0.1198 0.5887,0.1198" /><polyline points="-0.6059,0.0522 0.5887,0.0522" /><polyline points="-0.6059,-0.0155 0.5887,-0.0155" /><polyline points="-0.6059,-0.0831 0.5887,-0.0831" /><polyline points="-0.6059,-0.1508 0.5887,-0.1508" /><polyline points="-0.6059,-0.2184 0.5887,-0.2184" /><polyline points="-0.6059,-0.2861 0.5887,-0.2861" /><polyline points="-0.6059,-0.3537 0.5887,-0.3537" /></g></g></g><g class="axisbar"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.6792" width="0.0032" x="-0.6230" y="-0.3551" /></g></g></g><g class="axis"><g class="ticks"><g class="tickglyph"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.4" stroke-width="0.0040"><polyline points="-0.6059,0.3631--0.6059,0.3331" /><polyline points="-0.4068,0.3631--0.4068,0.3331" /><polyline points="-0.2077,0.3631--0.2077,0.3331" /><polyline points="-0.0086,0.3631--0.0086,0.3331" /><polyline points="0.1905,0.3631-0.1905,0.3331" /><polyline points="0.3896,0.3631-0.3896,0.3331" /><polyline points="0.5887,0.3631-0.5887,0.3331" /></g></g><g class="ticktext"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="middle"><text x="-0.6059" y="0.3911">0.0</text><text x="-0.4068" y="0.3911">0.5</text><text x="-0.2077" y="0.3911">1.0</text><text x="-0.0086" y="0.3911">1.5</text><text x="0.1905" y="0.3911">2.0</text><text x="0.3896" y="0.3911">2.5</text><text x="0.5887" y="0.3911">3.0</text></g></g><g class="ticklines"><g fill="none" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.05" stroke-width="0.0050"><polyline points="-0.6059,0.3228 -0.6059,-0.3537" /><polyline points="-0.4068,0.3228 -0.4068,-0.3537" /><polyline points="-0.2077,0.3228 -0.2077,-0.3537" /><polyline points="-0.0086,0.3228 -0.0086,-0.3537" /><polyline points="0.1905,0.3228 0.1905,-0.3537" /><polyline points="0.3896,0.3228 0.3896,-0.3537" /><polyline points="0.5887,0.3228 0.5887,-0.3537" /></g></g></g><g class="axisbar"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.0027" width="1.1978" x="-0.6074" y="0.3346" /></g></g></g><g class="title"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0796" stroke="none" stroke-width="0.0" text-anchor="middle"><text x="-0.0513" y="-0.4188">Line Chart</text></g></g><g class="title"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0478" stroke="none" stroke-width="0.0" text-anchor="end"><text x="0.6156" y="0.4608">Made with love and chart-svg</text></g></g><g class="legend"><g class="legendBorder"><g fill="rgb(99%, 99%, 99%)" fill-opacity="1.0" stroke="rgb(5%, 5%, 5%)" stroke-opacity="1.0" stroke-width="0.0025"><rect height="0.1793" width="0.3843" x="0.3299" y="0.0775" /></g></g><g class="legendContent"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.2297">palette1 2</text></g><g fill="none" stroke="rgb(66%, 7%, 55%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.2213 0.6883,0.2213" /></g><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.1756">palette1 1</text></g><g fill="none" stroke="rgb(2%, 29%, 48%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.1672 0.6883,0.1672" /></g><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.1215">palette1 0</text></g><g fill="none" stroke="rgb(2%, 73%, 80%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.1131 0.6883,0.1131" /></g></g></g><g class="frame"><g fill="rgb(100%, 100%, 100%)" fill-opacity="0.02" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="1.0" width="1.5" x="-0.75" y="-0.5" /></g></g></g></svg>
− other/line_.svg
@@ -1,52 +0,0 @@-<svg height="300" viewBox="-0.75 -0.5 1.5 1.0" width="450" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">- <style>svg {- color-scheme: light dark;- }- {- .canvas g, .title g, .axisbar g, .ticktext g, .tickglyph g, .ticklines g, .legendContent g text {- fill: rgb(5%, 5%, 5%);- }- .ticklines g, .tickglyph g, .legendBorder g {- stroke: rgb(5%, 5%, 5%);- }- .legendBorder g {- fill: rgb(94%, 94%, 94%);- }- }- @media (prefers-color-scheme:dark) {- .canvas g, .title g, .axisbar g, .ticktext g, .tickglyph g, .ticklines g, .legendContent g text {- fill: rgb(94%, 94%, 94%);- }- .ticklines g, .tickglyph g, .legendBorder g {- stroke: rgb(94%, 94%, 94%);- }- .legendBorder g {- fill: rgb(5%, 5%, 5%);- }- }</style>- <g class="chart">- <g class="line">- <g fill="none" stroke="rgb(2%, 73%, 80%)" stroke-opacity="1.0" stroke-width="0.0150">- <polyline points="-0.6059,0.1875 -0.2077,0.1875 0.1905,-0.3537" />- </g>- <g fill="none" stroke="rgb(2%, 29%, 48%)" stroke-opacity="1.0" stroke-width="0.0150">- <polyline points="-0.6059,0.3228 0.5091,-0.0831" />- </g><g fill="none" stroke="rgb(66%, 7%, 55%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="-0.4068,-0.2184 -0.4068,0.3228" /></g></g></g><g class="hud"><g class="frame"><g fill="rgb(100%, 100%, 100%)" fill-opacity="0.02" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.6866" width="1.2006" x="-0.6118" y="-0.3588" /></g></g><g class="axis"><g class="ticks"><g class="tickglyph"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.4" stroke-width="0.0040"><g transform="rotate(-90.0, -0.6357, 0.3228)"><polyline points="-0.6357,0.3378- -0.6357,0.3078" /></g><g transform="rotate(-90.0, -0.6357, 0.2551)"><polyline points="-0.6357,0.2701- -0.6357,0.2401" /></g><g transform="rotate(-90.0, -0.6357, 0.1875)"><polyline points="-0.6357,0.2025- -0.6357,0.1725" /></g><g transform="rotate(-90.0, -0.6357, 0.1198)"><polyline points="-0.6357,0.1348- -0.6357,0.1048" /></g><g transform="rotate(-90.0, -0.6357, 0.0522)"><polyline points="-0.6357,0.0672- -0.6357,0.0372" /></g><g transform="rotate(-90.0, -0.6357, -0.0155)"><polyline points="-0.6357,-0.0005- -0.6357,-0.0305" /></g><g transform="rotate(-90.0, -0.6357, -0.0831)"><polyline points="-0.6357,-0.0681- -0.6357,-0.0981" /></g><g transform="rotate(-90.0, -0.6357, -0.1508)"><polyline points="-0.6357,-0.1358- -0.6357,-0.1658" /></g><g transform="rotate(-90.0, -0.6357, -0.2184)"><polyline points="-0.6357,-0.2034- -0.6357,-0.2334" /></g><g transform="rotate(-90.0, -0.6357, -0.2861)"><polyline points="-0.6357,-0.2711- -0.6357,-0.3011" /></g><g transform="rotate(-90.0, -0.6357, -0.3537)"><polyline points="-0.6357,-0.3387- -0.6357,-0.3687" /></g></g></g><g class="ticktext"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="end"><text x="-0.6644" y="0.3321">0.0</text><text x="-0.6644" y="0.2644">0.5</text><text x="-0.6644" y="0.1968">1.0</text><text x="-0.6644" y="0.1291">1.5</text><text x="-0.6644" y="0.0615">2.0</text><text x="-0.6644" y="-0.0062">2.5</text><text x="-0.6644" y="-0.0738">3.0</text><text x="-0.6644" y="-0.1415">3.5</text><text x="-0.6644" y="-0.2091">4.0</text><text x="-0.6644" y="-0.2768">4.5</text><text x="-0.6644" y="-0.3444">5.0</text></g></g><g class="ticklines"><g fill="none" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.05" stroke-width="0.0050"><polyline points="-0.6059,0.3228 0.5887,0.3228" /><polyline points="-0.6059,0.2551 0.5887,0.2551" /><polyline points="-0.6059,0.1875 0.5887,0.1875" /><polyline points="-0.6059,0.1198 0.5887,0.1198" /><polyline points="-0.6059,0.0522 0.5887,0.0522" /><polyline points="-0.6059,-0.0155 0.5887,-0.0155" /><polyline points="-0.6059,-0.0831 0.5887,-0.0831" /><polyline points="-0.6059,-0.1508 0.5887,-0.1508" /><polyline points="-0.6059,-0.2184 0.5887,-0.2184" /><polyline points="-0.6059,-0.2861 0.5887,-0.2861" /><polyline points="-0.6059,-0.3537 0.5887,-0.3537" /></g></g></g><g class="axisbar"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.6792" width="0.0032" x="-0.6230" y="-0.3551" /></g></g></g><g class="axis"><g class="ticks"><g class="tickglyph"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.4" stroke-width="0.0040"><polyline points="-0.6059,0.3631- -0.6059,0.3331" /><polyline points="-0.4068,0.3631- -0.4068,0.3331" /><polyline points="-0.2077,0.3631- -0.2077,0.3331" /><polyline points="-0.0086,0.3631- -0.0086,0.3331" /><polyline points="0.1905,0.3631- 0.1905,0.3331" /><polyline points="0.3896,0.3631- 0.3896,0.3331" /><polyline points="0.5887,0.3631- 0.5887,0.3331" /></g></g><g class="ticktext"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="middle"><text x="-0.6059" y="0.3911">0.0</text><text x="-0.4068" y="0.3911">0.5</text><text x="-0.2077" y="0.3911">1.0</text><text x="-0.0086" y="0.3911">1.5</text><text x="0.1905" y="0.3911">2.0</text><text x="0.3896" y="0.3911">2.5</text><text x="0.5887" y="0.3911">3.0</text></g></g><g class="ticklines"><g fill="none" stroke="rgb(5%, 5%, 5%)" stroke-opacity="0.05" stroke-width="0.0050"><polyline points="-0.6059,0.3228 -0.6059,-0.3537" /><polyline points="-0.4068,0.3228 -0.4068,-0.3537" /><polyline points="-0.2077,0.3228 -0.2077,-0.3537" /><polyline points="-0.0086,0.3228 -0.0086,-0.3537" /><polyline points="0.1905,0.3228 0.1905,-0.3537" /><polyline points="0.3896,0.3228 0.3896,-0.3537" /><polyline points="0.5887,0.3228 0.5887,-0.3537" /></g></g></g><g class="axisbar"><g fill="rgb(5%, 5%, 5%)" fill-opacity="0.4" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="0.0027" width="1.1978" x="-0.6074" y="0.3346" /></g></g></g><g class="title"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0796" stroke="none" stroke-width="0.0" text-anchor="middle"><text x="-0.0513" y="-0.4188">Line Chart</text></g></g><g class="title"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0478" stroke="none" stroke-width="0.0" text-anchor="end"><text x="0.6156" y="0.4608">Made with love and chart-svg</text></g></g><g class="legend"><g class="legendBorder"><g fill="rgb(99%, 99%, 99%)" fill-opacity="1.0" stroke="rgb(5%, 5%, 5%)" stroke-opacity="1.0" stroke-width="0.0025"><rect height="0.1793" width="0.3843" x="0.3299" y="0.0775" /></g></g><g class="legendContent"><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.2297">palette1 2</text></g><g fill="none" stroke="rgb(66%, 7%, 55%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.2213 0.6883,0.2213" /></g><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.1756">palette1 1</text></g><g fill="none" stroke="rgb(2%, 29%, 48%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.1672 0.6883,0.1672" /></g><g fill="rgb(5%, 5%, 5%)" fill-opacity="1.0" font-size="0.0398" stroke="none" stroke-width="0.0" text-anchor="start"><text x="0.3498" y="0.1215">palette1 0</text></g><g fill="none" stroke="rgb(2%, 73%, 80%)" stroke-opacity="1.0" stroke-width="0.0150"><polyline points="0.5688,0.1131 0.6883,0.1131" /></g></g></g><g class="frame"><g fill="rgb(100%, 100%, 100%)" fill-opacity="0.02" stroke="rgb(0%, 0%, 0%)" stroke-opacity="0" stroke-width="0"><rect height="1.0" width="1.5" x="-0.75" y="-0.5" /></g></g></g></svg>
readme.org view
@@ -3,11 +3,11 @@ [[https://hackage.haskell.org/package/markup-parse][https://img.shields.io/hackage/v/markup-parse.svg]] [[https://github.com/tonyday567/markup-parse/actions?query=workflow%3Ahaskell-ci][https://github.com/tonyday567/markup-parse/workflows/haskell-ci/badge.svg]] -~markup-parse~ parses and prints a subset of common XML & HTML data.+~markup-parse~ parses and prints a subset of common XML & HTML structured data, from and to strict bytestrings * Development -#+begin_src haskell :results output+#+begin_src haskell :results output :exports both :r :set prompt "> " :set -Wno-type-defaults@@ -18,51 +18,55 @@ import Control.Monad import MarkupParse import MarkupParse.FlatParse-import Data.Map.Strict qualified as Map import MarkupParse.Patch import Data.ByteString qualified as B import Data.ByteString.Char8 qualified as C+import Data.Map.Strict qualified as Map import Data.Function import FlatParse.Basic hiding (take) import Data.String.Interpolate import Data.TreeDiff import Control.Monad--- import Perf bs <- B.readFile "other/line.svg" C.length bs #+end_src #+RESULTS:+: Preprocessing library for markup-parse-0.1.1..+: GHCi, version 9.6.2: https://www.haskell.org/ghc/ :? for help+: [1 of 3] Compiling MarkupParse.FlatParse ( src/MarkupParse/FlatParse.hs, interpreted )+: [2 of 3] Compiling MarkupParse ( src/MarkupParse.hs, interpreted )+: [3 of 3] Compiling MarkupParse.Patch ( src/MarkupParse/Patch.hs, interpreted ) : Ok, three modules loaded.-: >-: >+: g+: Ok, three modules loaded. : 7554 -* Main Pipeline+* Main Pipeline types -#+begin_src haskell :results output+#+begin_src haskell :results output :exports both :t tokenize Html :t gather Html-:t Markup Html :t normalize-:t tokenize Html >=> gather Html >>> fmap (Markup Html >>> normalize) >=> degather >>> fmap (fmap (detokenize Html) >>> mconcat)+:t degather :t detokenize Html+:t tokenize Html >=> gather Html >=> (normalize >>> pure) >=> degather Html >=> (fmap (detokenize Html) >>> pure) #+end_src #+RESULTS:-: tokenize Html :: ByteString -> These [MarkupWarning] [Token]-: gather Html :: [Token] -> These [MarkupWarning] [Tree Token]-: Markup Html :: [Tree Token] -> Markup+: tokenize Html :: ByteString -> Warn [Token]+: gather Html :: [Token] -> Warn Markup : normalize :: Markup -> Markup-: tokenize Html >=> gather Html >>> fmap (Markup Html >>> normalize) >=> degather >>> fmap (fmap (detokenize Html) >>> mconcat)-: :: ByteString -> These [MarkupWarning] ByteString+: degather :: Standard -> Markup -> Warn [Token] : detokenize Html :: Token -> ByteString+: tokenize Html >=> gather Html >=> (normalize >>> pure) >=> degather Html >=> (fmap (detokenize Html) >>> pure)+: :: ByteString -> These [MarkupWarning] [ByteString] Round trip equality -#+begin_src haskell :results output+#+begin_src haskell :results output :exports both m = markup_ Xml bs-m == (markup_ Xml $ markdown Compact m)+m == (markup_ Xml $ markdown_ Compact Xml m) #+end_src #+RESULTS:@@ -70,73 +74,74 @@ * MarkupParse.Patch -Obviously doesn't belong here long-term but has been very useful in testing and development.+Obviously, patch doesn't belong here long-term but has been very useful in testing and development. -#+begin_src haskell :results output-show $ ansiWlEditExpr <$> patch [1, 2, 3, 5] [0, 1, 2, 4, 6]+#+begin_src haskell :results output :exports both+showPatch $ patch [1, 2, 3, 5] [0, 1, 2, 4, 6] #+end_src #+RESULTS:-: Just [+0, -3, +4, -5, +6]+: [+0, -3, +4, -5, +6] * wiki diff test debug -#+begin_src haskell :results output+#+begin_src haskell :results output :exports both bs <- B.readFile "other/Parsing - Wikipedia.html" m = markup_ Html bs-m == (markup_ Html $ markdown Compact m)+m == (markup_ Html $ markdown_ Compact Html m) #+end_src #+RESULTS: : True * Reference-** Html Standards -[[https://html.spec.whatwg.org/multipage/syntax.html#elements-2:void-elements-2][HTML Standard]]--[[https://developer.mozilla.org/en-US/docs/Glossary/Void_element#self-closing_tags][void elements]]+[[https://html.spec.whatwg.org/#toc-syntax][HTML Standard]] -[[https://stackoverflow.com/questions/3558119/are-non-void-self-closing-tags-valid-in-html5][html - Are (non-void) self-closing tags valid in HTML5? - Stack Overflow]]+[[https://stackoverflow.com/questions/3558119/are-non-void-self-closing-tags-valid-in-html5][Are (non-void) self-closing tags valid in HTML5? - Stack Overflow]] -[[https://www.w3.org/TR/2017/REC-html52-20171214/syntax.html#tree-construction][HTML 5.2: 8. The HTML syntax]]+[[https://www.w3.org/TR/xml/][Extensible Markup Language (XML) 1.0 (Fifth Edition)]] ** Prior Art -attoparsec-based-https://hackage.haskell.org/package/html-parse+[[https://hackage.haskell.org/package/html-parse][html-parse]] is an attoparsec-based parser for HTML. The HTML parsing here has referenced this parsing logic for HTML elements. -event-based-https://hackage.haskell.org/package/xeno+[[https://hackage.haskell.org/package/blaze-markup][blaze-markup]] & [[https://hackage.haskell.org/package/lucid][lucid]] are HTML DSLs and printers, but not parsers. -parsec-based-https://hackage.haskell.org/package/XMLParser+[[https://hackage.haskell.org/package/xeno][xeno]] is an "event-based" XML parser with a more complicated base markup type. -https://hackage.haskell.org/package/hexml+[[https://hackage.haskell.org/package/XMLParser][XMLParser]] & [[https://hackage.haskell.org/package/hexml][hexml]] are parsec-based parsers. * Performance -Most testing has been via app/speed.hs--#+begin_src elisp-(setq haskell-process-args-cabal-repl '("markup-parse:bench:markup-parse-speed"))-#+end_src--#+RESULTS:-| markup-parse:bench:markup-parse-speed |+The [[https://hackage.haskell.org/package/perf][perf]] library has been used to measure performance of the library. -** Benchmarks+`cabal bench` runs the default benchmarking: -#+begin_src sh :results output-cabal run markup-parser-speed+#+begin_src sh :results output :exports both+cabal bench #+end_src #+RESULTS:-+#+begin_example+Running 1 benchmarks...+Benchmark markup-parse-speed: RUNNING...+label1 label2 old result new result change +gather time 9.33e4 7.73e4 improvement+html-parse tokenstime 1.21e6 1.17e6+html-parse tree time 6.61e4 7.59e4 slightly-degraded+markdown time 3.75e5 3.92e5+markup time 4.72e5 5.20e5 slightly-degraded+normalize time 2.80e5 3.04e5 slightly-degraded+tokenize time 8.59e5 8.67e5+Benchmark markup-parse-speed: FINISH+#+end_example ** Profiling +Profiling is used to aid library development:+ #+begin_src sh :results output cabal configure --enable-library-profiling --enable-executable-profiling -fprof-auto -fprof --write-ghc-environment-files=always --enable-benchmarks -O2 #+end_src@@ -151,8 +156,7 @@ executable-profiling: True #+end_example --Profiling slowed the main functions significantly:+Profiling slow the main functions significantly: #+begin_example ./app/speed -n 1000 --best -c +RTS -s -p -hc -l -RTS
src/MarkupParse.hs view
@@ -33,7 +33,7 @@ warnEither, warnMaybe, - -- * element creation+ -- * Element Element, element, element_,@@ -45,12 +45,12 @@ -- * Token components NameTag, selfClosers,- addAttrs, doctypeHtml, doctypeXml, AttrName, AttrValue, Attr (..),+ addAttrs, attrsP, nameP, @@ -129,7 +129,7 @@ -- -- @'markdown_' . 'markup_'@ is an isomorphic round trip from 'Markup' to 'ByteString' to 'Markup': ----- - This is subject to the Markup being 'wellFormed'.+-- - This is subject to the 'Markup' being 'wellFormed'. -- -- - The round-trip @'markup_' . 'markdown_'@ is not isomorphic as parsing forgets whitespace within tags, comments and declarations. --@@ -149,7 +149,7 @@ -- -- - 'normalize' concatenates content, and normalizes attributes, ----- > degather >=>+-- > degather Html >=> -- -- - 'degather' turns the markup tree back into a token list. Finally, --@@ -157,7 +157,7 @@ -- -- - 'detokenize' turns a token back into a bytestring. ----- Along the way, the kleisi fishies and compose forward usage accumulates any warnings via the 'These' monad instance.+-- Along the way, the kleisi fishies and compose forward usage accumulates any warnings via the 'These' monad instance, which is wrapped into a type synonym named 'Warn'. -- | From a parsing pov, Html & Xml (& Svg) are close enough that they share a lot of parsing logic, so that parsing and printing just need some tweaking. --@@ -187,7 +187,7 @@ TagMismatch NameTag NameTag | -- | An EndTag with no corresponding StartTag. UnmatchedEndTag- | -- | An EndTag with corresponding StartTag.+ | -- | An StartTag with no corresponding EndTag. UnclosedTag | -- | An EndTag should never appear in 'Markup' EndTagInTree@@ -235,7 +235,7 @@ markup :: Standard -> ByteString -> Warn Markup markup s bs = bs & (tokenize s >=> gather s) --- | markup but errors on warnings.+-- | 'markup' but errors on warnings. markup_ :: Standard -> ByteString -> Markup markup_ s bs = markup s bs & warnError @@ -278,8 +278,16 @@ -- | Whether an opening tag is a start tag or an empty element tag. data OpenTagType = StartTag | EmptyElemTag deriving (Show, Ord, Eq, Generic, NFData, ToExpr) --- | A Markup token+-- | A Markup token. The term is borrowed from <https://www.w3.org/html/wg/spec/tokenization.html#tokenization HTML> standards but is used across 'Html' and 'Xml' in this library. --+-- Note that the 'Token' type is used in two slightly different contexts:+--+-- - As an intermediary representation of markup between 'ByteString' and 'Markup'.+--+-- - As the primitives of 'Markup' 'Element's+--+-- Specifically, an 'EndTag' will occur in a list of tokens, but not as a primitive in 'Markup'. It may turn out to be better to have two different types for these two uses and future iterations of this library may head in this direction.+-- -- >>> runParser_ (many (tokenP Html)) [i|<foo>content</foo>|] -- [OpenTag StartTag "foo" [],Content "content",EndTag "foo"] --@@ -320,21 +328,31 @@ -- | Escape a single character. escapeChar :: Char -> ByteString-escapeChar '<' = "<"-escapeChar '>' = ">"-escapeChar '&' = "&"-escapeChar '\'' = "&apos"-escapeChar '"' = """+escapeChar '<' = "<"+escapeChar '>' = ">"+escapeChar '&' = "&"+escapeChar '\'' = "'"+escapeChar '"' = """ escapeChar x = B.singleton x --- | Escape Content+-- | Escape the following predefined character entity references: --+-- @+-- escapeChar \'<\' = "<"+-- escapeChar \'>\' = ">"+-- escapeChar \'&\' = "&"+-- escapeChar '\'' = "'"+-- escapeChar '"' = """+-- @+--+-- No attempt is made to meet the <https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references HTML Standards>+-- -- >>> escape [i|<foo class="a" bar='b'>|]--- "<foo class="a" bar=&aposb&apos>"+-- "<foo class="a" bar='b'>" escape :: ByteString -> ByteString escape bs = B.concatMap escapeChar bs --- | Append attributes to the existing Token attribute list.+-- | Append attributes to an existing Token attribute list. Returns Nothing for tokens that do not have attributes. addAttrs :: [Attr] -> Token -> Maybe Token addAttrs as (OpenTag t n as') = Just $ OpenTag t n (as <> as') addAttrs _ _ = Nothing@@ -395,41 +413,41 @@ "wbr" ] --- | Most functions return a 'Markup' rather than an 'Element' because it is more ergonimc to use the free monoid wrap (aka a list) in preference to returning a 'Maybe Element', say.+-- | Most functions return a 'Markup' rather than an 'Element' because it is often more ergonomic to use the free monoid (aka a list) in preference to returning a 'Maybe' 'Element' (say). type Element = Tree Token --- | Create a Markup element from a NameTag and attributes that wraps some other Markup.+-- | Create 'Markup' from a name tag and attributes that wraps some other markup. -- -- >>> element "div" [] (element_ "br" []) -- Markup {elements = [Node {rootLabel = OpenTag StartTag "div" [], subForest = [Node {rootLabel = OpenTag StartTag "br" [], subForest = []}]}]} element :: NameTag -> [Attr] -> Markup -> Markup element n as (Markup xs) = Markup [Node (OpenTag StartTag n as) xs] --- | Create a Markup element from a NameTag and attributes that doesn't wrap some other Markup. OpenTagType is StartTag. Use 'emptyElem' if you want to create a EmptyElemTag.+-- | Create 'Markup' from a name tag and attributes that doesn't wrap some other markup. The 'OpenTagType' used is 'StartTag'. Use 'emptyElem' if you want to create 'EmptyElemTag' based markup. -- -- >>> (element_ "br" []) -- Markup {elements = [Node {rootLabel = OpenTag StartTag "br" [], subForest = []}]} element_ :: NameTag -> [Attr] -> Markup element_ n as = Markup [Node (OpenTag StartTag n as) []] --- | Create a Markup element from a NameTag and attributes using EmptyElemTag, that doesn't wrap some other Markup. No checks are made on whether this creates well-formed Markup.+-- | Create 'Markup' from a name tag and attributes using 'EmptyElemTag', that doesn't wrap some other markup. No checks are made on whether this creates well-formed markup. -- -- >>> emptyElem "br" [] -- Markup {elements = [Node {rootLabel = OpenTag EmptyElemTag "br" [], subForest = []}]} emptyElem :: NameTag -> [Attr] -> Markup emptyElem n as = Markup [Node (OpenTag EmptyElemTag n as) []] --- | Create a Markup element from a NameTag and attributes that wraps some 'Content'. No escaping is performed.+-- | Create 'Markup' from a name tag and attributes that wraps some 'Content'. No escaping is performed. -- -- >>> elementc "div" [] "content" -- Markup {elements = [Node {rootLabel = OpenTag StartTag "div" [], subForest = [Node {rootLabel = Content "content", subForest = []}]}]} elementc :: NameTag -> [Attr] -> ByteString -> Markup elementc n as bs = element n as (contentRaw bs) --- | Create a Markup element from a bytestring, escaping the usual characters.+-- | Create 'Markup' 'Content' from a bytestring, escaping the usual characters. -- -- >>> content "<content>"--- Markup {elements = [Node {rootLabel = Content "<content>", subForest = []}]}+-- Markup {elements = [Node {rootLabel = Content "<content>", subForest = []}]} content :: ByteString -> Markup content bs = Markup [pure $ Content (escape bs)] @@ -519,7 +537,7 @@ (Doctype t) -> [i|<!#{t}>|] (Decl t as) -> bool [i|<?#{t}#{renderAttrs as}?>|] [i|<!#{t}!>|] (s == Html) --- | Indented 0 puts newlines in between the tags.+-- | @Indented 0@ puts newlines in between the tags. data RenderStyle = Compact | Indented Int deriving (Eq, Show, Generic) indentChildren :: RenderStyle -> [ByteString] -> [ByteString]@@ -589,7 +607,7 @@ (Cursor finalSibs finalParents, warnings) = foldl' (\(c, xs) t -> incCursor s t c & second (maybeToList >>> (<> xs))) (Cursor [] [], []) ts --- | gather but errors on warnings.+-- | 'gather' but errors on warnings. gather_ :: Standard -> [Token] -> Markup gather_ s ts = gather s ts & warnError @@ -632,7 +650,7 @@ degather :: Standard -> Markup -> Warn [Token] degather s (Markup tree) = concatWarns $ foldTree (addCloseTags s) <$> tree --- | degather but errors on warning+-- | 'degather' but errors on warning degather_ :: Standard -> Markup -> [Token] degather_ s m = degather s m & warnError @@ -785,13 +803,13 @@ ) <* $(char '>') --- | Xml production [32]+-- | xml production [32] xmlStandaloneP :: Parser e ByteString xmlStandaloneP = byteStringOf $ ws_ *> $(string "standalone") *> eq *> xmlYesNoP --- | Xml yes/no+-- | xml yes/no xmlYesNoP :: Parser e ByteString xmlYesNoP = wrappedQNoGuard (byteStringOf $ $(string "yes") <|> $(string "no"))
src/MarkupParse/FlatParse.hs view
@@ -3,21 +3,21 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} --- | Various flatparse helpers and combinators.+-- | Various <https://hackage.haskell.org/package/flatparse flatparse> helpers and combinators. module MarkupParse.FlatParse- ( -- * parsing+ ( -- * Parsing ParserWarning (..), runParserMaybe, runParserEither, runParserWarn, runParser_, - -- * flatparse re-exports+ -- * Flatparse re-exports runParser, Parser, Result (..), - -- * parsers+ -- * Parsers isWhitespace, ws_, ws,@@ -60,7 +60,7 @@ -- >>> import MarkupParse.FlatParse -- >>> import FlatParse.Basic --- | run a Parser, throwing away leftovers. Nothing on 'Fail' or 'Err'.+-- | Run a Parser, throwing away leftovers. Nothing on 'Fail' or 'Err'. -- -- >>> runParserMaybe ws "x" -- Nothing@@ -73,7 +73,7 @@ Fail -> Nothing Err _ -> Nothing --- | run a Parser, throwing away leftovers. Returns Left on 'Fail' or 'Err'.+-- | Run a Parser, throwing away leftovers. Returns Left on 'Fail' or 'Err'. -- -- >>> runParserEither ws " x" -- Right ' '@@ -175,14 +175,14 @@ wss :: Parser e ByteString wss = byteStringOf $ some ws --- | single quote+-- | Single quote -- -- >>> runParserMaybe sq "'" -- Just () sq :: ParserT st e () sq = $(char '\'') --- | double quote+-- | Double quote -- -- >>> runParserMaybe dq "\"" -- Just ()@@ -256,14 +256,14 @@ eq = ws_ *> $(char '=') <* ws_ {-# INLINE eq #-} --- | some with a separator+-- | Some with a separator. -- -- >>> runParser (sep ws (many (satisfy (/= ' ')))) "a b c" -- OK ["a","b","c"] "" sep :: Parser e s -> Parser e a -> Parser e [a] sep s p = (:) <$> p <*> many (s *> p) --- | parser bracketed by two other parsers+-- | Parser bracketed by two other parsers. -- -- >>> runParser (bracketed ($(char '[')) ($(char ']')) (many (satisfy (/= ']')))) "[bracketed]" -- OK "bracketed" ""@@ -271,14 +271,14 @@ bracketed o c p = o *> p <* c {-# INLINE bracketed #-} --- | Bracketed by square brackets.+-- | Parser bracketed by square brackets. -- -- >>> runParser bracketedSB "[bracketed]" -- OK "bracketed" "" bracketedSB :: Parser e [Char] bracketedSB = bracketed $(char '[') $(char ']') (many (satisfy (/= ']'))) --- | parser wrapped by another parser+-- | Parser wrapped by another parser. -- -- >>> runParser (wrapped ($(char '"')) (many (satisfy (/= '"')))) "\"wrapped\"" -- OK "wrapped" ""@@ -288,12 +288,12 @@ -- | A single digit ----- runParserMaybe digit "5"+-- >>> runParserMaybe digit "5" -- Just 5 digit :: Parser e Int digit = (\c -> ord c - ord '0') <$> satisfyAscii isDigit --- | (unsigned) Int parser+-- | An (unsigned) 'Int' parser -- -- >>> runParserMaybe int "567" -- Just 567@@ -307,7 +307,8 @@ digits :: Parser e (Int, Int) digits = chainr (\n (!place, !acc) -> (place * 10, acc + place * n)) digit (pure (1, 0)) --- |+-- | A 'Double' parser.+-- -- >>> runParser double "1.234x" -- OK 1.234 "x" --@@ -341,6 +342,7 @@ minus = $(char '-') -- | Parser for a signed prefix to a number.+-- -- >>> runParser (signed double) "-1.234x" -- OK (-1.234) "x" signed :: (Num b) => Parser e b -> Parser e b@@ -350,6 +352,9 @@ Nothing -> p Just () -> negate <$> p --- | comma parser+-- | Comma parser+--+-- >>> runParserMaybe comma ","+-- Just () comma :: Parser e () comma = $(char ',')
src/MarkupParse/Patch.hs view
@@ -21,7 +21,7 @@ -- >>> :set -XOverloadedStrings -- >>> import MarkupParse.Patch --- | compare a markup file with a round-trip transformation.+-- | Compare a file with a round-trip transformation. goldenPatch :: (ToExpr a) => (FilePath -> IO a) -> (a -> a) -> FilePath -> TestTree goldenPatch f testf fp = goldenTest