16 Boken om Ridgun
Lib:en json — Rekursiv RFC 8259 AST-tolk, Pretty-Printer och Sökvägsfrågor
16.1. Varför finns lib:en json? (Det verkliga problemet och Ridguns filosofi)
”Varför behöver jag en
json-lib skriven från grunden i ren Ridgun i stället för att bara länka in ett gammalt C-bibliotek?”
Det är den första fråga varje systemingenjör ställer sig när man möter json. Och svaret sammanfattar hela filosofin bakom ridgun-libs:
Låt oss vara ärliga: att navigera i djupt nästlad JSON i många statiskt typade språk kräver sidor av boilerplate-kod eller tio kedjade .and_then() bara för att läsa ett portnummer.
Lib:en json erbjuder en komplett rekursiv RFC 8259-tolk till den algebraiska datatypen JsonVal (JnNull, JnBool, JnInt, JnStr, JnArr, JnObj, JnErr), kompakt och indenterad serialisering (json pretty) samt punktseparerade sökvägsfrågor ("server.http.port", "users.0.name").
I ett traditionellt ekosystem innebär uppgifter inom domänen JSON ofta att man drar in externa C-beroenden, brottas med råa pekare, ogenomskinliga typkonverteringar, okontrollerade undantag eller oförutsägbara skräpsamlarpauser. I Ridgun löser lib:en json (src/main/ridgun/lib/json.rg) problemet genom fyra kompromisslösa principer:
- Deklarera
import json(ellerimport json as alias) överst i filen: varje lib definierar sinmodule :: json-namnrymd och i--strict-läge anropas flerordsfunktioner alltid med naturliga mellanslag. - Kedja dina datapipelines från vänster till höger med
->: skrivindata steg ett -> steg två -> resultat, använd(+ _ _ +)-sekvensmasker för att packa upp tupler och utelämna_ =vid sekventiella satser (eller kasta med-> ()). - Svansrekursion med TCO (Tail-Call Optimization): alla interna genomlöpningar i
jsonkompileras till inbyggda hårdvaruloopar med $O(1)$ stackutrymme. - Deterministiskt minne (
RMM1/RMM2): varje mellanliggande struktur frigörs i exakt det ögonblick dess sista läsning avslutas, helt utan Garbage Collector.
16.2. Inbyggda artefakter, import och komplett API-katalog för json
I ridgun-libs är json en förstklassig lib placerad i src/main/ridgun/lib/json.rg (domän JSON). När du bygger med stable bundle paketeras den i tre inbyggda format:
| Artefakt | Sökväg | Syfte |
|---|---|---|
| Ridgun-bundle | target/bundles/json.rglib | Portabel distribution och direkt import med import json |
| Kompilerat arkiv | target/json.rgc | Typkontrollerad mellanartefakt verifierad av ridgunc |
| Delat ELF64-objekt | target/libjson.so | Inbyggt dynamiskt bibliotek helt utan C- eller libc-beroenden |
Varje lib deklarerar sin egen namnrymd med
module :: json: skriv baraimport json(ellerimport json as alias) överst och anropa dess funktioner med naturliga mellanslag (min funktion x) och->-pipelines (x min funktion -> resultat).
16.2.1. Översikt över funktionella familjer i json
| Signatur / Funktion / Typ | Vad den gör och när den används |
|---|---|
type JsonVal | `JnNull |
json parse / json is ok | Tolkar JSON-text till JsonVal och kontrollerar att JnErr inte uppstått |
json stringify / json pretty | Serialiserar JsonVal till kompakt eller flerradig indenterad JSON |
json get path / json get str / json get int / json get bool | Navigerar i nästlade objekt och listor via punktseparerade sökvägar ("a.b.0.c") |
16.2.2. Komplett katalog över typer och signaturer i src/main/ridgun/lib/json.rg
Nedan följer hela förteckningen över algebraiska datatyper (type), mixfix-operatorer (op ::) och funktionssignaturer (fn ::) som definieras i src/main/ridgun/lib/json.rg (7 typer, 0 operatorer och 118 funktioner):
Algebraiska datatyper och poster (type) i json:
type JsonField = JsonField String JsonVal
type JsonVal = JsonNull | JsonBool Bool | JsonInt Int | JsonStr String | JsonArr [JsonVal] | JsonObj [JsonField]
type JsonRes = JsonOk JsonVal | JsonErr String
type JsonStep = JStepOk JsonVal Int | JStepErr String
type JsonArrStep = JArrOk [JsonVal] Int | JArrErr String
type JsonObjStep = JObjOk [JsonField] Int | JObjErr String
type JsonStrStep = JStrOk String Int | JStrErr String
| # | Funktion / Signatur (fn ::) | Returtyp |
|---|---|---|
1 | fn :: j is ws Int | Bool |
2 | fn :: j is digit Int | Bool |
3 | fn :: j skip ws String Int Int | Int |
4 | fn :: j spaces Int | String |
5 | fn :: j escape loop String Int Int String | String |
6 | fn :: j escape str String | String |
7 | fn :: j unescape char Int | String |
8 | fn :: j parse str loop String Int Int String | JsonStrStep |
9 | fn :: j parse string String Int Int | JsonStrStep |
10 | fn :: j scan digits String Int Int | Int |
11 | fn :: j parse number String Int Int | JsonStep |
12 | fn :: j finish neg num String Int Int | JsonStep |
13 | fn :: j finish pos num String Int Int | JsonStep |
14 | fn :: j parse val String Int Int | JsonStep |
15 | fn :: j parse val at String Int Int | JsonStep |
16 | fn :: j wrap str step JsonStrStep | JsonStep |
17 | fn :: j parse arr open String Int Int | JsonStep |
18 | fn :: j parse arr first String Int Int | JsonStep |
19 | fn :: j finish arr step JsonArrStep | JsonStep |
20 | fn :: j parse arr items String Int Int [JsonVal] | JsonArrStep |
21 | fn :: j parse arr after item String Int [JsonVal] JsonStep | JsonArrStep |
22 | fn :: j parse arr delim String Int Int [JsonVal] | JsonArrStep |
23 | fn :: j parse obj open String Int Int | JsonStep |
24 | fn :: j parse obj first String Int Int | JsonStep |
25 | fn :: j finish obj step JsonObjStep | JsonStep |
26 | fn :: j parse obj fields String Int Int [JsonField] | JsonObjStep |
27 | fn :: j parse obj after key String Int [JsonField] JsonStrStep | JsonObjStep |
28 | fn :: j parse obj colon String Int Int String [JsonField] | JsonObjStep |
29 | fn :: j parse obj after val String Int String [JsonField] JsonStep | JsonObjStep |
30 | fn :: j parse obj delim String Int Int [JsonField] | JsonObjStep |
31 | fn :: j check eof String Int JsonStep | JsonRes |
32 | fn :: j check eof pos Int Int JsonVal | JsonRes |
33 | fn :: json parse String | JsonRes |
34 | fn :: j join arr [JsonVal] | String |
35 | fn :: j join obj [JsonField] | String |
36 | fn :: json stringify JsonVal | String |
37 | fn :: j pretty arr [JsonVal] Int Int | String |
38 | fn :: j pretty obj [JsonField] Int Int | String |
39 | fn :: j pretty val JsonVal Int Int | String |
40 | fn :: json pretty JsonVal Int | String |
41 | fn :: j find field [JsonField] String | JsonVal |
42 | fn :: json get field JsonVal String | JsonVal |
43 | fn :: j nth val [JsonVal] Int | JsonVal |
44 | fn :: json get index JsonVal Int | JsonVal |
45 | fn :: j split dots loop String Int Int Int [String] | [String] |
46 | fn :: j step path JsonVal String | JsonVal |
47 | fn :: j walk path JsonVal [String] | JsonVal |
48 | fn :: json get path JsonVal String | JsonVal |
49 | fn :: json get str JsonVal String String | String |
50 | fn :: j unwrap str JsonVal String | String |
51 | fn :: json get int JsonVal String Int | Int |
52 | fn :: j unwrap int JsonVal Int | Int |
53 | fn :: json get bool JsonVal String Bool | Bool |
54 | fn :: j unwrap bool JsonVal Bool | Bool |
55 | fn :: j put field [JsonField] String JsonVal | [JsonField] |
56 | fn :: json set field JsonVal String JsonVal | JsonVal |
16.3. Djupdykning i json: Steg-för-steg-exempel för varje funktion
Det räcker inte med en signaturtabell: låt oss utforska varje funktionalitet i lib:en json genom progressiva, körbara Ridgun-exempel. Vi börjar med två tabellbaserade vardagsrecept och går därefter igenom alla funktionella scenarier, transformationer och gränsfall som verifieras i src/test/ridgun/test json.rg.
16.3.1. Exempel 1: Huvudflöde i tabellform med json
Lägg märke till hur =-tecknen och transformationsstegen med json linjeras som en ren tabell utan onödiga muterbara variabler eller dolda sidoeffekter:
import json
fn :: leer config servidor JsonVal => String Int Bool
leer config servidor doc =
json get str doc "server.host" "127.0.0.1" -> host
json get int doc "server.port" 8080 -> port
json get bool doc "server.tls.enabled" False -> tls
(host, port, tls)
16.3.2. Exempel 2: Säker komposition och hantering av gränsfall
Varje funktion i lib:en json är utformad för att kombineras sömlöst med booleska vakter | och med övriga 32 libs i ridgun-libs:
import json
fn :: construir manifiesto => String
construir manifiesto =
JsonObj [JsonField "app" (JsonStr "solstice"), JsonField "workers" (JsonInt 8)] -> nodo
json pretty nodo 2
16.3.3. Exempel 3: AST-tolkning (json parse), kompakt serialisering (json stringify) och indenterad utskrift (json pretty)
I detta praktiska exempel undersöker vi grundligt hur json beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:
fn :: test json parse stringify and pretty => ()
test json parse stringify and pretty =
lb = char from code 123
rb = char from code 125
raw = lb + "\"user\":" + lb + "\"name\":\"Ada \\\"Lovelace\\\"\",\"age\":36,\"active\":true,\"roles\":[\"admin\",\"math\"],\"meta\":null" + rb + rb
res = json parse raw
assert true (is json ok res) "AC1: json parse nested object and array"
val = unwrap json res
compact = json stringify val
assert str eq compact raw "AC1: json stringify lossless round-trip"
pretty_s = json pretty val 2
res2 = json parse pretty_s
assert true (is json ok res2) "AC1: json pretty output parses back cleanly"
assert str eq (json stringify (unwrap json res2)) raw "AC1: json pretty round-trip equality"
()
16.3.4. Exempel 4: JSON-primitiver (null, bool, int, str), flyktsekvenser och tomma behållare ([], {})
I detta praktiska exempel undersöker vi grundligt hur json beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:
fn :: test json primitives escapes and empty containers => ()
test json primitives escapes and empty containers =
lb = char from code 123
rb = char from code 125
bs = char from code 92
nl = char from code 10
tab = char from code 9
assert str eq (json stringify (unwrap json (json parse (lb + rb)))) (lb + rb) "AC1: empty object {}"
assert str eq (json stringify (unwrap json (json parse "[]"))) "[]" "AC1: empty array []"
assert str eq (json stringify (unwrap json (json parse (" " + lb + " " + rb + " ")))) (lb + rb) "AC1: whitespace-padded empty object"
assert true (is json null (unwrap json (json parse "null"))) "AC1: root null literal"
assert str eq (json stringify (unwrap json (json parse "true"))) "true" "AC1: root true literal"
assert str eq (json stringify (unwrap json (json parse "false"))) "false" "AC1: root false literal"
assert str eq (json stringify (unwrap json (json parse "0"))) "0" "AC1: root zero integer"
assert str eq (json stringify (unwrap json (json parse "-42000"))) "-42000" "AC1: root negative integer"
esc_json = lb + "\"msg\":\"line1" + bs + "nline2" + bs + "t" + bs + bs + "slash\"" + rb
esc_val = unwrap json (json parse esc_json)
assert str eq (json get str esc_val "msg" "") ("line1" + nl + "line2" + tab + bs + "slash") "AC1: unescape \\n, \\t, \\\\ in JSON strings"
assert str eq (json stringify esc_val) esc_json "AC1: re-escape \\n, \\t, \\\\ on stringify"
()
16.3.5. Exempel 5: Djupa punktsökvägsfrågor (json query, json query str/int/bool) och oföränderlig uppdatering (json set field)
I detta praktiska exempel undersöker vi grundligt hur json beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:
fn :: test json dot path queries and updates => ()
test json dot path queries and updates =
lb = char from code 123
rb = char from code 125
raw = lb + "\"user\":" + lb + "\"name\":\"Ada\",\"age\":36,\"active\":true,\"roles\":[\"admin\",\"math\"],\"matrix\":[[10,20],[30,40]]" + rb + ",\"count\":-7,\"disabled\":false" + rb
val = unwrap json (json parse raw)
assert str eq (json get str val "user.name" "") "Ada" "AC2: json get str dot path"
assert int equals (json get int val "user.age" 0) 36 "AC2: json get int dot path"
assert int equals (json get int val "count" 0) (-7) "AC2: json get int negative"
assert true (json get bool val "user.active" False) "AC2: json get bool true"
assert false (json get bool val "disabled" True) "AC2: json get bool false"
assert str eq (json get str val "user.roles.0" "") "admin" "AC2: json get str array index 0"
assert str eq (json get str val "user.roles.1" "") "math" "AC2: json get str array index 1"
assert int equals (json get int val "user.matrix.1.0" 0) 30 "AC2: nested 2D array index user.matrix.1.0"
assert int equals (json get int val "user.matrix.1.1" 0) 40 "AC2: nested 2D array index user.matrix.1.1"
assert str eq (json get str val "user.roles.99" "oob") "oob" "AC2: out-of-bounds array index fallback"
assert str eq (json get str val "user.missing.deep" "fallback") "fallback" "AC2: missing deep dot-path fallback"
updated = json set field val "version" (JsonInt 2)
assert int equals (json get int updated "version" 0) 2 "AC2: json set field adds new key"
overwritten = json set field updated "count" (JsonInt 99)
assert int equals (json get int overwritten "count" 0) 99 "AC2: json set field overwrites existing key"
()
16.3.6. Exempel 6: Säker detektering av felformaterad JSON-indata (JsonErr) utan panik eller undantag
I detta praktiska exempel undersöker vi grundligt hur json beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:
fn :: test json malformed inputs => ()
test json malformed inputs =
lb = char from code 123
rb = char from code 125
assert false (is json ok (json parse "")) "AC3: reject empty input"
assert false (is json ok (json parse " ")) "AC3: reject whitespace-only input"
assert false (is json ok (json parse (lb + "\"unclosed\":1"))) "AC3: reject unclosed object"
assert false (is json ok (json parse "[1, 2, 3")) "AC3: reject unclosed array"
assert false (is json ok (json parse "\"unterminated string")) "AC3: reject unterminated string"
assert false (is json ok (json parse (lb + "unquoted: 1" + rb))) "AC3: reject unquoted object key"
assert false (is json ok (json parse (lb + "\"k\" 1" + rb))) "AC3: reject missing colon in object"
assert false (is json ok (json parse "[1, 2] trailing")) "AC3: reject trailing garbage token"
assert false (is json ok (json parse "tru")) "AC3: reject truncated boolean literal"
assert false (is json ok (json parse "nul")) "AC3: reject truncated null literal"
()
16.4. Under huven: hur json är byggd i ren Ridgun (src/main/ridgun/lib/json.rg)
En av de största fördelarna med ridgun-libs är att det inte finns några svarta lådor i C. Du kan öppna src/main/ridgun/lib/json.rg och läsa exakt hur varje operation är implementerad med mönstermatchning, | / |.-vaktträd och svansrekursion (TCO). Låt oss titta på nyckeldelarna i den faktiska implementationen:
16.4.1. Intern implementation: Whitespace & Character Helpers
Se hur json.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:
module :: json
fn :: j is ws Int => Bool
j is ws c = c == 32 OR c == 9 OR c == 10 OR c == 13
fn :: j is digit Int => Bool
j is digit c = c >= 48 AND c <= 57
fn :: j skip ws String Int Int => Int
j skip ws s pos len
| pos >= len = len
| j is ws (str char at s pos) = j skip ws s (pos + 1) len
| otherwise = pos
fn :: j spaces Int => String
j spaces n
| n <= 0 = ""
| otherwise = " " + (j spaces (n - 1))
16.4.2. Intern implementation: Integer Literal Parsing
Se hur json.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:
fn :: j scan digits String Int Int => Int
j scan digits s pos len
| pos >= len = len
| j is digit (str char at s pos) = j scan digits s (pos + 1) len
| otherwise = pos
fn :: j parse number String Int Int => JsonStep
j parse number s pos len
| (str char at s pos) == 45 = j finish neg num s pos (j scan digits s (pos + 1) len)
| otherwise = j finish pos num s pos (j scan digits s pos len)
fn :: j finish neg num String Int Int => JsonStep
j finish neg num s pos end_pos
| end_pos <= (pos + 1) = JStepErr "invalid negative number"
| otherwise = JStepOk (JsonInt (str to int (str slice s pos end_pos))) end_pos
fn :: j finish pos num String Int Int => JsonStep
j finish pos num s pos end_pos
| end_pos <= pos = JStepErr "expected number"
| otherwise = JStepOk (JsonInt (str to int (str slice s pos end_pos))) end_pos
16.4.3. Intern implementation: Serialization & Pretty-Printing
Se hur json.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:
fn :: j join arr loop [JsonVal] String String String String => String
j join arr loop [] c0 c1 c2 c3 = j sb finish c0 c1 c2 c3
j join arr loop (v:vs) c0 c1 c2 c3 =
n0, n1, n2, n3 = j sb append ("," + (json stringify v)) c0 c1 c2 c3
j join arr loop vs n0 n1 n2 n3
fn :: j join arr [JsonVal] => String
j join arr [] = ""
j join arr (v:vs) = j join arr loop vs (json stringify v) "" "" ""
fn :: j join obj loop [JsonField] String String String String => String
j join obj loop [] c0 c1 c2 c3 = j sb finish c0 c1 c2 c3
j join obj loop ((JsonField k v):rest) c0 c1 c2 c3 =
item = "," + (j escape str k) + ":" + (json stringify v)
n0, n1, n2, n3 = j sb append item c0 c1 c2 c3
j join obj loop rest n0 n1 n2 n3
fn :: j join obj [JsonField] => String
j join obj [] = ""
j join obj ((JsonField k v):rest) =
first = (j escape str k) + ":" + (json stringify v)
j join obj loop rest first "" "" ""
fn :: j pos int to str Int String => String
j pos int to str n acc =
| n <= 0 = acc
| otherwise = j pos int to str (n / 10) ((char from code (48 + (n % 10))) + acc)
fn :: j int to str Int => String
j int to str n =
| n == 0 = "0"
| n > 0 = j pos int to str n ""
| n > (0 - 10) = "-" + (char from code (48 + (0 - n)))
| otherwise = "-" + (j pos int to str (0 - (n / 10)) (char from code (48 + (0 - (n % 10)))))
fn :: json stringify JsonVal => String
json stringify JsonNull = "null"
json stringify (JsonBool True) = "true"
json stringify (JsonBool False) = "false"
json stringify (JsonInt n) = j int to str n
json stringify (JsonStr s) = j escape str s
json stringify (JsonArr items) = "[" + (j join arr items) + "]"
json stringify (JsonObj fields) = (char from code 123) + (j join obj fields) + (char from code 125)
fn :: j pretty arr loop [JsonVal] Int Int String String String String => String
j pretty arr loop [] _ _ c0 c1 c2 c3 = j sb finish c0 c1 c2 c3
j pretty arr loop (v:vs) ind cur c0 c1 c2 c3 =
item = "," + (char from code 10) + (j spaces (cur + ind)) + (j pretty val v ind (cur + ind))
n0, n1, n2, n3 = j sb append item c0 c1 c2 c3
j pretty arr loop vs ind cur n0 n1 n2 n3
fn :: j pretty arr [JsonVal] Int Int => String
j pretty arr [] _ _ = ""
j pretty arr (v:vs) ind cur =
first = (j spaces (cur + ind)) + (j pretty val v ind (cur + ind))
j pretty arr loop vs ind cur first "" "" ""
fn :: j pretty obj loop [JsonField] Int Int String String String String => String
j pretty obj loop [] _ _ c0 c1 c2 c3 = j sb finish c0 c1 c2 c3
j pretty obj loop ((JsonField k v):rest) ind cur c0 c1 c2 c3 =
item = "," + (char from code 10) + (j spaces (cur + ind)) + (j escape str k) + ": " + (j pretty val v ind (cur + ind))
n0, n1, n2, n3 = j sb append item c0 c1 c2 c3
j pretty obj loop rest ind cur n0 n1 n2 n3
fn :: j pretty obj [JsonField] Int Int => String
j pretty obj [] _ _ = ""
j pretty obj ((JsonField k v):rest) ind cur =
first = (j spaces (cur + ind)) + (j escape str k) + ": " + (j pretty val v ind (cur + ind))
j pretty obj loop rest ind cur first "" "" ""
fn :: j pretty val JsonVal Int Int => String
j pretty val (JsonArr []) _ _ = "[]"
j pretty val (JsonArr items) ind cur =
nl = char from code 10
"[" + nl + (j pretty arr items ind cur) + nl + (j spaces cur) + "]"
j pretty val (JsonObj []) _ _ = (char from code 123) + (char from code 125)
j pretty val (JsonObj fields) ind cur =
nl = char from code 10
(char from code 123) + nl + (j pretty obj fields ind cur) + nl + (j spaces cur) + (char from code 125)
j pretty val v _ _ = json stringify v
fn :: json pretty JsonVal Int => String
json pretty val ind = j pretty val val ind 0
16.4.4. Intern implementation: Dot-Path Queries & Field Updates
Se hur json.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:
fn :: j find field [JsonField] String => JsonVal
j find field [] _ = JsonNull
j find field ((JsonField k v):fs) key =
| k == key = v
| otherwise = j find field fs key
fn :: json get field JsonVal String => JsonVal
json get field (JsonObj fields) key = j find field fields key
json get field _ _ = JsonNull
fn :: j nth val [JsonVal] Int => JsonVal
j nth val [] _ = JsonNull
j nth val (v:vs) idx =
| idx < 0 = JsonNull
| idx == 0 = v
| otherwise = j nth val vs (idx - 1)
fn :: json get index JsonVal Int => JsonVal
json get index (JsonArr items) idx = j nth val items idx
json get index _ _ = JsonNull
fn :: j split dots loop String Int Int Int [String] => [String]
j split dots loop s pos len start acc =
| pos >= len = acc :: [str slice s start len]
| (str char at s pos) == 46 = j split dots loop s (pos + 1) len (pos + 1) (acc :: [str slice s start pos])
| otherwise = j split dots loop s (pos + 1) len start acc
fn :: j step path JsonVal String => JsonVal
j step path (JsonObj fields) seg = j find field fields seg
j step path (JsonArr items) seg =
| seg != "" and (j scan digits seg 0 (str len seg)) == (str len seg) and (j digits fit i64 seg False) = j nth val items (str to int seg)
| otherwise = JsonNull
j step path _ _ = JsonNull
fn :: j walk path JsonVal [String] => JsonVal
j walk path val [] = val
j walk path val ("":rest) = j walk path val rest
j walk path val (seg:rest) = j walk path (j step path val seg) rest
fn :: json get path JsonVal String => JsonVal
json get path val "" = val
json get path val dot_path =
j split dots loop dot_path 0 (str len dot_path) 0 [] -> segs
j walk path val segs
fn :: json get str JsonVal String String => String
json get str val dot_path fallback =
json get path val dot_path -> target
j unwrap str target fallback
fn :: j unwrap str JsonVal String => String
j unwrap str (JsonStr s) _ = s
j unwrap str _ fallback = fallback
fn :: json get int JsonVal String Int => Int
json get int val dot_path fallback =
json get path val dot_path -> target
j unwrap int target fallback
fn :: j unwrap int JsonVal Int => Int
j unwrap int (JsonInt n) _ = n
j unwrap int _ fallback = fallback
fn :: json get bool JsonVal String Bool => Bool
json get bool val dot_path fallback =
json get path val dot_path -> target
j unwrap bool target fallback
fn :: j unwrap bool JsonVal Bool => Bool
j unwrap bool (JsonBool b) _ = b
j unwrap bool _ fallback = fallback
fn :: j put field [JsonField] String JsonVal => [JsonField]
j put field [] key new_val = [JsonField key new_val]
j put field ((JsonField k v):fs) key new_val =
| k == key = [JsonField key new_val] :: fs
| otherwise = [JsonField k v] :: (j put field fs key new_val)
fn :: json set field JsonVal String JsonVal => JsonVal
json set field (JsonObj fields) key new_val = JsonObj (j put field fields key new_val)
json set field _ key new_val = JsonObj [JsonField key new_val]
16.5. Algoritmiska garantier, RMM1/RMM2-minne och de 5 Gyllene Reglerna för json
- Algoritmisk garanti och säkerhet: Strikt avvisande av skräptecken efter rotvärdet (
trailing garbage), fullständig hantering av escape-sekvenser och undantagsfri felrapportering medJnErr. - $O(1)$ stackkomplexitet med TCO: Alla rekursiva loopar i
src/main/ridgun/lib/json.rgligger i svansposition, såridgunckompilerar dem till inbyggda hopp utan stacktillväxt. - Deterministiskt minne (
RMM1/RMM2): Noll skräpsamlarpauser; alla temporära strukturer frigörs deterministiskt när räckvidden lämnas. - Kontinuerlig verifiering: Statiskt kontrollerad med
stable checkoch verifierad av den automatiserade testsvitensrc/test/ridgun/test json.rg.
De 5 Gyllene Reglerna när du programmerar med json
- Deklarera
import jsonöverst i filen och utnyttja att mellanslag och understreck är utbytbara så att koden läses som naturlig prosa. - Linjera dina datapipelines med
=: kedja sammanjson-anrop i namngivna mellansteg och låtstable formatlinjera kolumnerna. - Utnyttja
json:s totala funktioner i dina|-vakter: kontrollera returvärden eller packa upp ADT:er med mönstermatchning i stället för att anta att extern indata alltid är giltig. - Undvik att kopiera stora strukturer när du bara läser ett fält: kom ihåg att i
RMM2medför inargument noll referensräkningskostnad (+0/-0). - Kombinera
jsonmedtest(import test): lås alltid fast dina domäninvarianter medassert equalsellerassert true.
I Kapitel 17 går vi vidare och utforskar lib:en csv på djupet (Lib:en csv — RFC 4180 CSV/TSV-Tillståndsmaskin och Kolumnindexerade Tabeller).