The Machines · Runtime stack

seedjson

A reckoner seed: json's parse and write, an object as a dict — machines/seeds/seedjson.shoddy

the seedjson machine's icon

Summary

seedjson bridges json. Per R4.16 there is no DOCUMENT cell at the keyboard. A parsed value converts to the same shapes every other seed already uses:

A parsed object is a dict the moment it lands on the stack. DGET, DHAS and DKEYS work on it unchanged.

The one gap: JSON null and an empty array both convert to the same empty LIST. So both write back out as []. JSON's own type tells them apart. Nothing on this stack does.

Why It's Useful

The guard this seed adds is not the parse — it is everything around it. json.shoddy's own reader already answers a Result (a value that carries either an answer or an error) rather than aborting on bad syntax. So the file work is where the care goes. A path that is not there, a directory handed where a file was meant, an unwritable destination: each of those is a refusal or an honest False. That is because JSONLOAD and JSONSAVE go through TRYREADFILE and TRYWRITEFILE rather than the whole-file builtins that abort.

The bigger thing it buys: a parsed object needs no words of its own. An object lands in the same LIST-of-PAIR shape seeddict established, so the dictionary words a session already knows can read it. DGET pulls a field. DKEYS lists them. MAP and FILTER walk an array. A seed that invented JSONGET would have added a second spelling of a word that was already there.

User's Guide

An array is a LIST. So the combinators a session already has — words like MAP that run a small program over each item — need no help from this seed:

> "[1,2,3,4]" JSONPARSE [ DUP * ] MAP JSONTEXT
x: "[1,4,9,16]"

An object has to come from a file, not from the entry line. The reckoner's tokenizer — the part that splits a typed line into words — has no escape inside a string literal. So there is no way to type a " within one, and every key in a JSON object is quoted. JSONLOAD is therefore the way an object reaches the stack. From there it is an ordinary dict:

> "mill.json" JSONLOAD "D" STO
[ empty ]
> "D" RCL "looms" DGET
x: 12
> "D" RCL DKEYS
x: { "name" "looms" }
> "D" RCL 2 JSONPRETTY "out.json" SWAP JSONSAVE
x: True

The two refusals below come from different places, and each says so. The first is json.shoddy's own reader reporting where the syntax went wrong. The second is the guarded read:

> "{oops" JSONPARSE
?: JSONPARSE: EXPECTED A QUOTED KEY IN AN OBJECT AT 2
> "no/such/file.json" JSONLOAD
?: JSONLOAD: cannot read the file

Word Reference

WordDescription
JSONPARSE ( str -- doc )The string, parsed as JSON — objects become dicts, arrays become lists.
JSONTEXT ( doc -- str )The value, written out as compact JSON text.
JSONPRETTY ( doc n -- str )The value, written out as JSON text indented n spaces per level.
JSONLOAD ( path -- doc )The file, read and parsed as JSON — a refusal names why it could not be, whether that is the file or the syntax.
JSONSAVE ( path doc -- ok )Write the value to path as JSON text; True on success.

Who Uses It

UserHow
halifaxThe calculator's JSON words: JSONPARSE, JSONTEXT, JSONPRETTY, JSONLOAD and JSONSAVE.
sparkySparky folds it too, so a model calling eval reaches the same words halifax puts at a prompt.

A mill claims this seed by folding RckSeedJson over its reckoner state, which is all halifax does.

The Machines It Uses

MachineWhy
cuttleThe Cell type every bridged word reads its arguments from and answers into.
jsonThe domain this seed bridges: JsonRead, JsonText, JsonPretty.
reckonerRckReg, RckSeeding and the argument readers every registered word is built from.
seqList plumbing under the JSON↔Cell converters.