Files
2026-05-05 01:38:51 -05:00

214 lines
8.3 KiB
EmacsLisp
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// runtime/json.el El JSON operations
//
// Thin El wrappers over seed JSON primitives, plus pure-El builders and
// helpers. Each function here corresponds to (and replaces) a C function
// from el-compiler/runtime/legacy/el_runtime.c (lines 26923333).
//
// Seed primitives consumed by this module:
// __json_get(json, key) -> String (value as string)
// __json_get_raw(json, key) -> String (raw JSON token)
// __json_parse_map(s) -> Map<String, Any>
// __json_stringify_val(v) -> String
// __json_array_len(arr) -> Int
// __json_array_get(arr, i) -> String (element as JSON fragment)
// __json_array_get_string(arr, i) -> String (element as string value)
// __json_set(json, key, value) -> String (JSON mutation)
// __str_to_int(s) -> Int
// __str_to_float(s) -> Float
// ---------------------------------------------------------------------------
// Core thin wrappers that delegate directly to seed
// ---------------------------------------------------------------------------
// json_get extract a value from a JSON object as a string.
// Supports dot-path traversal ("a.b.c") and array indices ("items.0.name").
fn json_get(json: String, key: String) -> String {
return __json_get(json, key)
}
// json_get_raw extract a raw JSON token (the un-decoded fragment) for a key.
// Useful when the caller wants to pass a sub-object to another JSON function.
fn json_get_raw(json: String, key: String) -> String {
return __json_get_raw(json, key)
}
// json_parse parse a JSON string into a Map<String, Any>.
// Arrays become ElList; objects become ElMap; scalars are typed values.
fn json_parse(s: String) -> Map<String, Any> {
return __json_parse_map(s)
}
// json_stringify serialize an El value (ElMap, ElList, String, Int) to JSON.
fn json_stringify(v: Any) -> String {
return __json_stringify_val(v)
}
// json_array_len return the number of elements in a JSON array string.
fn json_array_len(arr: String) -> Int {
return __json_array_len(arr)
}
// json_array_get return the i-th element of a JSON array as a JSON fragment.
// Nested objects and arrays are returned verbatim. Out-of-range -> "".
fn json_array_get(arr: String, i: Int) -> String {
return __json_array_get(arr, i)
}
// json_array_get_string return the i-th element of a JSON array as a plain
// string value (quotes and escape sequences removed). Non-string elements
// and out-of-range indices yield "".
fn json_array_get_string(arr: String, i: Int) -> String {
return __json_array_get_string(arr, i)
}
// ---------------------------------------------------------------------------
// Typed extractors delegate to seed then convert
// ---------------------------------------------------------------------------
// json_get_string extract a string value for a key.
// Equivalent to json_get but named explicitly for readability.
fn json_get_string(json: String, key: String) -> String {
return __json_get(json, key)
}
// json_get_int extract an integer value for a key.
fn json_get_int(json: String, key: String) -> Int {
let s: String = __json_get(json, key)
return str_to_int(s)
}
// json_get_float extract a floating-point value for a key.
fn json_get_float(json: String, key: String) -> Float {
let s: String = __json_get(json, key)
return str_to_float(s)
}
// json_get_bool extract a boolean value for a key.
// Returns true only when the raw JSON token is the literal "true".
fn json_get_bool(json: String, key: String) -> Bool {
let s: String = __json_get(json, key)
return str_eq(s, "true")
}
// ---------------------------------------------------------------------------
// Mutation
// ---------------------------------------------------------------------------
// json_set set or insert a key/value pair in a JSON object string.
// If the key already exists its value is replaced in-place; otherwise the
// pair is appended before the closing brace. The value must already be a
// valid JSON-encoded string (e.g. a quoted string, number, or sub-object).
fn json_set(json: String, key: String, value: String) -> String {
return __json_set(json, key, value)
}
// ---------------------------------------------------------------------------
// Pure-El builders no seed call required
// ---------------------------------------------------------------------------
// json_build_object build a JSON object from alternating key/value strings.
//
// keys_and_values must contain an even number of elements laid out as:
// [key0, val0, key1, val1, ...]
//
// Both keys and values are assumed to be plain strings that will be
// double-quoted and JSON-escaped by this function. Pass a pre-encoded
// number or sub-object as the value if you need non-string JSON types.
//
// Example:
// json_build_object(["name", "alice", "role", "admin"])
// -> {"name":"alice","role":"admin"}
fn json_build_object(keys_and_values: [String]) -> String {
let n: Int = el_list_len(keys_and_values)
let result: String = "{"
let i: Int = 0
while i < n - 1 {
let key: String = el_list_get(keys_and_values, i)
let val: String = el_list_get(keys_and_values, i + 1)
let sep: String = if i == 0 { "" } else { "," }
let escaped_key: String = json_escape_string(key)
let escaped_val: String = json_escape_string(val)
let result = result + sep + "\"" + escaped_key + "\":\"" + escaped_val + "\""
let i = i + 2
}
return result + "}"
}
// json_build_array build a JSON array from a list of already-JSON-encoded
// strings.
//
// Each element in items must be a valid JSON fragment (quoted string, number,
// object, array, or literal). The function joins them with commas and wraps
// the result in brackets.
//
// Example:
// json_build_array(["\"alice\"", "\"bob\""])
// -> ["alice","bob"]
fn json_build_array(items: [String]) -> String {
let n: Int = el_list_len(items)
let result: String = "["
let i: Int = 0
while i < n {
let item: String = el_list_get(items, i)
let sep: String = if i == 0 { "" } else { "," }
let result = result + sep + item
let i = i + 1
}
return result + "]"
}
// json_array_push append a pre-encoded JSON element to a JSON array string.
// elem must be a valid JSON fragment (e.g. "\"foo\"" or "42").
// Returns a new JSON array string with elem appended.
// Example: json_array_push("[]", "\"alice\"") -> "[\"alice\"]"
fn json_array_push(arr: String, elem: String) -> String {
let n: Int = json_array_len(arr)
if n == 0 {
return "[" + elem + "]"
}
// arr ends with ']'; insert before it
let inner_end: Int = str_last_index_of(arr, "]")
if inner_end < 0 {
return "[" + elem + "]"
}
let prefix: String = str_slice(arr, 0, inner_end)
return prefix + "," + elem + "]"
}
// json_escape_string escape a raw string so it can be safely embedded as a
// JSON string value.
//
// Characters escaped: backslash, double-quote, newline, carriage return, tab.
// The returned value does NOT include surrounding double-quotes; wrap it in
// quotes if you need a complete JSON string literal.
fn json_escape_string(s: String) -> String {
let s1: String = str_replace(s, "\\", "\\\\")
let s2: String = str_replace(s1, "\"", "\\\"")
let s3: String = str_replace(s2, "\n", "\\n")
let s4: String = str_replace(s3, "\r", "\\r")
let s5: String = str_replace(s4, "\t", "\\t")
return s5
}
// ---------------------------------------------------------------------------
// DHARMA byte decoding
// ---------------------------------------------------------------------------
// bytes_to_str decode a JSON array of integer byte values back to a string.
// "[104,105]" -> "hi"
// Inverse of str_to_bytes (defined in string.el). Defined here because it
// depends on json_array_len and json_array_get_string which live in this file.
fn bytes_to_str(arr: String) -> String {
let n: Int = json_array_len(arr)
if n == 0 { return "" }
let out: String = __str_alloc(n)
let i: Int = 0
while i < n {
let elem: String = json_array_get_string(arr, i)
let b: Int = __str_to_int(elem)
out = __str_set_char(out, i, b)
i = i + 1
}
return out
}