Joan edukira
Ridgun
Euskara

16 Ridgun Liburua

json lib-a — RFC 8259 AST Zuhaitz Errekurtsiboa, Pretty-Printer eta Bide-kontsultak

16.1. Zergatik existitzen da json lib-a? (Benetako arazoa eta Ridgun filosofia)

«Zergatik behar dut json lib bat hutsetik Ridgun hutsean idatzia, betiko C liburutegi bat estekatu beharrean?»

Hori da edozein sistema-ingeniarik json-era iristean egiten duen lehen galdera. Eta erantzunak ridgun-libs-en filosofia osoa laburbiltzen du:

Izan gaitezen zintzoak: lengoaia estatiko askotan JSON habiaratu batean nabigatzeak hiru pantaila kode errepikakor idaztea edo hamar .and_then() kateatzea eskatzen du ataka soil bat irakurri aurretik.

json lib-ak RFC 8259-rako jaitsiera errekurtsiboko analizatzaile oso bat eskaintzen du JsonVal ADT aljebraikoaren gainean (JnNull, JnBool, JnInt, JnStr, JnArr, JnObj, JnErr), serializazio trinkoa zein koskaduna (json pretty) eta puntu bidezko bide-kontsultak ("server.http.port").

Ekosistema tradizional batean, JSON domeinuko zereginak ebazteak C-ko kanpo-mendekotasunak arrastatzea, erakusle gordinekin borrokatzea, mota-bihurketa opakuak, egiaztatu gabeko salbuespenak edo zabor-biltzailearen geldialdi ustekabekoak jasatea dakar. Ridgunen, json lib-ak (src/main/ridgun/lib/json.rg) lau printzipio negoziatu ezinekin heltzen dio arazoari:

  1. Deklaratu import json (edo import json as alias) fitxategiaren hasieran: lib bakoitzak module :: json izen-espazioa definitzen du eta --strict moduan hitz anitzeko funtzioei zuriune naturalekin deitzen zaie beti.
  2. Kateatu zure datu-hodiak ezkerretik eskuinera -> bidez: idatzi sarrera lehen urratsa -> bigarren urratsa -> emaitza, erabili (+ _ _ +) maskarak sekuentziak desegituratzeko eta kendu _ = sententzia sekuentzialetan (edo baztertu -> () bidez).
  3. Isats-errekurtsioa TCOrekin (Tail-Call Optimization): json-eko barne-ibilbide guztiak $O(1)$ pila-espazioko begizta natiboetara konpilatzen dira.
  4. Memoria determinista (RMM1 / RMM2): tarteko egitura bakoitza bere azken irakurketa amaitzen den une zehatzean askatzen da, Garbage Collector gabe.

16.2. Artefaktu natiboak, inportazioa eta json APIaren katalogo osoa

ridgun-libs-en, json lehen mailako lib bat da, src/main/ridgun/lib/json.rg bidean kokatua (JSON domeinua). stable bundle-ekin konpilatzean, hiru formatu natibotan paketatzen da:

ArtefaktuaBideaHelburua
Ridgun Bundle-atarget/bundles/json.rglibBanaketa eta inportazio zuzena import json bidez
Fitxategi konpilatuatarget/json.rgcridgunc-ek egiaztatutako tarteko artefaktua
ELF64 objektu partekatuatarget/libjson.soC edo libc mendekotasunik gabeko liburutegi dinamiko natiboa

Lib bakoitzak bere izen-espazioa deklaratzen du module :: json bidez: nahikoa da goiburuan import json (edo import json as alias) idaztea eta bere funtzioei zuriune naturalekin (nire funtzioa x) eta -> hodiekin (x nire funtzioa -> emaitza) deitzea.

16.2.1. json-eko familia funtzionalen laburpena

