1 # ezjson: JSON for Bend 2, with a parser, a printer and a pull cursor 2 # 3 # `parse` reads text into Maybe Json. `print` writes compact text and 4 # `pretty` indented text. `cursor` reads one event at a time. Build null, 5 # booleans, numbers, strings, arrays, and objects; read them with get, at, and 6 # as_bool, as_str, as_num, as_u32, as_f32; has and len tell a missing key or 7 # index from a null; items lists an array's elements, and is_arr and is_obj 8 # tell the containers apart. 9 import Base 10 import ./src/value.bend as Value 11 import ./src/parse.bend as Parse 12 import ./src/print.bend as Print 13 import ./src/pull.bend as Pull 14 import ./src/fixed.bend as Fixed 15 16 # a text as a value, or None when it is not JSON 17 def parse(txt: String) -> Maybe<&2, Value.Json>: 18 Parse.parse(txt) 19 20 # a value as compact JSON 21 def print(val: Value.Json) -> String: 22 Print.print(val) 23 24 # a value as JSON indented two spaces a level, one element or member a line, 25 # with no newline at the end 26 def pretty(val: Value.Json) -> String: 27 Print.pretty(val) 28 29 # null 30 def null() -> Value.Json: 31 Value.null() 32 33 # a boolean 34 def bool(flag: Bool) -> Value.Json: 35 Value.bool(flag) 36 37 # a string 38 def str(txt: String) -> Value.Json: 39 Value.str(txt) 40 41 # a JSON number from its text, or null when that text is not one 42 def num(raw: String) -> Value.Json: 43 Value.num(raw) 44 45 # an object from pairs 46 def obj(pairs: List<&1, String & Value.Json>) -> Value.Json: 47 Value.obj(pairs) 48 49 # an array from a list 50 def arr(items: List<&2, Value.Json>) -> Value.Json: 51 Value.arr(items) 52 53 # an object's value at a key; JNull when it is not an object or has no such 54 # key 55 def get(val: Value.Json, key: String) -> Value.Json: 56 Value.get(val, key) 57 58 # an array's item at an index; JNull when it is not an array or has none 59 def at(val: Value.Json, idx: U32) -> Value.Json: 60 Value.at(val, idx) 61 62 # whether an object has a key; false when the value is not an object. Tells 63 # a missing key from a key whose value is null, which `get` does not 64 def has(val: Value.Json, key: String) -> Bool: 65 Value.has(val, key) 66 67 # how many elements an array, or members an object, has; 0 for anything else 68 def len(val: Value.Json) -> U32: 69 Value.len(val) 70 71 # an array's elements, in order; [] when the value is not an array. One 72 # walk of the array, where a loop over `at` walks it again for every index 73 def items(val: Value.Json) -> List<&2, Value.Json>: 74 Value.items(val) 75 76 # whether a value is an array 77 def is_arr(val: Value.Json) -> Bool: 78 Value.is_arr(val) 79 80 # whether a value is an object 81 def is_obj(val: Value.Json) -> Bool: 82 Value.is_obj(val) 83 84 # a boolean, or none when the value is not one 85 def as_bool(val: Value.Json) -> Maybe<&2, Bool>: 86 Value.as_bool(val) 87 88 # a string, or none when the value is not one 89 def as_str(val: Value.Json) -> Maybe<&2, String>: 90 Value.as_str(val) 91 92 # a number's text, or none when the value is not a number 93 def as_num(val: Value.Json) -> Maybe<&2, String>: 94 Value.as_num(val) 95 96 # a number's text written with `places` decimals, rounded half to even on 97 # its decimal digits (not through a binary float), as Python's `'%.Nf'` 98 # writes a value whose decimal text is exact; None when the text is not a 99 # JSON number. A `-` is kept even when the result is zero: `-0.04` to one 100 # place is `-0.0` 101 def num.fixed(raw: String, places: Nat) -> Maybe<&2, String>: 102 Fixed.fixed(raw, places) 103 104 # a number as a U32, or none when it is not one or does not fit 105 def as_u32(val: Value.Json) -> Maybe<&2, U32>: 106 Value.as_u32(val) 107 108 # a number as an F32, or none when it is not one or does not fit 109 def as_f32(val: Value.Json) -> Maybe<&2, F32>: 110 Value.as_f32(val) 111 112 # a cursor at the start of a JSON text. It holds the unread suffix of that 113 # text. A span event keeps its own suffix, so it stays valid after `next` 114 # moves the cursor, and it is gone when the event is dropped 115 def cursor(src: String) -> Pull.Cur: 116 Pull.cursor(src) 117 118 # the next event. Commas and colons are not events. After the root value, 119 # the next call is the end, or an error if another value follows 120 def next(cur: Pull.Cur) -> (Pull.Ev & Pull.Cur): 121 Pull.next(cur) 122 123 # drop the next value, scalar or container, without building it 124 def skip(cur: Pull.Cur) -> Pull.Cur: 125 Pull.skip(cur) 126 127 # the text of a string, key, or number event, copying a span. None otherwise 128 def text(ev: Pull.Ev) -> Maybe<&2, String>: 129 Pull.text(ev)