sysl

Module

sysl.encoding.json

JSON, read and written: a document parsed into a value tree that owns itself, and a value tree written back out compactly or for a person to read.

Import import sysl.encoding.json

A document keeps the order it was written in. An object’s members are a sequence rather than a map, so {"b":1,"a":2} writes back the way it arrived, and a name written twice keeps both members. That is the only representation that round-trips, and it is the right trade at the sizes JSON is used at – a configuration file, a request body, a protocol message.

A malformed document is refused with the place it went wrong: a JsonError carries the line, the column and the byte offset, and what was wrong there, so a message can point at it.

Nothing here opens or reads anything: parse takes text the caller already has, and writing goes into any Writer. It needs a heap and nothing else, so it builds for a board that has one.

Index

max_depth parse parse_bytes pretty quoted write_pretty Fault Json JsonError Member Display for Fault Display for Json Display for JsonError Eq for Json Eq for Member

Constants

max_depth

const max_depth: usize = 128

How deep arrays and objects may nest before a document is refused.

The reader is recursive, so without a bound a hostile document of a hundred thousand [ would exhaust the stack. 128 is far past what a real document nests and small enough for a board’s stack.

Functions

parse

parse(text: string) -> Result[Json, JsonError]

One whole JSON text read into a value, or why it is not one.

Whitespace may surround the value; anything else after it is refused. A number too large for a real reads as an infinity.

parse_bytes

parse_bytes(b: []const u8) -> Result[Json, JsonError]

The same over bytes that may not be text, which is what arrives from a socket or a file: a string holding bytes that are not UTF-8 is refused with NotUtf8 rather than taken.

pretty

pretty(j: Json, step: usize) -> string

The value as JSON a person reads: one element or member to a line, indented by step spaces per level, with a space after every colon. An empty array or object stays [] or {}.

quoted

quoted(s: string) -> string

A string as a JSON string literal, quotes and escapes included – for a program that assembles JSON by hand. The quote, the backslash and every byte below a space are escaped; everything else stands for itself, since JSON text is UTF-8.

write_pretty

write_pretty(j: Json, step: usize, out: *Writer)

pretty‘s rendering, written into a sink rather than gathered into a string.

Types

Fault

enum Fault
    Expected(what: string)
    Ended(what: string)
    Trailing
    LeadingZero
    BadEscape
    BadHex
    LoneSurrogate
    Control
    Unclosed
    NotUtf8
    TooDeep

What was wrong at the place a JsonError names.

Json

enum Json
    Null
    Bool(b: bool)
    Int(n: long)
    Big(digits: string)
    Real(x: real)
    Str(s: string)
    Arr(items: Buf[Json])
    Obj(members: Buf[Member])

A JSON value: one of the things the grammar can produce.

Numbers are three variants rather than one, so that an integer stays an integer. JSON’s grammar has a single numeric production and says nothing about how it is stored. Carrying every number as a real loses the exact value of an integer past 2^53, which is where an identifier or a timestamp in milliseconds lives; carrying every number as a long cannot hold 0.5. So:

  • Int is a number written with no fraction and no exponent that fits a long;
  • Real is a number written with a fraction or an exponent – 1.0 is a Real, not an Int;
  • Big is a whole number past a long in either direction, carried as the digits the document wrote (sign included), so it round-trips exactly. sysl.math.bigint.parse_radix(d, 10) computes with one.

A caller that does not care which asks as_real.

The tree owns itself. An array holds a Buf[Json] and an object a Buf[Member], with no reference anywhere, so every walk over a document is a plain match.

MemberSignatureDescription
is_nullis_null(self) -> boolWhether this is null – which is a value here, not the absence of one.
as_boolas_bool(self) -> Option[bool]The boolean this is, or None for every other shape.
as_intas_int(self) -> Option[long]The integer this is, only where the document wrote one that fits a long.
as_realas_real(self) -> Option[real]The number this is, whichever variant carries it – the one accessor that widens.
as_stras_str(self) -> Option[string]
lenlen(self) -> usizeHow many elements an array has, or how many members an object has.
atat(self, i: usize) -> Option[Json]The element at a position, for an array; None past the end and for anything else.
membermember(self, i: usize) -> Option[Member]The member at a position, for an object, in the order the document wrote them – how an object is walked when its names are not known in advance.
getget(self, key: string) -> Option[Json]The value written under a name, or None where there is none.

JsonError

struct JsonError
    fault: Fault
    line: usize
    col: usize
    offset: usize

Why a document was refused, and where.

line and col count from one; a column counts characters, not bytes, so it is what an editor shows. offset is the byte offset from the start of the text, counting from zero.

Member

struct Member
    key: string
    value: Json

One member of an object: the name it was written under, and what was written there.

Implementations

Display for Fault

impl Display for Fault

Display for Json

impl Display for Json

The value as JSON, compactly. Rendering goes straight into the sink, so print(j) writes a large document with nothing assembled in the middle.

Display for JsonError

impl Display for JsonError

line:col: what, the shape a compiler’s diagnostic has.

Eq for Json

impl Eq for Json

Two values are equal when they are the same shape holding equal things.

The numeric variants do not meet: Int(1) and Real(1.0) are two different texts, and this equality is about two documents saying the same thing. Objects compare in order, for the same reason.

Eq for Member

impl Eq for Member

Two members are equal when they were written under the same name and hold equal values.

Search

Esc
↑↓ to navigate ↵ to open Esc to close