A reckoner seed: json's parse and write, an object as a dict — machines/seeds/seedjson.shoddy
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.
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.
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 | Description |
|---|---|
| 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. |
| User | How | |
|---|---|---|
| halifax | The calculator's JSON words: JSONPARSE, JSONTEXT, JSONPRETTY, JSONLOAD and JSONSAVE. | |
| sparky | Sparky 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.
| Machine | Why | |
|---|---|---|
| cuttle | The Cell type every bridged word reads its arguments from and answers into. | |
| json | The domain this seed bridges: JsonRead, JsonText, JsonPretty. | |
| reckoner | RckReg, RckSeeding and the argument readers every registered word is built from. | |
| seq | List plumbing under the JSON↔Cell converters. |