Sinadura / Funtzioa / MotaZer egiten duen eta noiz erabili
type JsonVal`JnNull
json parse / json is okJSON testua JsonVal zuhaitzean analizatzen du eta JnErr ez dagoela egiaztatu
json stringify / json prettyJsonVal JSON trinko edo lerro anitzeko koskadunera serializatzen du
json get path / json get str / json get int / json get boolObjektu eta zerrenda habiaratuak zeharkatzen ditu puntudun bideekin ("a.b.0.c")

16.2.2. src/main/ridgun/lib/json.rg-eko mota eta sinadura guztien katalogoa

Jarraian duzu src/main/ridgun/lib/json.rg fitxategian definitutako mota aljebraiko (type), eragile (op ::) eta funtzio-sinadura (fn ::) guztien inbentario osoa (7 mota, 0 eragile eta 118 funtzio):

json-eko mota aljebraikoak eta erregistroak (type):

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
#Funtzioa / Sinadura (fn ::)Itzulera-mota
1fn :: j is ws IntBool
2fn :: j is digit IntBool
3fn :: j skip ws String Int IntInt
4fn :: j spaces IntString
5fn :: j escape loop String Int Int StringString
6fn :: j escape str StringString
7fn :: j unescape char IntString
8fn :: j parse str loop String Int Int StringJsonStrStep
9fn :: j parse string String Int IntJsonStrStep
10fn :: j scan digits String Int IntInt
11fn :: j parse number String Int IntJsonStep
12fn :: j finish neg num String Int IntJsonStep
13fn :: j finish pos num String Int IntJsonStep
14fn :: j parse val String Int IntJsonStep
15fn :: j parse val at String Int IntJsonStep
16fn :: j wrap str step JsonStrStepJsonStep
17fn :: j parse arr open String Int IntJsonStep
18fn :: j parse arr first String Int IntJsonStep
19fn :: j finish arr step JsonArrStepJsonStep
20fn :: j parse arr items String Int Int [JsonVal]JsonArrStep
21fn :: j parse arr after item String Int [JsonVal] JsonStepJsonArrStep
22fn :: j parse arr delim String Int Int [JsonVal]JsonArrStep
23fn :: j parse obj open String Int IntJsonStep
24fn :: j parse obj first String Int IntJsonStep
25fn :: j finish obj step JsonObjStepJsonStep
26fn :: j parse obj fields String Int Int [JsonField]JsonObjStep
27fn :: j parse obj after key String Int [JsonField] JsonStrStepJsonObjStep
28fn :: j parse obj colon String Int Int String [JsonField]JsonObjStep
29fn :: j parse obj after val String Int String [JsonField] JsonStepJsonObjStep
30fn :: j parse obj delim String Int Int [JsonField]JsonObjStep
31fn :: j check eof String Int JsonStepJsonRes
32fn :: j check eof pos Int Int JsonValJsonRes
33fn :: json parse StringJsonRes
34fn :: j join arr [JsonVal]String
35fn :: j join obj [JsonField]String
36fn :: json stringify JsonValString
37fn :: j pretty arr [JsonVal] Int IntString
38fn :: j pretty obj [JsonField] Int IntString
39fn :: j pretty val JsonVal Int IntString
40fn :: json pretty JsonVal IntString
41fn :: j find field [JsonField] StringJsonVal
42fn :: json get field JsonVal StringJsonVal
43fn :: j nth val [JsonVal] IntJsonVal
44fn :: json get index JsonVal IntJsonVal
45fn :: j split dots loop String Int Int Int [String][String]
46fn :: j step path JsonVal StringJsonVal
47fn :: j walk path JsonVal [String]JsonVal
48fn :: json get path JsonVal StringJsonVal
49fn :: json get str JsonVal String StringString
50fn :: j unwrap str JsonVal StringString
51fn :: json get int JsonVal String IntInt
52fn :: j unwrap int JsonVal IntInt
53fn :: json get bool JsonVal String BoolBool
54fn :: j unwrap bool JsonVal BoolBool
55fn :: j put field [JsonField] String JsonVal[JsonField]
56fn :: json set field JsonVal String JsonValJsonVal

16.3. json sakonki esploratzen: Funtzionalitate bakoitzaren urratsez urratseko adibideak

Ez da nahikoa sinadura-taula ikustea: json lib-aren funtzionalitate bakoitza Ridgunen exekutagarriak diren adibide progresiboen bidez esploratuko dugu. Eguneroko bi errezeta taularatuekin hasiko gara eta ondoren xehetasunez ikusiko ditugu src/test/ridgun/test json.rg-en egiaztatutako agertoki funtzional eta muga-kasu guztiak.

16.3.1. 1. adibidea: Fluxu nagusia estilo taularatuan json erabiliz

Ikusi nola Ridgunen = ikurrak eta json bidezko eraldaketa-urratsak taula garbi bat bezala lerrokatuta geratzen diren, beharrezkoak ez diren aldagai aldakorrik edo ezkutuko albo-ondoriorik gabe:

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. 2. adibidea: Konposizio segurua eta muga-kasuen kudeaketa

json lib-eko funtzio oro | guarda boolearrekin eta ridgun-libs-eko gainerako 32 lib-ekin konposatzeko diseinatuta dago:

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. 3. adibidea: Analisi sintaktikoa (json parse), serializazio trinkoa (json stringify) eta inprimaketa koskaduna (json pretty)

Adibide praktiko honetan json bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 4. adibidea: JSON primitibak (null, bool, int, str), ihes-sekuentziak eta edukiontzi hutsak ([], {})

Adibide praktiko honetan json bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 5. adibidea: Puntuzko bide-kontsulta sakonak (json query, json query str/int/bool) eta eguneratze funtzionala (json set field)

Adibide praktiko honetan json bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 6. adibidea: Gaizki eratutako JSON sarreren detekzio segurua (JsonErr) izurik eta salbuespenik gabe

Adibide praktiko honetan json bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. Kaputaren azpian: nola dagoen eraikia json Ridgun hutsean (src/main/ridgun/lib/json.rg)

ridgun-libs-en abantaila handienetako bat C-ko kutxa beltzik ez egotea da. src/main/ridgun/lib/json.rg ireki dezakezu eta eragiketa bakoitza pattern matching, | / |. guarda-zuhaitzak eta isats-errekurtsioa (TCO) erabiliz nola inplementatuta dagoen irakurri. Ikus ditzagun bere benetako inplementazioaren funtsezko piezak:

16.4.1. Barne-inplementazioa: Whitespace & Character Helpers

Ikusi nola json.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Barne-inplementazioa: Integer Literal Parsing

Ikusi nola json.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Barne-inplementazioa: Serialization & Pretty-Printing

Ikusi nola json.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Barne-inplementazioa: Dot-Path Queries & Field Updates

Ikusi nola json.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Berme algoritmikoak, RMM1/RMM2 memoria eta json-en Urrezko 5 Arauak

  • Berme algoritmikoa eta segurtasunekoa: Erro-balioaren ondorengo zaborraren (trailing garbage) ukapen zorrotza, ihes-sekuentzien kudeaketa osoa eta salbuespenik gabeko JnErr itzulera segurua.
  • $O(1)$ pila-konplexutasuna TCOrekin: src/main/ridgun/lib/json.rg-eko begizta errekurtsibo guztiak isats-posizioan daude; beraz, ridgunc-ek pila-hazkuntzarik gabeko jauzi natiboetara konpilatzen ditu.
  • Memoria determinista (RMM1/RMM2): Zero zabor-biltzaile etenaldi; tarteko egitura guztiak modu deterministan askatzen dira esparrutik irtetean.
  • Etengabeko egiaztapena: Zorrotz probatua stable check eta src/test/ridgun/test json.rg suite automatizatuaren bidez.

json-ekin programatzeko Urrezko 5 Arauak

  1. Deklaratu import json fitxategiaren hasieran eta baliatu zuriuneen eta azpimarren arteko baliokidetasuna zure kodea prosa bezala irakur dadin.
  2. Lerrokatu zure datu-hodiak = ikurrarekin: kateatu json-erako deiak izen propioko urratsetan eta utzi stable format-i zutabeak lerrokatzen.
  3. Baliatu json-eko funtzio osoak zure | guardetan: egiaztatu itzulera-balioak edo askatu ADTak pattern matching bidez, kanpoko sarrera beti baliozkoa dela suposatu beharrean.
  4. Saihestu egitura handiak kopiatzea eremu bat kontsultatu besterik behar ez baduzu: gogoratu RMM2-n sarrerako argumentuek ez dituztela kontagailuak handitzen edo txikitzen (+0 / -0).
  5. Konbinatu json eta test (import test): idatzi beti asertzioak assert equals edo assert true erabiliz zure domeinuaren inbarianteak finkatzeko.

17. kapituluan csv lib-a sakonki esploratuko dugu (csv lib-a — CSV/TSV-rako RFC 4180 Egoera-analizatzailea eta Taula Indexatuak).