Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
json.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file json.c
12 * @brief json-c helpers for Fun VM JSON-related opcodes (conditional build).
13 *
14 * This module centralizes small utilities that convert between json-c objects
15 * and the Fun VM's Value type. Keeping the concrete conversion logic in
16 * src/extensions/ allows VM opcode implementations to remain minimal — they
17 * focus on VM stack marshalling and delegate the heavy lifting here, mirroring
18 * other extensions (PCRE2, SQLite, XML2, INI).
19 *
20 * Build-time feature flag:
21 * - All code in this file is compiled only when FUN_WITH_JSON is enabled. When
22 * disabled, JSON-related opcodes should provide safe no-op fallbacks in
23 * their respective VM files.
24 *
25 * Type mapping between json-c and Fun:
26 * - json null -> Fun Nil
27 * - json boolean -> Fun Bool
28 * - json number -> Fun Int (64-bit) or Fun Float (double) depending on
29 * the underlying json-c representation
30 * - json string -> Fun String (assumed UTF-8 from json-c)
31 * - json array -> Fun Array (elements converted recursively)
32 * - json object -> Fun Map<string, any> (values converted recursively)
33 *
34 * Ownership and memory:
35 * - The conversion functions allocate new Fun Values and/or new json-c trees.
36 * The caller owns the returned result and is responsible for releasing it
37 * (free_value for Fun Values, json_object_put for json-c objects) when no
38 * longer needed.
39 * - No global state is retained; all allocations are tied to the returned
40 * objects.
41 *
42 * Encoding and limits:
43 * - json-c strings are treated as UTF-8; the Fun VM strings are expected to be
44 * UTF-8 as well. No transcoding is performed.
45 * - Deeply nested inputs convert recursively; extremely deep trees may exhaust
46 * the C stack. Cycles are not possible in well-formed JSON; if presented via
47 * custom json_object graphs, behaviour is undefined (may loop or duplicate).
48 *
49 * Thread-safety:
50 * - Not intrinsically thread-safe or unsafe. The helpers are stateless; each
51 * invocation operates on its arguments only. Coordinate external access to
52 * shared json_object instances if they are mutated concurrently.
53 */
54
55#ifdef FUN_WITH_JSON
56//#include "value.h"
57//#include "vm.h"
58
59#include <json-c/json.h>
60//#include <string.h>
61
62/**
63 * @brief Convert a json-c object tree into a Fun Value.
64 *
65 * Mapping rules:
66 * - null -> Nil
67 * - boolean -> Bool
68 * - number (int/double) -> Int/Float (uses json_object_get_int64/json_object_get_double)
69 * - string -> String (assumes UTF-8, no transcoding)
70 * - array -> Array (elements converted recursively, preserving order)
71 * - object -> Map<string, any> (values converted recursively; keys are copied as-is)
72 *
73 * Error handling and ownership:
74 * - If j is NULL, returns Nil.
75 * - The returned Value is newly created and owned by the caller, who must
76 * dispose it with free_value() when done. Internally allocated temporaries
77 * are released before returning.
78 *
79 * Notes:
80 * - json-c may store numbers as double even when they look integral; such
81 * values will end up as Float in Fun.
82 * - Deep or large JSON inputs are traversed recursively; excessive depth could
83 * lead to stack pressure.
84 *
85 * @param j Pointer to a json_object (may be NULL).
86 * @return Value Converted Value (Nil on NULL input or on unsupported types).
87 */
88static Value json_to_fun(json_object *j) {
89 if (!j) return make_nil();
90 enum json_type t = json_object_get_type(j);
91 switch (t) {
92 case json_type_null:
93 return make_nil();
94 case json_type_boolean:
95 return make_bool(json_object_get_boolean(j));
96 case json_type_double:
97 return make_float(json_object_get_double(j));
98 case json_type_int:
99 return make_int((int64_t)json_object_get_int64(j));
100 case json_type_string:
101 return make_string(json_object_get_string(j));
102 case json_type_array: {
103 size_t n = json_object_array_length(j);
104 if (n == 0) {
105 return make_array_from_values(NULL, 0);
106 }
107 Value *vals = (Value *)malloc(sizeof(Value) * n);
108 if (!vals) return make_array_from_values(NULL, 0);
109 for (size_t i = 0; i < n; ++i) {
110 json_object *item = json_object_array_get_idx(j, (int)i);
111 vals[i] = json_to_fun(item);
112 }
113 Value arr = make_array_from_values(vals, (int)n);
114 for (size_t i = 0; i < n; ++i)
115 free_value(vals[i]);
116 free(vals);
117 return arr;
118 }
119 case json_type_object: {
120 Value map = make_map_empty();
121 json_object_object_foreach(j, key, val) {
122 (void)map_set(&map, key, json_to_fun(val));
123 }
124 return map;
125 }
126 default:
127 return make_nil();
128 }
129}
130
131/**
132 * @brief Convert a Fun Value into a newly allocated json-c object tree.
133 *
134 * Mapping rules:
135 * - Nil -> json null
136 * - Bool -> json boolean
137 * - Int (64-bit) -> json int64
138 * - Float (double) -> json double
139 * - String -> json string (assumes UTF-8 already)
140 * - Array -> json array (elements converted recursively)
141 * - Map -> json object (keys are taken from the map's string keys)
142 *
143 * Unsupported or opaque Fun types are stringified using the placeholder
144 * "<unsupported>" to keep the conversion total.
145 *
146 * Ownership:
147 * - The caller owns the returned json_object* and must release it with
148 * json_object_put() when no longer needed.
149 *
150 * Notes and limitations:
151 * - Map keys are enumerated via map_keys_array(); only string keys are used.
152 * Non-string keys are ignored.
153 * - json-c does not support NaN/Inf as JSON numbers in a standard way; if such
154 * values appear in Fun Float, they are forwarded to json-c as-is.
155 *
156 * @param v Pointer to the source Value (must not be NULL).
157 * @return json_object* Newly created tree representing v.
158 */
159static json_object *fun_to_json(const Value *v) {
160 switch (v->type) {
161 case VAL_NIL:
162 return json_object_new_null();
163 case VAL_BOOL:
164 return json_object_new_boolean(v->i ? 1 : 0);
165 case VAL_INT:
166 return json_object_new_int64(v->i);
167 case VAL_FLOAT:
168 return json_object_new_double(v->d);
169 case VAL_STRING:
170 return json_object_new_string(v->s ? v->s : "");
171 case VAL_ARRAY: {
172 json_object *arr = json_object_new_array();
173 int n = array_length(v);
174 for (int i = 0; i < n; ++i) {
175 Value item;
176 if (array_get_copy(v, i, &item)) {
177 json_object_array_add(arr, fun_to_json(&item));
178 free_value(item);
179 } else {
180 json_object_array_add(arr, json_object_new_null());
181 }
182 }
183 return arr;
184 }
185 case VAL_MAP: {
186 json_object *obj = json_object_new_object();
187 /* We don't have an iterator API; use keys() helper */
188 Value keys = map_keys_array(v);
189 int kn = array_length(&keys);
190 for (int i = 0; i < kn; ++i) {
191 Value k;
192 if (!array_get_copy(&keys, i, &k)) continue;
193 if (k.type == VAL_STRING && k.s) {
194 Value val;
195 if (map_get_copy(v, k.s, &val)) {
196 json_object_object_add(obj, k.s, fun_to_json(&val));
197 free_value(val);
198 } else {
199 json_object_object_add(obj, k.s, json_object_new_null());
200 }
201 }
202 free_value(k);
203 }
204 free_value(keys);
205 return obj;
206 }
207 default:
208 /* Fallback: stringify unsupported types */
209 return json_object_new_string("<unsupported>");
210 }
211}
212#endif
static json_object * fun_to_json(const Value *v)
Convert a Fun Value into a newly allocated json-c object tree.
Definition json.c:159
static Value json_to_fun(json_object *j)
Convert a json-c object tree into a Fun Value.
Definition json.c:88
int map_set(Value *vm, const char *key, Value v)
Insert or replace a key in the map.
Definition map.c:79
Value make_map_empty(void)
Construct a new empty map Value.
Definition map.c:35
int map_get_copy(const Value *vm, const char *key, Value *out)
Look up a key and copy the stored value into out.
Definition map.c:148
Value map_keys_array(const Value *vm)
Return all map keys as an array of strings.
Definition map.c:183
Tagged union representing a Fun value.
Definition value.h:68
int64_t i
Definition value.h:71
double d
Definition value.h:72
ValueType type
Definition value.h:69
char * s
Definition value.h:73
Value make_bool(int v)
Construct a boolean Value.
Definition value.c:79
Value make_nil(void)
Construct a nil Value.
Definition value.c:126
Value make_string(const char *s)
Construct a string Value by duplicating the given C string.
Definition value.c:95
int array_length(const Value *v)
Get the element count of an array Value.
Definition value.c:176
void free_value(Value v)
Free dynamic storage owned by a Value.
Definition value.c:517
int array_get_copy(const Value *v, int index, Value *out)
Copy an array element into out.
Definition value.c:192
Value make_float(double v)
Construct a Value representing a double-precision float.
Definition value.c:64
Value make_int(int64_t v)
Construct a Value representing a 64-bit integer.
Definition value.c:51
Value make_array_from_values(const Value *vals, int count)
Create an array Value by copying items from an input span.
Definition value.c:142
@ VAL_BOOL
Definition value.h:52
@ VAL_ARRAY
Definition value.h:55
@ VAL_MAP
Definition value.h:56
@ VAL_STRING
Definition value.h:53
@ VAL_NIL
Definition value.h:57
@ VAL_INT
Definition value.h:51
@ VAL_FLOAT
Definition value.h:58