diff options
| author | suresh <sureshkrishnan.ai@gmail.com> | 2026-01-25 11:24:22 -0500 |
|---|---|---|
| committer | suresh <sureshkrishnan.ai@gmail.com> | 2026-01-25 11:24:22 -0500 |
| commit | 0bb69cb67078dfa921b5b8a42275ef31dfbc9a56 (patch) | |
| tree | b579695576ae27f7316866b18bd54073f8e2ca1f | |
| parent | 0bd7b99fbf813415b9a0217eaa2a4e8f6f74e1ea (diff) | |
fixed beffer overflow in vector. Added serialize and deserialize in json with vector reading from the struct with json
| -rw-r--r-- | examples/data/json_config.zc | 191 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 13 | ||||
| -rw-r--r-- | src/codegen/codegen_stmt.c | 2 | ||||
| -rw-r--r-- | src/parser/parser_core.c | 215 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 24 | ||||
| -rw-r--r-- | std/json.zc | 203 |
6 files changed, 574 insertions, 74 deletions
diff --git a/examples/data/json_config.zc b/examples/data/json_config.zc index ccfb1a2..f6283b2 100644 --- a/examples/data/json_config.zc +++ b/examples/data/json_config.zc @@ -5,15 +5,21 @@ import "std/map.zc" import "std/option.zc" import "std/string.zc" import "std/core.zc" +import "std/vec.zc" -raw { - typedef struct JsonValue* JsonValuePtr; +@derive(FromJson, ToJson) +struct Features { + logging: bool; + metrics: bool; } +@derive(FromJson, ToJson) struct Config { server_name: String; port: int; - logging: bool; + max_connections: int; + features: Features; + allowed_hosts: Vec<String>; } fn main() { @@ -24,76 +30,135 @@ fn main() { !"Failed to read config file: {content_res.err}"; return 1; } - - var json_str = content_res.unwrap(); - var json_res = JsonValue::parse(json_str.c_str()); - if json_res.is_err() { - !"JSON Parse Error: {json_res.err}"; + var json_str = content_res.unwrap(); + var parse_res = JsonValue::parse(json_str.c_str()); + if parse_res.is_err() { + !"JSON Parse Error"; json_str.free(); return 1; } - - var root = json_res.unwrap(); - - defer { - json_str.free(); - JsonValue::free(root); - free(root); - } - - if (*root).kind.tag != JsonType::JSON_OBJECT().tag { - !"Expected JSON Object at root"; - return 1; - } - - var config = Config { - server_name: String::new("Unknown"), - port: 0, - logging: false - }; - - var obj_map = (*root).object_val; - - if Map<JsonValue*>::contains(obj_map, "server_name") { - var opt = Map<JsonValue*>::get(obj_map, "server_name"); - var val = opt.unwrap(); - if (*val).kind.tag == JsonType::JSON_STRING().tag { - config.server_name.free(); - config.server_name = String::new((*val).string_val); - } - } - - if Map<JsonValue*>::contains(obj_map, "port") { - var opt = Map<JsonValue*>::get(obj_map, "port"); - var val = opt.unwrap(); - if (*val).kind.tag == JsonType::JSON_NUMBER().tag { - config.port = (int)(*val).number_val; - } + + var root = parse_res.unwrap(); + + // ============================================ + // Demo 1: Using accessor methods + // ============================================ + "=== Accessor methods ==="; + + var server_opt = (*root).get_string("server_name"); + var server = server_opt.unwrap_or("default"); + + var port_opt = (*root).get_int("port"); + var port = port_opt.unwrap_or(8080); + + var max_conn_opt = (*root).get_int("max_connections"); + var max_conn = max_conn_opt.unwrap_or(10); + + var logging = false; + var metrics = false; + var features_opt = (*root).get_object("features"); + if features_opt.is_some() { + var features = features_opt.unwrap(); + var logging_opt = features.get_bool("logging"); + logging = logging_opt.unwrap_or(false); + var metrics_opt = features.get_bool("metrics"); + metrics = metrics_opt.unwrap_or(false); } - - if Map<JsonValue*>::contains(obj_map, "features") { - var opt = Map<JsonValue*>::get(obj_map, "features"); - var features = opt.unwrap(); - if (*features).kind.tag == JsonType::JSON_OBJECT().tag { - var f_obj = (*features).object_val; - if Map<JsonValue*>::contains(f_obj, "logging") { - var l_opt = Map<JsonValue*>::get(f_obj, "logging"); - var l = l_opt.unwrap(); - if (*l).kind.tag == JsonType::JSON_BOOL().tag { - config.logging = (*l).bool_val; + + "Server: {server}"; + "Port: {port}"; + "Max Connections: {max_conn}"; + "Logging: {logging}"; + "Metrics: {metrics}"; + + // Reading array: allowed_hosts + var hosts_opt = (*root).get_array("allowed_hosts"); + if hosts_opt.is_some() { + var hosts = hosts_opt.unwrap(); + var count = hosts.len(); + "Allowed Hosts: ({count} entries)"; + for var i: usize = 0; i < count; i = i + 1 { + var host_opt = hosts.at(i); + if host_opt.is_some() { + var host_val = host_opt.unwrap(); + var host_str = (*host_val).as_string(); + if host_str.is_some() { + var h = host_str.unwrap(); + " - {h}"; } } } } - - "Configuration Loaded:"; + + // ============================================ + // Demo 2: from_json / to_json + // ============================================ + ""; + "=== @derive(FromJson, ToJson) ==="; + + var config_res = Config::from_json(root); + var config = config_res.unwrap(); + var s_name = config.server_name.c_str(); - "Server: {s_name}"; - "Port: {config.port}"; - "Logging: {config.logging}"; - + "Config from JSON:"; + " server_name: {s_name}"; + " port: {config.port}"; + " max_connections: {config.max_connections}"; + " features.logging: {config.features.logging}"; + " features.metrics: {config.features.metrics}"; + var hosts_count = config.allowed_hosts.length(); + " allowed_hosts: ({hosts_count} entries)"; + for var i: usize = 0; i < hosts_count; i = i + 1 { + var host = config.allowed_hosts.get(i).c_str(); + " - {host}"; + } + + // Serialize back to JSON + var json_out = config.to_json(); + + var out_name_opt = json_out.get_string("server_name"); + var out_name = out_name_opt.unwrap_or("?"); + + var out_port_opt = json_out.get_int("port"); + var out_port = out_port_opt.unwrap_or(0); + + var out_features_opt = json_out.get_object("features"); + var out_logging = false; + var out_metrics = false; + if out_features_opt.is_some() { + var out_features = out_features_opt.unwrap(); + var logging_opt = out_features.get_bool("logging"); + out_logging = logging_opt.unwrap_or(false); + var metrics_opt = out_features.get_bool("metrics"); + out_metrics = metrics_opt.unwrap_or(false); + } + + // Check allowed_hosts in serialized output + var out_hosts_opt = json_out.get_array("allowed_hosts"); + var out_hosts_count: usize = 0; + if out_hosts_opt.is_some() { + out_hosts_count = out_hosts_opt.unwrap().len(); + } + + ""; + "Round-trip to_json:"; + " server_name: {out_name}"; + " port: {out_port}"; + " features.logging: {out_logging}"; + " features.metrics: {out_metrics}"; + " allowed_hosts: {out_hosts_count} entries"; + + // Cleanup config.server_name.free(); - + for var i: usize = 0; i < config.allowed_hosts.length(); i = i + 1 { + config.allowed_hosts.get(i).free(); + } + config.allowed_hosts.free(); + json_out.free(); + json_str.free(); + JsonValue::free(root); + free(root); + return 0; } diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index d525963..e5d73a0 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -22,13 +22,15 @@ static void emit_freestanding_preamble(FILE *out) "uint64_t\n", out); fputs("#define F32 float\n#define F64 double\n", out); - fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " + fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : \"false\"; }\n", out); + fputs("#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " "char*: \"%s\", void*: \"%p\")\n", out); + fputs("#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x), default: (x))\n", out); fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out); fputs("__attribute__((weak)) void* z_malloc(usize sz) { return NULL; }\n", out); @@ -62,7 +64,10 @@ void emit_preamble(ParserContext *ctx, FILE *out) fputs("#define ZC_AUTO auto\n", out); fputs("#define ZC_CAST(T, x) static_cast<T>(x)\n", out); // C++ _z_str via overloads - fputs("inline const char* _z_str(bool) { return \"%d\"; }\n", out); + fputs("inline const char* _z_bool_str(bool b) { return b ? \"true\" : \"false\"; }\n", out); + fputs("inline const char* _z_str(bool) { return \"%s\"; }\n", out); + fputs("inline const char* _z_arg(bool b) { return _z_bool_str(b); }\n", out); + fputs("template<typename T> inline T _z_arg(T x) { return x; }\n", out); fputs("inline const char* _z_str(char) { return \"%c\"; }\n", out); fputs("inline const char* _z_str(int) { return \"%d\"; }\n", out); fputs("inline const char* _z_str(unsigned int) { return \"%u\"; }\n", out); @@ -82,13 +87,15 @@ void emit_preamble(ParserContext *ctx, FILE *out) fputs("#define ZC_AUTO __auto_type\n", out); fputs("#define ZC_CAST(T, x) ((T)(x))\n", out); fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out); - fputs("#define _z_str(x) _Generic((x), _Bool: \"%d\", char: \"%c\", " + fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : \"false\"; }\n", out); + fputs("#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " "char*: \"%s\", void*: \"%p\")\n", out); + fputs("#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x), default: (x))\n", out); } fputs("typedef size_t usize;\ntypedef char* string;\n", out); diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index ff8ea46..cba55b4 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -1519,7 +1519,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) emit_auto_type(ctx, node->repl_print.expr, node->token, out); fprintf(out, " _zval = ("); codegen_expression(ctx, node->repl_print.expr, out); - fprintf(out, "); fprintf(stdout, _z_str(_zval), _zval); fprintf(stdout, " + fprintf(out, "); fprintf(stdout, _z_str(_zval), _z_arg(_zval)); fprintf(stdout, " "\"\\n\"); }\n"); break; } diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c index 7468bfb..3bc6d66 100644 --- a/src/parser/parser_core.c +++ b/src/parser/parser_core.c @@ -637,6 +637,221 @@ static ASTNode *generate_derive_impls(ParserContext *ctx, ASTNode *strct, char * code = xmalloc(1024); sprintf(code, "impl Copy for %s {}", name); } + else if (0 == strcmp(trait, "FromJson")) + { + // Generate from_json(j: JsonValue*) -> Result<StructName> + // Only works for structs (not enums) + if (strct->type != NODE_STRUCT) + { + zwarn_at(strct->token, "@derive(FromJson) only works on structs"); + continue; + } + + char body[8192]; + body[0] = 0; + + // Track Vec<String> fields for forget calls + char *vec_fields[32]; + int vec_field_count = 0; + + // Build field assignments + ASTNode *f = strct->strct.fields; + while (f) + { + if (f->type == NODE_FIELD) + { + char *fn = f->field.name; + char *ft = f->field.type; + char assign[2048]; + + if (!fn || !ft) + { + f = f->next; + continue; + } + + // Map types to appropriate get_* calls + if (strcmp(ft, "int") == 0) + { + sprintf(assign, "var _f_%s = (*j).get_int(\"%s\").unwrap_or(0);\n", fn, fn); + } + else if (strcmp(ft, "double") == 0) + { + sprintf(assign, "var _f_%s = (*j).get_float(\"%s\").unwrap_or(0.0);\n", fn, fn); + } + else if (strcmp(ft, "bool") == 0) + { + sprintf(assign, "var _f_%s = (*j).get_bool(\"%s\").unwrap_or(false);\n", fn, fn); + } + else if (strcmp(ft, "char*") == 0) + { + sprintf(assign, "var _f_%s = (*j).get_string(\"%s\").unwrap_or(\"\");\n", fn, fn); + } + else if (strcmp(ft, "String") == 0) + { + sprintf(assign, "var _f_%s = String::new((*j).get_string(\"%s\").unwrap_or(\"\"));\n", fn, fn); + } + else if (ft && strstr(ft, "Vec") && strstr(ft, "String")) + { + // Track this field for forget() call later + if (vec_field_count < 32) + { + vec_fields[vec_field_count++] = fn; + } + sprintf(assign, + "var _f_%s = Vec<String>::new();\n" + "var _arr_%s = (*j).get_array(\"%s\");\n" + "if _arr_%s.is_some() {\n" + " var _a_%s = _arr_%s.unwrap();\n" + " for var _i_%s: usize = 0; _i_%s < _a_%s.len(); _i_%s = _i_%s + 1 {\n" + " var _item_%s = _a_%s.at(_i_%s);\n" + " if _item_%s.is_some() {\n" + " var _str_%s = (*_item_%s.unwrap()).as_string();\n" + " if _str_%s.is_some() {\n" + " var _s_%s = String::new(_str_%s.unwrap());\n" + " _f_%s.push(_s_%s); _s_%s.forget();\n" + " }\n" + " }\n" + " }\n" + "}\n", + fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn); + } + else + { + // Nested struct: call NestedType::from_json recursively + sprintf(assign, + "var _opt_%s = (*j).get(\"%s\");\n" + "var _f_%s: %s;\n" + "if _opt_%s.is_some() { _f_%s = %s::from_json(_opt_%s.unwrap()).unwrap(); }\n", + fn, fn, fn, ft, fn, fn, ft, fn); + } + strcat(body, assign); + } + f = f->next; + } + + // Build struct initialization + strcat(body, "return Result<"); + strcat(body, name); + strcat(body, ">::Ok("); + strcat(body, name); + strcat(body, " { "); + + f = strct->strct.fields; + int first = 1; + while (f) + { + if (f->type == NODE_FIELD) + { + if (!first) strcat(body, ", "); + char init[128]; + // Check if this is a Vec<String> field - clone it to avoid double-free + int is_vec_field = 0; + for (int vi = 0; vi < vec_field_count; vi++) + { + if (strcmp(vec_fields[vi], f->field.name) == 0) + { + is_vec_field = 1; + break; + } + } + if (is_vec_field) + { + sprintf(init, "%s: _f_%s.clone()", f->field.name, f->field.name); + } + else + { + sprintf(init, "%s: _f_%s", f->field.name, f->field.name); + } + strcat(body, init); + first = 0; + } + f = f->next; + } + strcat(body, " }); "); + + code = xmalloc(8192 + 1024); + sprintf(code, + "impl %s { fn from_json(j: JsonValue*) -> Result<%s> { %s } }", + name, name, body); + } + else if (0 == strcmp(trait, "ToJson")) + { + // Generate to_json(self) -> JsonValue + // Only works for structs (not enums) + if (strct->type != NODE_STRUCT) + { + zwarn_at(strct->token, "@derive(ToJson) only works on structs"); + continue; + } + + char body[8192]; + strcpy(body, "var _obj = JsonValue::object();\n"); + + ASTNode *f = strct->strct.fields; + while (f) + { + if (f->type == NODE_FIELD) + { + char *fn = f->field.name; + char *ft = f->field.type; + char set_call[2048]; + + if (!fn || !ft) + { + f = f->next; + continue; + } + + if (strcmp(ft, "int") == 0) + { + sprintf(set_call, "_obj.set(\"%s\", JsonValue::number((double)self.%s));\n", fn, fn); + } + else if (strcmp(ft, "double") == 0) + { + sprintf(set_call, "_obj.set(\"%s\", JsonValue::number(self.%s));\n", fn, fn); + } + else if (strcmp(ft, "bool") == 0) + { + sprintf(set_call, "_obj.set(\"%s\", JsonValue::bool(self.%s));\n", fn, fn); + } + else if (strcmp(ft, "char*") == 0) + { + sprintf(set_call, "_obj.set(\"%s\", JsonValue::string(self.%s));\n", fn, fn); + } + else if (strcmp(ft, "String") == 0) + { + sprintf(set_call, "_obj.set(\"%s\", JsonValue::string(self.%s.c_str()));\n", fn, fn); + } + else if (ft && strstr(ft, "Vec") && strstr(ft, "String")) + { + sprintf(set_call, + "var _arr_%s = JsonValue::array();\n" + "for var _i_%s: usize = 0; _i_%s < self.%s.length(); _i_%s = _i_%s + 1 {\n" + " _arr_%s.push(JsonValue::string(self.%s.get(_i_%s).c_str()));\n" + "}\n" + "_obj.set(\"%s\", _arr_%s);\n", + fn, fn, fn, fn, fn, fn, fn, fn, fn, fn, fn); + } + else + { + // Nested struct: call to_json recursively + sprintf(set_call, + "_obj.set(\"%s\", self.%s.to_json());\n", + fn, fn); + } + strcat(body, set_call); + } + f = f->next; + } + + strcat(body, "return _obj;"); + + code = xmalloc(8192 + 1024); + sprintf(code, + "impl %s { fn to_json(self) -> JsonValue { %s } }", + name, body); + } if (code) { diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 4b09c83..54e9b88 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1591,10 +1591,15 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, Type *t = expr_node ? expr_node->type_info : NULL; char *inferred_type = t ? type_to_string(t) : find_symbol_type(ctx, clean_expr); + int is_bool = 0; if (inferred_type) { - if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0 || - strcmp(inferred_type, "bool") == 0) + if (strcmp(inferred_type, "bool") == 0) + { + format_spec = "%s"; + is_bool = 1; + } + else if (strcmp(inferred_type, "int") == 0 || strcmp(inferred_type, "i32") == 0) { format_spec = "%d"; } @@ -1657,7 +1662,16 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, strcat(gen, buf); strcat(gen, format_spec); strcat(gen, "\", "); - strcat(gen, rw_expr); + if (is_bool) + { + strcat(gen, "_z_bool_str("); + strcat(gen, rw_expr); + strcat(gen, ")"); + } + else + { + strcat(gen, rw_expr); + } strcat(gen, "); "); } else @@ -1667,9 +1681,9 @@ char *process_printf_sugar(ParserContext *ctx, const char *content, int newline, sprintf(buf, "fprintf(%s, _z_str(", target); strcat(gen, buf); strcat(gen, rw_expr); - strcat(gen, "), "); + strcat(gen, "), _z_arg("); strcat(gen, rw_expr); - strcat(gen, "); "); + strcat(gen, ")); "); } } diff --git a/std/json.zc b/std/json.zc index cfef2c3..6c4a0da 100644 --- a/std/json.zc +++ b/std/json.zc @@ -4,6 +4,7 @@ import "./vec.zc" import "./map.zc" import "./string.zc" import "./result.zc" +import "./option.zc" @derive(Eq) enum JsonType { @@ -201,7 +202,47 @@ impl JsonValue { *m = Map_JsonValuePtr::new(); return JsonValue { kind: JsonType::JSON_OBJECT(), string_val: 0, number_val: 0, bool_val: false, array_val: 0, object_val: m }; } - + + // ============================================ + // Heap-allocated factory methods (returns pointers) + // ============================================ + + fn null_ptr() -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::null(); + return p; + } + + fn bool_ptr(b: bool) -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::bool(b); + return p; + } + + fn number_ptr(n: double) -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::number(n); + return p; + } + + fn string_ptr(s: char*) -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::string(s); + return p; + } + + fn array_ptr() -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::array(); + return p; + } + + fn object_ptr() -> JsonValue* { + var p: JsonValue* = malloc(sizeof(JsonValue)); + *p = JsonValue::object(); + return p; + } + fn push(self, val: JsonValue) { if (self.kind.tag != JsonType::JSON_ARRAY().tag) return; var p: JsonValue* = malloc(sizeof(JsonValue)); @@ -223,7 +264,165 @@ impl JsonValue { } return Result<JsonValue*>::Err("JSON parse error"); } - + + // ============================================ + // Type checking helpers + // ============================================ + + fn is_null(self) -> bool { + return self.kind.tag == JsonType::JSON_NULL().tag; + } + + fn is_bool(self) -> bool { + return self.kind.tag == JsonType::JSON_BOOL().tag; + } + + fn is_number(self) -> bool { + return self.kind.tag == JsonType::JSON_NUMBER().tag; + } + + fn is_string(self) -> bool { + return self.kind.tag == JsonType::JSON_STRING().tag; + } + + fn is_array(self) -> bool { + return self.kind.tag == JsonType::JSON_ARRAY().tag; + } + + fn is_object(self) -> bool { + return self.kind.tag == JsonType::JSON_OBJECT().tag; + } + + // ============================================ + // Direct value extractors + // ============================================ + + fn as_string(self) -> Option<char*> { + if self.kind.tag == JsonType::JSON_STRING().tag { + return Option<char*>::Some(self.string_val); + } + return Option<char*>::None(); + } + + fn as_int(self) -> Option<int> { + if self.kind.tag == JsonType::JSON_NUMBER().tag { + return Option<int>::Some((int)self.number_val); + } + return Option<int>::None(); + } + + fn as_float(self) -> Option<double> { + if self.kind.tag == JsonType::JSON_NUMBER().tag { + return Option<double>::Some(self.number_val); + } + return Option<double>::None(); + } + + fn as_bool(self) -> Option<bool> { + if self.kind.tag == JsonType::JSON_BOOL().tag { + return Option<bool>::Some(self.bool_val); + } + return Option<bool>::None(); + } + + // ============================================ + // Object key accessors + // ============================================ + + fn get(self, key: char*) -> Option<JsonValue*> { + if self.kind.tag != JsonType::JSON_OBJECT().tag { + return Option<JsonValue*>::None(); + } + if Map<JsonValue*>::contains(self.object_val, key) { + return Map<JsonValue*>::get(self.object_val, key); + } + return Option<JsonValue*>::None(); + } + + fn get_string(self, key: char*) -> Option<char*> { + var opt = self.get(key); + if opt.is_none() { + return Option<char*>::None(); + } + var val = opt.unwrap(); + return (*val).as_string(); + } + + fn get_int(self, key: char*) -> Option<int> { + var opt = self.get(key); + if opt.is_none() { + return Option<int>::None(); + } + var val = opt.unwrap(); + return (*val).as_int(); + } + + fn get_float(self, key: char*) -> Option<double> { + var opt = self.get(key); + if opt.is_none() { + return Option<double>::None(); + } + var val = opt.unwrap(); + return (*val).as_float(); + } + + fn get_bool(self, key: char*) -> Option<bool> { + var opt = self.get(key); + if opt.is_none() { + return Option<bool>::None(); + } + var val = opt.unwrap(); + return (*val).as_bool(); + } + + fn get_object(self, key: char*) -> Option<JsonValue*> { + var opt = self.get(key); + if opt.is_none() { + return Option<JsonValue*>::None(); + } + var val = opt.unwrap(); + if (*val).kind.tag == JsonType::JSON_OBJECT().tag { + return Option<JsonValue*>::Some(val); + } + return Option<JsonValue*>::None(); + } + + fn get_array(self, key: char*) -> Option<JsonValue*> { + var opt = self.get(key); + if opt.is_none() { + return Option<JsonValue*>::None(); + } + var val = opt.unwrap(); + if (*val).kind.tag == JsonType::JSON_ARRAY().tag { + return Option<JsonValue*>::Some(val); + } + return Option<JsonValue*>::None(); + } + + // ============================================ + // Array accessors + // ============================================ + + fn at(self, index: usize) -> Option<JsonValue*> { + if self.kind.tag != JsonType::JSON_ARRAY().tag { + return Option<JsonValue*>::None(); + } + if index >= self.array_val.length() { + return Option<JsonValue*>::None(); + } + return Option<JsonValue*>::Some(self.array_val.get(index)); + } + + fn len(self) -> usize { + if self.kind.tag == JsonType::JSON_ARRAY().tag { + return self.array_val.length(); + } + if self.kind.tag == JsonType::JSON_OBJECT().tag { + return self.object_val.length(); + } + return 0; + } + fn free(self) { if (self.kind.tag == JsonType::JSON_STRING().tag) free(self.string_val); if (self.kind.tag == JsonType::JSON_ARRAY().tag) { |
