git.iko.soy/rowtype-yoga/purescript-parsing-dataview
on master help sign in
14 files changed+565−371
since splitsnapshots
unifiedsplit
.github/workflows/push-build.yml+40−9 CHANGELOG.md+39 DEVELOPMENT.md−43 LICENSE+17−104 LICENSE_THIRDPARTY+87 README.md+48−38 bower.json+15−12 packages.dhall+2−1 shell.nix−30 test.dhall → spago-dev.dhall+2−1 spago.dhall+5−2 src/Parsing/DataView.Basic.purs+51 src/Text/Parsing/Parser/DataView.purs → src/Parsing/DataView.purs+141−112 test/Main.purs+118−19
.github/workflows/push-build.yml+40−9
@@ -1,17 +1,48 @@
11name: CI
22
3−on: [push]
3+on:
4+ push:
5+ branches: [master]
6+ pull_request:
7+ branches: [master]
48
59jobs:
610 build:
7−
811 runs-on: ubuntu-latest
912
1013 steps:
11− - uses: actions/checkout@v2
12− - uses: actions/setup-node@v1
13− - run: npm install -g purescript
14− - run: npm install -g spago
15− - name: build
16− run: |
17− spago -x test.dhall test
14+ - uses: actions/checkout@v2
15+
16+ - name: Set up a PureScript toolchain
17+ uses: purescript-contrib/setup-purescript@main
18+ with:
19+ purescript: "unstable"
20+ purs-tidy: "latest"
21+
22+ - name: Cache PureScript dependencies
23+ uses: actions/cache@v2
24+ with:
25+ key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}
26+ path: |
27+ .spago
28+ output
29+ - name: Install dependencies
30+ run: spago install
31+
32+ - name: Build source
33+ run: spago build --no-install --purs-args '--censor-lib --strict'
34+
35+ - name: Install dev dependencies
36+ run: spago -x spago-dev.dhall install
37+
38+ - name: Run tests
39+ run: spago -x spago-dev.dhall test --no-install
40+
41+ - name: Check formatting
42+ run: purs-tidy check src test
43+
44+ - name: Verify Bower & Pulp
45+ run: |
46+ npm install bower pulp@16.0.0-0
47+ npx bower install
48+ npx pulp build -- --censor-lib --strict
CHANGELOG.mdnew file+39
@@ -0,0 +1,39 @@
1+# Changelog
2+
3+## v3.2.0
4+
5+- Build for PureScript v0.15.4
6+- New combinator: `anyTill`
7+- New parser: `anyCodePointUTF8`
8+- Move to rowtype-yoga org
9+
10+## v3.1.0
11+
12+Bugfix `takeN`.
13+
14+## v3.0.0
15+
16+### Breaking changes
17+
18+- Build for PureScript v0.15.0 with __parsing__ v9.0.0.
19+
20+## v2.1.0 2021-11-24
21+
22+Licensing cleanup.
23+
24+## v2.0.0 2021-07-21
25+
26+Build for PureScript v0.14.0.
27+
28+Added the `match` combinator.
29+
30+### Breaking changes
31+
32+Some typeclass constraints on the public API have changed due to changes
33+in the __arraybuffer__ package v11. For the most part we don’t expect
34+this will require any changes to code which depends on this package. If
35+you are using the `anyPrim` function you might have to change `AProxy` to
36+`Prelude.Proxy`. See for more details:
37+
38+* https://github.com/purescript-contrib/purescript-arraybuffer/blob/main/CHANGELOG.md#v1100
39+
DEVELOPMENT.mddeleted−43
@@ -1,43 +0,0 @@
1−# Development Notes
2−
3−All of the commands needed for development are in the `shell.nix` environment.
4−
5−```
6−$ nix-shell
7−```
8−
9−## Edit
10−
11−The `psc-ide` command is available for the [VS Code Purescript IDE extension](https://github.com/nwolverson/vscode-ide-purescript.git).
12−
13−```
14−[nix-shell:]$ code .
15−```
16−
17−## Build
18−
19−```
20−[nix-shell:]$ spago build
21−```
22−
23−## Test
24−
25−The test dependencies are specified seperately in `test.dhall`.
26−
27−```
28−[nix-shell:]$ spago -x test.dhall test
29−```
30−
31−## Publishing to Pursuit with `spago`
32−
33−[Publishing with `spago`](https://github.com/purescript/spago#publish-my-library).
34−
35−```
36−[nix-shell:]$ spago bump-version --no-dry-run major
37−[nix-shell:]$ npm install pulp
38−[nix-shell:]$ node node_modules/pulp/index.js publish
39−```
40−
41−## Bower
42−
43−The `bower.json` is generated by running `spago bump-version`, do not edit it.
LICENSE+17−104
@@ -3,107 +3,20 @@ Copyright 2020 James Dawson Brock
33Redistribution and use in source and binary forms, with or without
44modification, are permitted provided that the following conditions are met:
55
6−* Redistributions of source code must retain the above copyright notice,
7− this list of conditions and the following disclaimer.
8−* Redistributions in binary form must reproduce the above copyright
9− notice, this list of conditions and the following disclaimer in the
10− documentation and/or other materials provided with the distribution.
11−
12−This software is provided by the copyright holders "as is" and any express or
13−implied warranties, including, but not limited to, the implied warranties of
14−merchantability and fitness for a particular purpose are disclaimed. In no
15−event shall the copyright holders be liable for any direct, indirect,
16−incidental, special, exemplary, or consequential damages (including, but not
17−limited to, procurement of substitute goods or services; loss of use, data,
18−or profits; or business interruption) however caused and on any theory of
19−liability, whether in contract, strict liability, or tort (including
20−negligence or otherwise) arising in any way out of the use of this software,
21−even if advised of the possibility of such damage.
22−
23−This library is based on purescript-parsing, copyright follows.
24−
25−--------------------------------------------------------------------------------
26−
27−Copyright 2014-2016 PureScript
28−
29−Redistribution and use in source and binary forms, with or without
30−modification, are permitted provided that the following conditions are met:
31−
32−* Redistributions of source code must retain the above copyright notice,
33− this list of conditions and the following disclaimer.
34−* Redistributions in binary form must reproduce the above copyright
35− notice, this list of conditions and the following disclaimer in the
36− documentation and/or other materials provided with the distribution.
37−
38−This software is provided by the copyright holders "as is" and any express or
39−implied warranties, including, but not limited to, the implied warranties of
40−merchantability and fitness for a particular purpose are disclaimed. In no
41−event shall the copyright holders be liable for any direct, indirect,
42−incidental, special, exemplary, or consequential damages (including, but not
43−limited to, procurement of substitute goods or services; loss of use, data,
44−or profits; or business interruption) however caused and on any theory of
45−liability, whether in contract, strict liability, or tort (including
46−negligence or otherwise) arising in any way out of the use of this software,
47−even if advised of the possibility of such damage.
48−
49−-------------------------------------------------------------------------------
50−
51−This library uses code adapted from the Haskell libraries parsec and indents.
52−Their licenses are reproduced below:
53−
54−'parsec' license:
55−
56− Copyright 1999-2000, Daan Leijen; 2007, Paolo Martini. All rights reserved.
57−
58− Redistribution and use in source and binary forms, with or without
59− modification, are permitted provided that the following conditions are met:
60−
61− * Redistributions of source code must retain the above copyright notice,
62− this list of conditions and the following disclaimer.
63− * Redistributions in binary form must reproduce the above copyright
64− notice, this list of conditions and the following disclaimer in the
65− documentation and/or other materials provided with the distribution.
66−
67− This software is provided by the copyright holders "as is" and any express or
68− implied warranties, including, but not limited to, the implied warranties of
69− merchantability and fitness for a particular purpose are disclaimed. In no
70− event shall the copyright holders be liable for any direct, indirect,
71− incidental, special, exemplary, or consequential damages (including, but not
72− limited to, procurement of substitute goods or services; loss of use, data,
73− or profits; or business interruption) however caused and on any theory of
74− liability, whether in contract, strict liability, or tort (including
75− negligence or otherwise) arising in any way out of the use of this software,
76− even if advised of the possibility of such damage.
77−
78−'indents' license:
79−
80− Copyright (c)2010, Sam Anklesaria
81−
82− All rights reserved.
83−
84− Redistribution and use in source and binary forms, with or without
85− modification, are permitted provided that the following conditions are met:
86−
87− * Redistributions of source code must retain the above copyright
88− notice, this list of conditions and the following disclaimer.
89−
90− * Redistributions in binary form must reproduce the above
91− copyright notice, this list of conditions and the following
92− disclaimer in the documentation and/or other materials provided
93− with the distribution.
94−
95− * Neither the name of Sam Anklesaria nor the names of other
96− contributors may be used to endorse or promote products derived
97− from this software without specific prior written permission.
98−
99− THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
100− "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
101− LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
102− A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
103− OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
104− SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
105− LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
106− DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
107− THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
108− (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
109− OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6+1. Redistributions of source code must retain the above copyright notice, this
7+ list of conditions and the following disclaimer.
8+
9+2. Redistributions in binary form must reproduce the above copyright notice,
10+ this list of conditions and the following disclaimer in the documentation
11+ and/or other materials provided with the distribution.
12+
13+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
LICENSE_THIRDPARTYnew file+87
@@ -0,0 +1,87 @@
1+This library is based on purescript-parsing, copyright follows.
2+
3+--------------------------------------------------------------------------------
4+
5+Copyright 2014-2016 PureScript
6+
7+Redistribution and use in source and binary forms, with or without
8+modification, are permitted provided that the following conditions are met:
9+
10+* Redistributions of source code must retain the above copyright notice,
11+ this list of conditions and the following disclaimer.
12+* Redistributions in binary form must reproduce the above copyright
13+ notice, this list of conditions and the following disclaimer in the
14+ documentation and/or other materials provided with the distribution.
15+
16+This software is provided by the copyright holders "as is" and any express or
17+implied warranties, including, but not limited to, the implied warranties of
18+merchantability and fitness for a particular purpose are disclaimed. In no
19+event shall the copyright holders be liable for any direct, indirect,
20+incidental, special, exemplary, or consequential damages (including, but not
21+limited to, procurement of substitute goods or services; loss of use, data,
22+or profits; or business interruption) however caused and on any theory of
23+liability, whether in contract, strict liability, or tort (including
24+negligence or otherwise) arising in any way out of the use of this software,
25+even if advised of the possibility of such damage.
26+
27+-------------------------------------------------------------------------------
28+
29+This library uses code adapted from the Haskell libraries parsec and indents.
30+Their licenses are reproduced below:
31+
32+'parsec' license:
33+
34+ Copyright 1999-2000, Daan Leijen; 2007, Paolo Martini. All rights reserved.
35+
36+ Redistribution and use in source and binary forms, with or without
37+ modification, are permitted provided that the following conditions are met:
38+
39+ * Redistributions of source code must retain the above copyright notice,
40+ this list of conditions and the following disclaimer.
41+ * Redistributions in binary form must reproduce the above copyright
42+ notice, this list of conditions and the following disclaimer in the
43+ documentation and/or other materials provided with the distribution.
44+
45+ This software is provided by the copyright holders "as is" and any express or
46+ implied warranties, including, but not limited to, the implied warranties of
47+ merchantability and fitness for a particular purpose are disclaimed. In no
48+ event shall the copyright holders be liable for any direct, indirect,
49+ incidental, special, exemplary, or consequential damages (including, but not
50+ limited to, procurement of substitute goods or services; loss of use, data,
51+ or profits; or business interruption) however caused and on any theory of
52+ liability, whether in contract, strict liability, or tort (including
53+ negligence or otherwise) arising in any way out of the use of this software,
54+ even if advised of the possibility of such damage.
55+
56+'indents' license:
57+
58+ Copyright (c)2010, Sam Anklesaria
59+
60+ All rights reserved.
61+
62+ Redistribution and use in source and binary forms, with or without
63+ modification, are permitted provided that the following conditions are met:
64+
65+ * Redistributions of source code must retain the above copyright
66+ notice, this list of conditions and the following disclaimer.
67+
68+ * Redistributions in binary form must reproduce the above
69+ copyright notice, this list of conditions and the following
70+ disclaimer in the documentation and/or other materials provided
71+ with the distribution.
72+
73+ * Neither the name of Sam Anklesaria nor the names of other
74+ contributors may be used to endorse or promote products derived
75+ from this software without specific prior written permission.
76+
77+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md+48−38
@@ -1,18 +1,20 @@
11# purescript-parsing-dataview
22
3−[![CI](https://github.com/jamesdbrock/purescript-parsing-dataview/workflows/CI/badge.svg?branch=master)](https://github.com/jamesdbrock/purescript-parsing-dataview/actions)
3+[![CI](https://github.com/rowtype-yoga/purescript-parsing-dataview/workflows/CI/badge.svg?branch=master)](https://github.com/rowtype-yoga/purescript-parsing-dataview/actions)
44[![Pursuit](http://pursuit.purescript.org/packages/purescript-parsing-dataview/badge)](http://pursuit.purescript.org/packages/purescript-parsing-dataview/)
5+[![Maintainer: jamesdbrock](https://img.shields.io/badge/maintainer-jamesdbrock-teal.svg)](https://github.com/jamesdbrock)
56
6−Primitive parsers for parsing
7−`DataView`s on Javascript `ArrayBuffer`s with the package
8−[__purescript-parsing__](https://pursuit.purescript.org/packages/purescript-parsing/).
7+Primitive parsers for
8+`DataView`s on JavaScript `ArrayBuffer`s with the package
9+[__parsing__](https://pursuit.purescript.org/packages/purescript-parsing/).
910
10−With this package, the input stream support of __purescript-parsing__
11−roughly matches the built-in stream support of __Megaparsec__:
11+With this package, the input stream support of __parsing__
12+is similar to the built-in stream support of [__Megaparsec__](https://hackage.haskell.org/package/megaparsec):
1213
13−| Stream type | purescript-parsing | Megaparsec |
14+| Stream type | parsing | Megaparsec |
1415|----|-----|----|
15−| UTF-16 strings | String | Text |
16+| UTF-16 strings | String | Text < v2.0|
17+| UTF-8 strings | __DataView__ | Text ≥ v2.0 |
1618| Listy strings | Token | String |
1719| Binary blobs | __DataView__ | ByteString |
1820
@@ -24,11 +26,14 @@ Parse values out of a `dataview :: Data.ArrayBuffer.Types.DataView`. All
2426
2527### Parse two numbers
2628
27−Parse two big-endian IEEE 754 double-precision floats.
29+Parse two big-endian IEEE 754 double-precision `Number`s.
2830
2931```purescript
32+import Parsing (runParserT)
33+import Parsing.DataView (anyFloat64be)
34+
3035do
31− result <- Text.Parsing.Parser.runParserT dataview $ do
36+ result <- runParserT dataview do
3237 float1 <- anyFloat64be
3338 float2 <- anyFloat64be
3439 pure $ Tuple float1 float2
@@ -36,49 +41,52 @@ do
3641
3742### Parse an array
3843
39−Parse an array of `n` 32-bit signed integers.
44+Parse an array of `n` 32-bit big-endian signed `Int`s.
4045
4146```purescript
47+import Parsing (runParserT)
48+import Parsing.DataView (anyUint32be)
49+import Data.Unfoldable (replicateA)
50+
4251do
43− result <- Text.Parsing.Parser.runParserT dataview $ replicateA n anyInt32be
52+ result <- runParserT dataview $ replicateA n anyInt32be
4453```
4554
46−### Parse UTF8
55+### Parse UTF-8
4756
48−Parse a `String` as UTF8 with a length prefix.
57+Parse a UTF-8 `String` with a length prefix.
4958
5059We give this as an example, rather than supporting it in the library, because
5160it depends on
52−[`Data.TextDecoding.decodeUtf8`](https://pursuit.purescript.org/packages/purescript-text-encoding/docs/Data.TextDecoding#v:decodeUtf8).
61+[__web-encoding__](https://pursuit.purescript.org/packages/purescript-web-encoding) for UTF-8.
5362
5463```purescript
55−import Effect (Effect, liftEffect)
56−import Control.Monad.Trans.Class (lift)
57−
58−mkTypedArray :: Data.ArrayBuffer.Types.DataView -> Effect Data.ArrayBuffer.Types.Uint8Array
59−mkTypedArray dv = do
60− let buffer = Data.ArrayBuffer.DataView.buffer dv
61− byteOffset = Data.ArrayBuffer.DataView.byteOffset dv
62− byteLength = Data.ArrayBuffer.DataView.byteLength dv
63− Data.ArrayBuffer.Typed.part buffer byteOffset byteLength
64+import Control.Monad.Except (ExceptT)
65+import Data.ArrayBuffer.Cast (toUint8Array)
66+import Effect.Exception (catchException, message)
67+import Parsing (runParserT, liftExceptT)
68+import Parsing.DataView (anyInt32be, takeN)
69+import Web.Encoding.TextDecoder as TextDecoder
70+import Web.Encoding.UtfLabel as UtfLabel
6471
6572do
66− result <- Text.Parsing.Parser.runParserT dataview $ do
67− -- Parse a 32-bit big-endian length prefix for the length of the utf8 string,
68− -- in bytes.
69− length <- anyUint32be
70− stringview <- takeN $ UInt.toInt length
71− stringarray <- lift $ liftEffect $ mkTypedArray stringview
72− case Data.TextDecoding.decodeUtf8 stringarray of
73− Left err -> Data.Parsing.Parser.fail $ show err
74− Right s -> pure s
73+ textDecoder <- TextDecoder.new UtfLabel.utf8
74+
75+ result <- runParserT dataview do
76+ -- First parse a 32-bit big-endian length prefix for the length
77+ -- of the UTF-8 string in bytes.
78+ length <- anyInt32be
79+ stringview <- takeN length
80+ stringarray <- lift $ liftEffect $ toUint8Array stringview
81+ liftExceptT $ ExceptT $ catchException (pure <<< Left <<< message) do
82+ Right <$> TextDecoder.decode stringarray textDecoder
7583```
7684
7785## Serialization
7886
7987This package is for reading (`DataView`s on) `ArrayBuffer`s, not writing
8088them. See the package
81−[__purescript-arraybuffer-builder__](https://pursuit.purescript.org/packages/purescript-arraybuffer-builder/)
89+[__arraybuffer-builder__](https://pursuit.purescript.org/packages/purescript-arraybuffer-builder/)
8290for a way to
8391serialize and build `ArrayBuffer`s.
8492
@@ -88,8 +96,10 @@ serialize and build `ArrayBuffer`s.
8896* [MDN `ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
8997* [MDN `DataView`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView)
9098
91−## Notes
99+## Development
92100
93−Perhaps some day this package can be
94−[merged into __purescript-parsing__](https://github.com/purescript-contrib/purescript-parsing/issues/88),
95−but for now it has too many non-*purescript-contrib* dependencies.
101+Run the tests with the development `spago` file:
102+
103+```
104+spago -x spago-dev.dhall test
105+```
bower.json+15−12
@@ -1,11 +1,11 @@
11{
22 "name": "purescript-parsing-dataview",
33 "license": [
4− "BSD-3-Clause"
4+ "BSD-2-Clause"
55 ],
66 "repository": {
77 "type": "git",
8− "url": "https://github.com/jamesdbrock/purescript-parsing-dataview"
8+ "url": "https://github.com/rowtype-yoga/purescript-parsing-dataview"
99 },
1010 "ignore": [
1111 "**/.*",
@@ -14,15 +14,18 @@
1414 "output"
1515 ],
1616 "dependencies": {
17− "purescript-arraybuffer": "^v11.0.1",
18− "purescript-arraybuffer-types": "^v3.0.0",
19− "purescript-effect": "^v3.0.0",
20− "purescript-float32": "^v1.0.0",
21− "purescript-maybe": "^v5.0.0",
22− "purescript-parsing": "^v6.0.2",
23− "purescript-prelude": "^v5.0.1",
24− "purescript-transformers": "^v5.1.0",
25− "purescript-tuples": "^v6.0.1",
26− "purescript-uint": "^v6.0.3"
17+ "purescript-arraybuffer": "^v13.1.1",
18+ "purescript-arraybuffer-types": "^v3.0.2",
19+ "purescript-effect": "^v4.0.0",
20+ "purescript-enums": "^v6.0.1",
21+ "purescript-float32": "^v2.0.0",
22+ "purescript-maybe": "^v6.0.0",
23+ "purescript-parsing": "^v10.2.0",
24+ "purescript-prelude": "^v6.0.1",
25+ "purescript-strings": "^v6.0.1",
26+ "purescript-tailrec": "^v6.1.0",
27+ "purescript-transformers": "^v6.0.0",
28+ "purescript-tuples": "^v7.0.0",
29+ "purescript-uint": "^v7.0.0"
2730 }
2831}
packages.dhall+2−1
@@ -117,7 +117,8 @@ let additions =
117117-------------------------------
118118-}
119119let upstream =
120− https://github.com/purescript/package-sets/releases/download/psc-0.14.3-20210716/packages.dhall sha256:1f9af624ddfd5352455b7ac6df714f950d499e7e3c6504f62ff467eebd11042c
120+ https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221202/packages.dhall
121+ sha256:4e2f2e060ab73b119eeb55fe9bc75168b380fc5e0696d2cc2941ac674b32cb3c
121122
122123let overrides = {=}
123124
shell.nixdeleted−30
@@ -1,30 +0,0 @@
1−# Universal shell for PureScript repos
2−# { pkgs ? import <nixpkgs> { }
3−{ pkgs ? import (builtins.fetchGit {
4− # https://github.com/NixOS/nixpkgs/releases/tag/20.09
5− url = "https://github.com/nixos/nixpkgs/";
6− ref = "refs/tags/20.09";
7− rev = "cd63096d6d887d689543a0b97743d28995bc9bc3";
8− }) {}
9−}:
10−let
11− easy-ps = import (builtins.fetchGit {
12− url = "https://github.com/justinwoo/easy-purescript-nix.git";
13− rev = "bbef4245cd6810ea84e97a47c801947bfec9fadc";
14− }) { inherit pkgs; };
15−in
16−pkgs.mkShell {
17− nativeBuildInputs = [
18− easy-ps.purs-0_14_3
19− easy-ps.spago
20− easy-ps.pulp
21− easy-ps.psc-package
22− pkgs.nodejs-14_x
23− pkgs.nodePackages.bower
24− ];
25− LC_ALL = "C.UTF-8"; # https://github.com/purescript/spago/issues/507
26− # https://github.com/purescript/spago#install-autocompletions-for-bash
27− shellHook = ''
28− source <(spago --bash-completion-script `which spago`)
29− '';
30−}
test.dhall → spago-dev.dhallrenamed+2−1
@@ -18,7 +18,8 @@ in conf //
1818 , "console"
1919 , "foldable-traversable"
2020 , "lists"
21− , "psci-support"
2221 , "either"
22+ , "web-encoding"
23+ , "exceptions"
2324 ]
2425 }
spago.dhall+5−2
@@ -5,16 +5,19 @@
55 [ "arraybuffer"
66 , "arraybuffer-types"
77 , "effect"
8+ , "enums"
89 , "float32"
910 , "maybe"
1011 , "parsing"
1112 , "prelude"
13+ , "strings"
14+ , "tailrec"
1215 , "transformers"
1316 , "tuples"
1417 , "uint"
1518 ]
1619, packages = ./packages.dhall
1720, sources = [ "src/**/*.purs" ]
18−, license = "BSD-3-Clause"
19−, repository = "https://github.com/jamesdbrock/purescript-parsing-dataview"
21+, license = "BSD-2-Clause"
22+, repository = "https://github.com/rowtype-yoga/purescript-parsing-dataview"
2023}
src/Parsing/DataView.Basic.pursnew file+51
@@ -0,0 +1,51 @@
1+-- | Basic `DataView` parsers derived from primitive `DataView` parsers.
2+-- |
3+-- | All of these parsers will consume when they succeed.
4+-- |
5+-- | All of these parsers will not consume and will automatically
6+-- | backtrack when they fail.
7+module Parsing.DataView.Basic
8+ ( anyCodePointUTF8
9+ ) where
10+
11+import Prelude
12+
13+import Data.ArrayBuffer.Types (DataView)
14+import Data.Enum (toEnum)
15+import Data.String (CodePoint)
16+import Data.UInt (toInt)
17+import Effect.Class (class MonadEffect)
18+import Parsing (ParserT, fail, liftMaybe)
19+import Parsing.Combinators (tryRethrow)
20+import Parsing.DataView (anyUint8)
21+
22+-- | Decode one UTF-8 code point, *1—4* bytes.
23+anyCodePointUTF8
24+ :: forall m
25+ . MonadEffect m
26+ => ParserT DataView m CodePoint
27+anyCodePointUTF8 = tryRethrow do
28+ -- https://en.wikipedia.org/wiki/UTF-8#Encoding
29+ c0 <- toInt <$> anyUint8
30+ if c0 < 128 then do
31+ liftMaybe (\_ -> "Invalid CodePoint " <> show c0) (toEnum c0)
32+ else if c0 < 192 then fail "Invalid UTF-8 encoding"
33+ else if c0 < 224 then do
34+ c1 <- toInt <$> anyUint8
35+ if c1 < 128 || c1 > 191 then fail "Invalid UTF-8 encoding" else pure unit
36+ let codepoint = (64 * (c0 `mod` 32)) + (c1 `mod` 64)
37+ liftMaybe (\_ -> "Invalid CodePoint " <> show codepoint) (toEnum codepoint)
38+ else if c0 < 240 then do
39+ c1 <- toInt <$> anyUint8
40+ c2 <- toInt <$> anyUint8
41+ if c1 < 128 || c1 > 191 || c2 < 128 || c2 > 191 then fail "Invalid UTF-8 encoding" else pure unit
42+ let codepoint = (4096 * (c0 `mod` 16)) + (64 * (c1 `mod` 64)) + (c2 `mod` 64)
43+ liftMaybe (\_ -> "Invalid CodePoint " <> show codepoint) (toEnum codepoint)
44+ else if c0 < 248 then do
45+ c1 <- toInt <$> anyUint8
46+ c2 <- toInt <$> anyUint8
47+ c3 <- toInt <$> anyUint8
48+ if c1 < 128 || c1 > 191 || c2 < 128 || c2 > 191 || c3 < 128 || c3 > 191 then fail "Invalid UTF-8 encoding" else pure unit
49+ let codepoint = (262144 * (c0 `mod` 8)) + (4096 * (c1 `mod` 64)) + (64 * (c2 `mod` 64)) + (c3 `mod` 64)
50+ liftMaybe (\_ -> "Invalid CodePoint " <> show codepoint) (toEnum codepoint)
51+ else fail "Invalid UTF-8 encoding"
src/Text/Parsing/Parser/DataView.purs → src/Parsing/DataView.pursrenamed+141−112
@@ -1,24 +1,42 @@
1−-- | Primitive parsers for parsing Javascript ArrayBuffers with the
2−-- | [`Text.Parsing.Parser`](https://pursuit.purescript.org/packages/purescript-parsing/docs/Text.Parsing.Parser)
3−-- | module in package __purescript-parsing__.
1+-- | Primitive parsers for input type `DataView`.
2+-- |
3+-- | All of these primitive parsers will consume when they succeed.
4+-- |
5+-- | All of these primitive parsers will not consume and will automatically
6+-- | backtrack when they fail.
7+-- |
48-- | See the package README for usage examples.
59-- |
10+-- | ### Mutable ArrayBuffer
11+-- |
612-- | All of the parsers in this module operate on an input stream of
7−-- | [`Data.ArrayBuffer.Types.DataView`](https://pursuit.purescript.org/packages/purescript-arraybuffer-types/docs/Data.ArrayBuffer.Types#t:DataView),
8−-- | which represents a range of an
9−-- | `Data.ArrayBuffer.Types.ArrayBuffer`.
13+-- | [__Data.ArrayBuffer.Types.DataView__](https://pursuit.purescript.org/packages/purescript-arraybuffer-types/docs/Data.ArrayBuffer.Types#t:DataView),
14+-- | which represents a range of a mutable
15+-- | [__Data.ArrayBuffer.Types.ArrayBuffer__](https://pursuit.purescript.org/packages/purescript-arraybuffer-types/docs/Data.ArrayBuffer.Types#t:ArrayBuffer).
1016-- |
11−-- | For operations for working with `ArrayBuffer` and `DataView`, see
17+-- | For operations for working with `DataView`, see
1218-- | module
13−-- | [`Data.ArrayBuffer.DataView`](https://pursuit.purescript.org/packages/purescript-arraybuffer/docs/Data.ArrayBuffer.DataView)
14−-- | in package __purescript-arraybuffer__.
19+-- | [__Data.ArrayBuffer.DataView__](https://pursuit.purescript.org/packages/purescript-arraybuffer/docs/Data.ArrayBuffer.DataView)
20+-- | in package __arraybuffer__.
1521-- |
1622-- | Reading from an `ArrayBuffer` is an `Effect`ful activity, so
1723-- | all parsers in this module must be run in a
18−-- | `MonadEffect m => ParserT DataView m` context, with
19−-- | `Text.Parsing.Parser.runParserT`.
20−module Text.Parsing.Parser.DataView
21− ( anyPrim
24+-- | `MonadEffect m => ParserT DataView m` context with
25+-- | `Parsing.runParserT`.
26+-- |
27+-- | ### Position
28+-- |
29+-- | In a `DataView` parser, the `Position {index}` counts the number of
30+-- | bytes since the beginning of the input.
31+-- |
32+-- | The `Postion {line,column}` fields are unused and will remain constant *1*.
33+module Parsing.DataView
34+ ( takeN
35+ , takeRest
36+ , eof
37+ , match
38+ , anyTill
39+ , anyPrim
2240 , anyInt8
2341 , anyInt16be
2442 , anyInt16le
@@ -48,96 +66,70 @@ module Text.Parsing.Parser.DataView
4866 , satisfyFloat32le
4967 , satisfyFloat64be
5068 , satisfyFloat64le
51− , takeN
52− , takeRest
53− , eof
54− , match
55− )
56− where
69+ ) where
5770
5871import Prelude
5972
73+import Control.Monad.Rec.Class (Step(..), tailRecM)
6074import Control.Monad.Trans.Class (lift)
61−import Control.Monad.State (get, put)
75+import Data.ArrayBuffer.DataView (Endian(LE, BE))
76+import Data.ArrayBuffer.DataView (buffer, byteLength, byteOffset, get, part) as DV
77+import Data.ArrayBuffer.Types (DataView, Int16, Int32, Int8, Uint16, Uint32, Uint8, Float32, Float64, ByteLength)
78+import Data.ArrayBuffer.ValueMapping (class BytesPerType, class BinaryValue, class ShowArrayViewType, byteWidth)
79+import Data.Float32 as Float32
6280import Data.Maybe (Maybe(..))
63−import Data.Symbol (SProxy(..), class IsSymbol, reflectSymbol)
64−import Data.Tuple(Tuple(..))
81+import Data.Symbol (class IsSymbol)
82+import Data.Tuple (Tuple(..))
6583import Data.UInt (UInt)
66−import Data.Float32 as Float32
6784import Effect.Class (class MonadEffect, liftEffect)
85+import Parsing (ParseState(..), ParserT, Position(..), consume, fail, getParserT, stateParserT)
86+import Parsing.Combinators (alt, try, tryRethrow)
6887import Type.Proxy (Proxy(..))
6988
70−import Text.Parsing.Parser (ParserT, ParseState(..), fail)
71−import Text.Parsing.Parser.Pos (Position(..))
72−import Text.Parsing.Parser.Combinators (tryRethrow)
73−import Data.ArrayBuffer.Types
74− ( DataView
75− , Int16
76− , Int32
77− , Int8
78− , Uint16
79− , Uint32
80− , Uint8
81− , Float32
82− , Float64
83− , ByteLength
84− )
85−import Data.ArrayBuffer.ValueMapping
86− ( class BytesPerType
87− , class BinaryValue
88− , class ShowArrayViewType
89− , byteWidth
90− )
91−import Data.ArrayBuffer.DataView
92− ( Endian(LE,BE)
93− )
94−import Data.ArrayBuffer.DataView (byteLength, byteOffset, get, part, buffer) as DV
95−
9689-- | Parse one fixed-bit-width `Data.ArrayBuffer.Types.ArrayViewType` primitive
9790-- | of a given endianness.
9891-- |
9992-- | #### Example
10093-- |
101−-- | Parse a little-endian 32-bit signed integer (4 bytes):
94+-- | Parse a little-endian 32-bit two’s-complement signed integer (4 bytes):
10295-- |
10396-- | anyPrim LE (Proxy :: Proxy Int32)
10497-- |
10598-- | or just use the convenience function `anyInt32le`, see below.
106−anyPrim :: forall a name m t
107− . BinaryValue a t
108− => BytesPerType a
109− => ShowArrayViewType a name
110− => IsSymbol name
111− => MonadEffect m
112− => Endian
113− -> Proxy a
114− -> ParserT DataView m t
99+anyPrim
100+ :: forall a name m t
101+ . BinaryValue a t
102+ => BytesPerType a
103+ => ShowArrayViewType a name
104+ => IsSymbol name
105+ => MonadEffect m
106+ => Endian
107+ -> Proxy a
108+ -> ParserT DataView m t
115109anyPrim endian _ = do
116− ParseState input (Position{line,column}) _ <- get
117− lift (liftEffect (DV.get endian (Proxy :: Proxy a) input (column-1))) >>= case _ of
118− Nothing -> fail $ "Cannot parse " <> reflectSymbol (SProxy :: SProxy name) <>
119− ", unexpected end of DataView"
110+ ParseState input (Position { index }) _ <- getParserT
111+ lift (liftEffect (DV.get endian (Proxy :: Proxy a) input index)) >>= case _ of
112+ Nothing -> fail "anyPrim unexpected end of DataView"
120113 Just i -> do
121− put $ ParseState input (Position {line, column:column + byteWidth (Proxy :: Proxy a) }) true
122− pure i
114+ stateParserT \_ -> Tuple i $ ParseState input (Position { line: 1, column: 1, index: index + byteWidth (Proxy :: Proxy a) }) true
123115
124−-- | Parse one 8-bit signed integer (byte).
116+-- | Parse one 8-bit two’s-complement signed integer (byte).
125117anyInt8 :: forall m. MonadEffect m => ParserT DataView m Int
126118anyInt8 = anyPrim LE (Proxy :: Proxy Int8)
127119
128−-- | Parse one 16-bit big-endian signed integer.
120+-- | Parse one 16-bit big-endian two’s-complement signed integer.
129121anyInt16be :: forall m. MonadEffect m => ParserT DataView m Int
130122anyInt16be = anyPrim BE (Proxy :: Proxy Int16)
131123
132−-- | Parse one 16-bit little-endian signed integer.
124+-- | Parse one 16-bit little-endian two’s-complement signed integer.
133125anyInt16le :: forall m. MonadEffect m => ParserT DataView m Int
134126anyInt16le = anyPrim LE (Proxy :: Proxy Int16)
135127
136−-- | Parse one 32-bit big-endian signed integer.
128+-- | Parse one 32-bit big-endian two’s-complement signed integer.
137129anyInt32be :: forall m. MonadEffect m => ParserT DataView m Int
138130anyInt32be = anyPrim BE (Proxy :: Proxy Int32)
139131
140−-- | Parse one 32-bit little-endian signed integer.
132+-- | Parse one 32-bit little-endian two’s-complement signed integer.
141133anyInt32le :: forall m. MonadEffect m => ParserT DataView m Int
142134anyInt32le = anyPrim LE (Proxy :: Proxy Int32)
143135
@@ -161,19 +153,19 @@ anyUint32be = anyPrim BE (Proxy :: Proxy Uint32)
161153anyUint32le :: forall m. MonadEffect m => ParserT DataView m UInt
162154anyUint32le = anyPrim LE (Proxy :: Proxy Uint32)
163155
164−-- | Parse one 32-bit big-endian floating point number.
156+-- | Parse one 32-bit big-endian IEEE 754 floating-point number.
165157anyFloat32be :: forall m. MonadEffect m => ParserT DataView m Float32.Float32
166158anyFloat32be = anyPrim BE (Proxy :: Proxy Float32)
167159
168−-- | Parse one 32-bit little-endian floating point number.
160+-- | Parse one 32-bit little-endian IEEE 754 floating-point number.
169161anyFloat32le :: forall m. MonadEffect m => ParserT DataView m Float32.Float32
170162anyFloat32le = anyPrim LE (Proxy :: Proxy Float32)
171163
172−-- | Parse one 64-bit big-endian floating point number.
164+-- | Parse one 64-bit big-endian IEEE 754 floating-point number.
173165anyFloat64be :: forall m. MonadEffect m => ParserT DataView m Number
174166anyFloat64be = anyPrim BE (Proxy :: Proxy Float64)
175167
176−-- | Parse one 64-bit little-endian floating point number.
168+-- | Parse one 64-bit little-endian IEEE 754 floating-point number.
177169anyFloat64le :: forall m. MonadEffect m => ParserT DataView m Number
178170anyFloat64le = anyPrim LE (Proxy :: Proxy Float64)
179171
@@ -186,22 +178,22 @@ anyFloat64le = anyPrim LE (Proxy :: Proxy Float64)
186178-- | satisfy LE (Proxy :: Proxy Int32) (_ == 3)
187179-- |
188180-- | or just use the convenience function `satisfyInt32le`, see below.
189−satisfy :: forall a name m t
190− . BinaryValue a t
191− => BytesPerType a
192− => ShowArrayViewType a name
193− => IsSymbol name
194− => Show t
195− => MonadEffect m
196− => Endian
197− -> Proxy a
198− -> (t -> Boolean)
199− -> ParserT DataView m t
181+satisfy
182+ :: forall a name m t
183+ . BinaryValue a t
184+ => BytesPerType a
185+ => ShowArrayViewType a name
186+ => IsSymbol name
187+ => Show t
188+ => MonadEffect m
189+ => Endian
190+ -> Proxy a
191+ -> (t -> Boolean)
192+ -> ParserT DataView m t
200193satisfy endian proxy f = tryRethrow do
201194 i <- anyPrim endian proxy
202195 if f i then pure i
203− else fail $ reflectSymbol (SProxy :: SProxy name) <>
204− " " <> show i <> " did not satisfy predicate."
196+ else fail "satisfy predicate failed"
205197
206198-- | Parse one 8-bit signed integer that satisfies the given predicate.
207199satisfyInt8 :: forall m. MonadEffect m => (Int -> Boolean) -> ParserT DataView m Int
@@ -270,29 +262,32 @@ satisfyFloat64le = satisfy LE (Proxy :: Proxy Float64)
270262-- |
271263takeN :: forall m. MonadEffect m => ByteLength -> ParserT DataView m DataView
272264takeN n = do
273− ParseState input (Position {line,column}) _ <- get
274− unless (n >= 0) $ fail $ "Cannot take negative number of bytes."
275− unless (column + n - 1 <= DV.byteLength input) $
276− fail $ "Cannot take " <> show n <> " bytes, only " <>
277− show (DV.byteLength input - column + 1) <> " bytes remain."
278− p <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + (column-1)) n
279− put $ ParseState input (Position {line,column:column+n}) true
280− pure p
265+ ParseState input (Position { index }) _ <- getParserT
266+ if (n < 0) then
267+ fail "takeN cannot take negative number of bytes"
268+ else if (index + n > DV.byteLength input) then
269+ fail "takeN expected N bytes"
270+ else do
271+ p <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + index) n
272+ stateParserT \_ -> Tuple p $ ParseState input (Position { line: 1, column: 1, index: index + n }) true
281273
282274-- | Take the rest of the input, however many bytes remain. Always succeeds.
283275takeRest :: forall m. MonadEffect m => ParserT DataView m DataView
284276takeRest = do
285− ParseState input (Position {line,column}) _ <- get
286− p <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + (column-1))
287− (DV.byteLength input - (column-1))
288− put $ ParseState input (Position {line,column:DV.byteLength input + 1}) true
289− pure p
277+ ParseState input (Position { index }) _ <- getParserT
278+ p <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + index)
279+ (DV.byteLength input - index)
280+ stateParserT \_ -> Tuple p $ ParseState input (Position { line: 1, column: 1, index: DV.byteLength input + 1 }) true
290281
291−-- | Parse succeeds at the end of the input DataView. Consumes no input.
282+-- | Parse succeeds at the end of the input DataView.
292283eof :: forall m. Monad m => ParserT DataView m Unit
293284eof = do
294− ParseState input (Position {column}) _ <- get
295− unless (column > DV.byteLength input) $ fail "Expected end of DataView"
285+ ParseState input (Position { index }) _ <- getParserT
286+ if (index + 1 > DV.byteLength input) then do
287+ -- We must consume so this combines correctly with notFollowedBy
288+ consume
289+ else do
290+ fail "eof expected end of DataView"
296291
297292-- | The
298293-- | [famous `match`](http://www.serpentine.com/blog/2014/05/31/attoparsec/#from-strings-to-buffers-and-cursors)
@@ -302,23 +297,57 @@ eof = do
302297-- | was consumed while it was being parsed.
303298match :: forall a m. MonadEffect m => ParserT DataView m a -> ParserT DataView m (Tuple DataView a)
304299match p = do
305− ParseState input (Position {column:column0}) _ <- get
300+ ParseState input (Position { index: index0 }) _ <- getParserT
306301 x <- p
307− ParseState _ (Position {column:column1}) _ <- get
308− part <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + (column0-1)) (column1-column0)
302+ ParseState _ (Position { index: index1 }) _ <- getParserT
303+ part <- lift $ liftEffect $ DV.part (DV.buffer input) (DV.byteOffset input + index0) (index1 - index0)
309304 pure $ Tuple part x
310305
306+-- | Combinator which finds the first position in the input `DataView` where the
307+-- | phrase can parse. Returns both the
308+-- | parsed result and the unparsable input section searched before the parse.
309+-- | Will fail if no section of the input is parseable. To backtrack the input
310+-- | stream on failure, combine with `tryRethrow`.
311+-- |
312+-- | This combinator is equivalent to `manyTill_ anyInt8`, but it will be
313+-- | faster because it returns a slice of the input `DataView` for the
314+-- | section preceding the parse instead of a `List Int`.
315+-- |
316+-- | Be careful not to look too far
317+-- | ahead; if the phrase parser looks to the end of the input then `anyTill`
318+-- | could be *O(n²)*.
319+anyTill
320+ :: forall m a
321+ . MonadEffect m
322+ => ParserT DataView m a
323+ -> ParserT DataView m (Tuple DataView a)
324+anyTill p = do
325+ ParseState input1 (Position { index: index0 }) _ <- getParserT
326+ Tuple index1 t <- tailRecM go unit
327+ part <- lift $ liftEffect $ DV.part (DV.buffer input1) (DV.byteOffset input1 + index0) (index1 - index0)
328+ pure $ Tuple part t
329+ where
330+ go unit = alt
331+ ( do
332+ ParseState _ (Position { index: index1 }) _ <- getParserT
333+ t <- try p
334+ pure $ Done $ Tuple index1 t
335+ )
336+ ( do
337+ _ <- anyInt8
338+ pure $ Loop unit
339+ )
340+
311341-- ****************************** Notes ****************************************
312342--
313−-- The `initialPostion` in a `ParserT` is `Postion{line:1,column:1)`.
314−-- We keep `line` invariant and use `column-1` to denote the offset into the
315−-- `DataView`, that's why there are so many plus 1 and minus 1 operations
316−-- in this module.
317−-- https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html
318−--
319343-- We cannot have a primitive parser which parses a `DataView` and produces
320344-- an `ArrayView` (a Javascript Typed Array). Javascript DataViews are
321345-- endianess-aware, but Javascript Typed Arrays assume the native endianness
322346-- of the local machine. DataViews are intended to be used for I/O, ArrayViews
323347-- are intended to be used internally for graphics in a process, and they're
324348-- not intended to be both applied to the same ArrayBuffer.
349+-- The exception: `Uint8Array`
350+--
351+-- The failure messages are all constant strings form performance reasons.
352+-- If failure messages were constructed lazily then we could have more
353+-- descriptive messages.
test/Main.purs+118−19
@@ -2,22 +2,34 @@ module Test.Main where
22
33import Prelude
44
5+import Control.Monad.Except (ExceptT(..))
56import Control.Monad.Trans.Class (lift)
6−import Data.ArrayBuffer.ArrayBuffer (empty) as AB
7+import Data.ArrayBuffer.ArrayBuffer as AB
8+import Data.ArrayBuffer.Cast (fromUint8Array, toUint8Array)
79import Data.ArrayBuffer.DataView as DataView
10+import Data.ArrayBuffer.Typed (fromArray)
11+import Data.ArrayBuffer.Types (Uint8Array)
812import Data.Either (Either(..))
13+import Data.Enum (fromEnum)
914import Data.List (fromFoldable)
15+import Data.List as List
1016import Data.Maybe (Maybe(..))
1117import Data.Traversable (for_)
1218import Data.Tuple (Tuple(..))
1319import Data.UInt (fromInt)
1420import Effect (Effect)
21+import Effect.Class (liftEffect)
1522import Effect.Console (logShow)
16−import Test.Assert (assert')
17−import Text.Parsing.Parser (ParserT, runParserT, parseErrorPosition, fail)
18−import Text.Parsing.Parser.Combinators (manyTill)
19−import Text.Parsing.Parser.DataView as DV
20−import Text.Parsing.Parser.Pos (Position(..))
23+import Effect.Exception (catchException, message)
24+import Parsing (ParseError(..), ParserT, Position(..), fail, liftExceptT, parseErrorPosition, runParserT)
25+import Parsing.Combinators (many, manyTill)
26+import Parsing.DataView (anyInt8, anyTill, eof, satisfyInt8)
27+import Parsing.DataView as DV
28+import Parsing.DataView.Basic (anyCodePointUTF8)
29+import Test.Assert (assert', assertEqual')
30+import Web.Encoding.TextDecoder as TextDecoder
31+import Web.Encoding.TextEncoder as TextEncoder
32+import Web.Encoding.UtfLabel as UtfLabel
2133
2234parseTestT :: forall s a. Show a => Eq a => s -> a -> ParserT s Effect a -> Effect Unit
2335parseTestT input expected p = do
@@ -32,19 +44,15 @@ parseFailT :: forall s a. s -> Position -> ParserT s Effect a -> Effect Unit
3244parseFailT input failPos p = do
3345 result <- runParserT input p
3446 case result of
35− Right _ -> assert' ("ParseError expected at " <> show failPos) false
47+ Right _ -> assert' ("ParseError expected at " <> show failPos) false
3648 Left err -> do
3749 let pos = parseErrorPosition err
3850 assert' ("ParseError expected at " <> show failPos <> " but parser failed at " <> show pos)
39− (pos == failPos)
51+ (pos == failPos)
4052 logShow $ show failPos
4153
4254mkPos :: Int -> Position
43−mkPos n = mkPos' n 1
44−
45−mkPos' :: Int -> Int -> Position
46−mkPos' column line = Position { column: column, line: line }
47−
55+mkPos n = Position { index: n, line: 1, column: 1 }
4856
4957main :: Effect Unit
5058main = do
@@ -52,13 +60,13 @@ main = do
5260 -- Test input is DataView = [5,6,7,8,9]
5361 ab <- AB.empty 5
5462 let dv = DataView.whole ab
55− for_ [0,1,2,3,4] $ \i -> DataView.setInt8 dv i (i+5)
63+ for_ [ 0, 1, 2, 3, 4 ] $ \i -> DataView.setInt8 dv i (i + 5)
5664 -- anyInt8
5765 parseTestT dv 5 DV.anyInt8
5866 -- manyTill eof
59− parseTestT dv (fromFoldable [5,6,7,8,9]) $ manyTill DV.anyInt8 DV.eof
67+ parseTestT dv (fromFoldable [ 5, 6, 7, 8, 9 ]) $ manyTill DV.anyInt8 DV.eof
6068 -- manyTill satisfy
61− parseTestT dv (fromFoldable [5,6,7,8]) $ manyTill DV.anyInt8 (DV.satisfyInt8 (_ == 9))
69+ parseTestT dv (fromFoldable [ 5, 6, 7, 8 ]) $ manyTill DV.anyInt8 (DV.satisfyInt8 (_ == 9))
6270 -- anyInt16be
6371 parseTestT dv 0x0506 DV.anyInt16be
6472 -- anyInt16le
@@ -68,7 +76,7 @@ main = do
6876 -- anyUint16le
6977 parseTestT dv (fromInt 0x0605) DV.anyUint16le
7078 -- fail on anyInt16le past end of DataView
71− parseFailT dv (mkPos 5) $ DV.anyInt16le *> DV.anyInt16le *> DV.anyInt16le
79+ parseFailT dv (mkPos 4) $ DV.anyInt16le *> DV.anyInt16le *> DV.anyInt16le
7280 -- anyInt32be and anyInt8
7381 parseTestT dv (Tuple 0x05060708 0x09) $ do
7482 l <- DV.anyInt32be
@@ -105,7 +113,7 @@ main = do
105113 Right actual -> pure actual
106114 Left err -> fail $ show err
107115 -- fail on takeN past end of DataView
108− parseFailT dv (mkPos 1) $ DV.takeN 6
116+ parseFailT dv (mkPos 0) $ DV.takeN 6
109117 -- takeRest then new parse on takeRest then anyInt8
110118 parseTestT dv 0x07 do
111119 _ <- DV.takeN 1
@@ -116,11 +124,12 @@ main = do
116124 Left err -> fail $ show err
117125 Right x -> pure x
118126 -- takeN then a new parse then fail on anyInt16le past end of DataView
119− parseFailT dv (mkPos 2) $ do
127+ parseFailT dv (mkPos 1) $ do
120128 dv2 <- DV.takeN 1
121129 lift (runParserT dv2 DV.anyInt16le) >>= case _ of
122130 Left err -> fail $ show err
123131 Right x -> pure x
132+ parseTestT dv unit $ DV.takeN 5 *> pure unit
124133
125134 -- test match combinator
126135 runParserT dv (DV.match (DV.anyInt8 *> DV.anyInt8)) >>= case _ of
@@ -130,3 +139,93 @@ main = do
130139 assert' ("match failed " <> show r0 <> " " <> show r1) $ r0 == Just 5 && r1 == Just 6
131140 logShow $ show r0 <> " " <> show r1
132141 Left err -> assert' ("match failed " <> show err) false
142+
143+ do
144+ ablarge <- AB.empty 200000
145+ let dvlarge = DataView.whole ablarge
146+ -- forE 0 (AB.byteLength ablarge - 1) \i -> void $ DataView.setInt8 dvlarge i i
147+ x <- map List.length <$> runParserT dvlarge (many anyInt8)
148+ assertEqual' "stack safety"
149+ { actual: x
150+ , expected: Right 200000
151+ }
152+
153+ do
154+ let
155+ teststring = "test string 👓"
156+
157+ textDecoder <- TextDecoder.new UtfLabel.utf8
158+
159+ textEncoder <- TextEncoder.new
160+
161+ let testarray = TextEncoder.encode teststring textEncoder
162+
163+ testview <- fromUint8Array testarray
164+
165+ result <- runParserT testview do
166+ let length = DataView.byteLength testview
167+ stringview <- DV.takeN length
168+ stringarray <- lift $ liftEffect $ toUint8Array stringview
169+ liftExceptT $ ExceptT $ catchException (pure <<< Left <<< message) do
170+ Right <$> TextDecoder.decode stringarray textDecoder
171+
172+ assertEqual' "UTF-8 decoding example"
173+ { expected: Right teststring
174+ , actual: result
175+ }
176+
177+ do
178+ result <- runParserT dv do
179+ Tuple pre _ <- anyTill do
180+ _ <- satisfyInt8 (_ == 7)
181+ _ <- satisfyInt8 (_ == 8)
182+ pure unit
183+ pure pre
184+
185+ assertEqual' "anyTill"
186+ { expected: Right 2
187+ , actual: DataView.byteLength <$> result
188+ }
189+
190+ do
191+ -- Test one code point from each byte group
192+ -- a 97
193+ -- λ 955
194+ -- 月 0x6708
195+ -- 👓 0x1F453
196+ let teststring = "aλ月👓a"
197+ textEncoder <- TextEncoder.new
198+ let testarray = TextEncoder.encode teststring textEncoder
199+ testview <- fromUint8Array testarray
200+
201+ result <- (map <<< map <<< map) fromEnum $ runParserT testview do
202+ c0 <- anyCodePointUTF8
203+ c1 <- anyCodePointUTF8
204+ c2 <- anyCodePointUTF8
205+ c3 <- anyCodePointUTF8
206+ c4 <- anyCodePointUTF8
207+ eof
208+ pure [ c0, c1, c2, c3, c4 ]
209+
210+ assertEqual' "anyCodePointUTF8 basic"
211+ { expected: Right [ 97, 955, 0x6708, 0x1F453, 97 ]
212+ , actual: result
213+ }
214+
215+ do
216+ test :: Uint8Array <- fromArray (map fromInt [ 0x00BF ])
217+ testv <- fromUint8Array test
218+ result <- runParserT testv anyCodePointUTF8
219+ assertEqual' "anyCodePointUTF8 invalid 1"
220+ { expected: Left (ParseError "Invalid UTF-8 encoding" (Position { index: 0, column: 1, line: 1 }))
221+ , actual: result
222+ }
223+
224+ do
225+ test :: Uint8Array <- fromArray (map fromInt [ 0x00C0, 0 ])
226+ testv <- fromUint8Array test
227+ result <- runParserT testv anyCodePointUTF8
228+ assertEqual' "anyCodePointUTF8 invalid 2"
229+ { expected: Left (ParseError "Invalid UTF-8 encoding" (Position { index: 0, column: 1, line: 1 }))
230+ , actual: result
231+ }