APIJson.Pull
Json.Pull
The JSON pull parser and the decoding protocol behind Json.Decode.
Everyday code imports Json. This module serves custom Decode instances
and code that consumes a document token by token: the Pull effect and
its handlers, container and path helpers, and the buffered Scan
protocol that derived decoders run on.
Parsing rejects invalid UTF-8, malformed escapes, trailing input, and nesting deeper than 256 arrays or objects. An error is reported at the start of the offending token, whatever the input’s buffering; a missing field after its object, and a duplicate one at its value. Malformed or incomplete encoded input is reported at the first offending sequence, which is left unconsumed.
Error
type Error = { message : String , offset : Int , line : Int , column : Int , path : String }Source
A JSON failure and where it happened.
Fields
Instances:
Eq Error
Show Error
Number
type NumberSource
A JSON number kept as its lexeme, so it round-trips without conversion.
Json.number makes one and numberText reads it.
Scan
type ScanSource
A pure, bounded view of one immutable text window. A completed value keeps its container allowance unchanged. Negative allowance marks lookahead that must be consumed through Pull before scanning can continue.
ScanStep
type ScanStep a = ScanDone a Scan | ScanContinue (Result (() ->{Pull} ScanStep a) (a, Scan))Source
Suspensions return a step, so a long sequence of refills or unknown keys runs through runScan’s tail loop instead of nesting continuation calls. What a suspended scan answers when run.
Token
type Token = BeginObject | EndObject | BeginArray | EndArray | Colon | Comma | StringToken String | NumberToken String | BooleanToken Bool | NullToken | EndInputSource
One lexical JSON token.
Constructors
Instances:
Eq Token
Show Token
Value
type Value = Null | Boolean Bool | Numeric Number | Text String | Array (List Value) | Object (List (String, Value))Source
A generic JSON document tree. Object members keep their order.
Constructors
Instances:
Decode Value
Encode Value
Decode
class Decode aSource
Types that decode from JSON. Derive it, or write scanValue; a decoder
written with Pull operations is
scanValue cursor = Json.Pull.scanFromPull cursor { ... }.
Methods
scanValue
scanValue : Decode a => Scan -> Result (() ->{Pull} ScanStep a) (a, Scan)Source
Starts a pure buffered scan of one value at cursor. Ok is the value
and the position after it; Err suspends work that runScan will
run, and is not a JSON error. Creating either consumes nothing, refills
nothing, and performs no consumer effects; Ok keeps the nesting
allowance decodeValue would.
Instances:
Decode ()
Decode Bool
Decode Char
Decode Float
Decode Int
Decode String
Decode Value
Decode a => Decode (List a)
Decode a => Decode (Maybe a)
Pull
effect PullSource
Reading a document token by token. withReader and withTextReader
handle it.
Operations
next
next : () ->{Pull} TokenSource
Consumes and answers the next token, or the pending one from peek.
at
at : () ->{Pull} ErrorSource
The current source position, as an Error with an empty message. It
publishes buffered consumption.
nextElement
nextElement : Bool ->{Pull} BoolSource
Consumes an array’s separator or closing bracket and answers whether
another element follows, leaving that element unscanned. first says
whether no element has been read yet. It honors a token already
obtained by peek.
nextKey
nextKey : Bool ->{Pull} Maybe StringSource
Consumes an object’s separator, key, and colon and answers the key, or
consumes the closing brace and answers Nothing. first says whether no
member has been read yet. It honors a token already obtained by peek.
decodedString
decodedString : () ->{Pull} Result String StringSource
Consumes a scalar and answers it as a string, or a message after consuming a token of another type.
decodedInt
decodedInt : () ->{Pull} Result String IntSource
Consumes a scalar and answers it as an integer, or a message after consuming a token of another type or a number out of range.
decodedFloat
decodedFloat : () ->{Pull} Result String FloatSource
Consumes a scalar and answers it as a finite float, or a message after consuming a token of another type or a number out of range.
bufferedScan
bufferedScan : () ->{Pull} Maybe ScanSource
A speculative scan at the current position, or Nothing while a peek
token is pending. It consumes nothing, and abandoning it unrun consumes
nothing either.
currentScan
currentScan : () ->{Pull} ScanSource
A scan at the current position that also carries pending lookahead. It consumes nothing.
acceptScan
acceptScan : Scan ->{Pull} ()Source
Publishes the position a completed scan reached, consuming up to it. Call
it right away, before other parser operations. A suspension may already
have published a prefix; publishing the final position settles the
rest. The reader is committed at the next refill, at, or scope exit.
beginArray
beginArray : () ->{Pull} ()Source
Consumes an array’s opening bracket, failing if the next token is another.
beginObject
beginObject : () ->{Pull} ()Source
Consumes an object’s opening brace, failing if the next token is another.
decodeValue
decodeValue : Decode a => () ->{Pull} aSource
Decodes the next value as a: starts scanValue at the cursor, runs any
suspended work, and publishes the final position. Calling it for a child
runs that child’s one parser.
fail
fail : String ->{Pull} aSource
Fails with message at the current path, positioned at the start of the
token most recently returned by next or peek or consumed by a
decoded operation, or at the current position once the cursor moved on
another way.
import Bytes
import Json.Pull
import Reader
import Result exposing (Result(..))
failure : Result Json.Pull.Error Int
failure =
Reader.withBytes (Bytes.fromString "[true]") { reader ->
Json.Pull.withReader reader {
Json.Pull.beginArray ()
ignore (Json.Pull.peek ())
Json.Pull.fail "no booleans"
}
}
Result.mapError { e -> (e.message, e.offset) } failure == Err ("no booleans", 1)
mapScan
mapScan : (a -> b) -> Result (() ->{Pull} ScanStep a) (a, Scan) -> Result (() ->{Pull} ScanStep b) (b, Scan)Source
Transforms a scan’s value, buffered or resumed alike.
numberText
numberText : Number -> StringSource
The number’s lexeme as written.
readValue
readValue : () ->{Pull} ValueSource
Consumes the next value and builds its generic tree.
runScan
runScan : Result (() ->{Pull} ScanStep a) (a, Scan) ->{Pull} (a, Scan)Source
Runs suspended scan work until it answers a value and position, or fails with a JSON or source failure.
scanFromPull
scanFromPull : Scan -> (() ->{Pull} a) -> Result (() ->{Pull} ScanStep a) (a, Scan)Source
A deferred action runs only after the pure scan reaches a boundary or a token/custom decoder. It publishes that prefix and returns its new window.
skipValue
skipValue : () ->{Pull} ()Source
Consumes and validates the next value without building it.
import Bytes
import Json.Pull
import Reader
import Result exposing (Result(..))
rest =
Reader.withBytes (Bytes.fromString "[{\"a\": [1, 2]}, 3]") { reader ->
Json.Pull.withReader reader {
Json.Pull.beginArray ()
Json.Pull.skipValue ()
ignore (Json.Pull.nextElement False)
Json.Pull.next ()
}
}
rest == Ok (Json.Pull.NumberToken "3")
withIndex
withIndex : Int -> (() ->{Pull} a) ->{Pull} aSource
Runs action with element index added to the path of errors raised
inside it.
withKey
withKey : String -> (() ->{Pull} a) ->{Pull} aSource
Runs action with key added to the path of errors raised inside it.
withReader
withReader : Reader e -> (() ->{Pull} a) ->{e} Result Error aSource
Runs action with the Pull effect over a byte reader, decoding UTF-8.
On return, normally or with Err, the reader is positioned after the last
token scanned, peek lookahead included, as it is when an unrelated
effect leaves the scope; inside, it may lag behind, so advance it only
after the scope.
import Bytes
import Json.Pull
import Reader
import Result exposing (Result(..))
tokens = Reader.withBytes (Bytes.fromString "[1]") { reader -> Json.Pull.withReader reader { [Json.Pull.next (), Json.Pull.peek (), Json.Pull.next (), Json.Pull.next ()] } }
tokens == Ok [Json.Pull.BeginArray, Json.Pull.NumberToken "1", Json.Pull.NumberToken "1", Json.Pull.EndArray]
withTextReader
withTextReader : Reader e -> (() ->{Pull} a) ->{e} Result Error aSource
withReader over a text reader, whose encoding the caller chose.
