@@ -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.