From d2182a433c06ed9100e1d3d167d29dd161da307a Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 2 Apr 2025 12:52:45 +0000 Subject: [PATCH] Initial commit --- .envrc | 1 + .gitignore | 5 ++ CMakeLists.txt | 18 +++++ README.md | 6 ++ flake.lock | 61 ++++++++++++++++ flake.nix | 42 +++++++++++ json.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ json.h | 45 ++++++++++++ main.c | 50 +++++++++++++ 9 files changed, 421 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 json.c create mode 100644 json.h create mode 100644 main.c diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5adaf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Nix +result* + +# Misc +.direnv diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bc7edb2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.20) + +project( + json + LANGUAGES + C +) + +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +add_executable( + ${PROJECT_NAME} + main.c + json.c +) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ee6924 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Json + +A simple json stringifying library written in C out of frustration. + +See `main.c` for a usage example. + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..a47bfd9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1743315132, + "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "52faf482a3889b7619003c0daec593a1912fddc1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4cc5a94 --- /dev/null +++ b/flake.nix @@ -0,0 +1,42 @@ +{ + description = "C Template"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + nixpkgs + , flake-utils + , ... + }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + pname = "hello-world"; #package name + version = "0.0.1"; + src = ./.; + buildInputs = with pkgs; [ + ]; + nativeBuildInputs = with pkgs; [ + cmake + ninja + pkg-config + clang-tools + ctags + ]; + in + { + devShells.default = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } { + inherit buildInputs nativeBuildInputs; + }; + + packages.default = pkgs.stdenv.mkDerivation { + inherit buildInputs nativeBuildInputs pname version src; + }; + + formatter = pkgs.alejandra; + }); +} diff --git a/json.c b/json.c new file mode 100644 index 0000000..a98052f --- /dev/null +++ b/json.c @@ -0,0 +1,193 @@ +#include "json.h" + +#include + +#define UNREACHABLE() assert(false) + +Json json_init(FILE* stream, Whitespace whitespace) +{ + return (Json){ + .stream = stream, + .whitespace = whitespace, + .indent_level = 0, + .next_punctuation = BEGINNING, + }; +} + +void json_free(Json* json) +{ +} + +void begin_value(Json* json); +void end_value(Json* json); +void push_indentation(Json* json); +void pop_indentation(Json* json); +void indent(Json* json); + +void json_begin_array(Json* json) +{ + begin_value(json); + putc('[', json->stream); + push_indentation(json); + json->next_punctuation = NONE; +} + +void json_end_array(Json* json) +{ + pop_indentation(json); + switch (json->next_punctuation) { + case NONE: + break; + case COMMA: + indent(json); + break; + case BEGINNING: + case COLON: + UNREACHABLE(); + } + putc(']', json->stream); + end_value(json); +} + +void json_begin_object(Json* json) +{ + begin_value(json); + putc('{', json->stream); + push_indentation(json); + json->next_punctuation = NONE; +} + +void json_end_object(Json* json) +{ + pop_indentation(json); + switch (json->next_punctuation) { + case NONE: + break; + case COMMA: + indent(json); + break; + case BEGINNING: + case COLON: + UNREACHABLE(); + } + putc('}', json->stream); + end_value(json); +} + +void json_add_object_field(Json* json, const char* key) +{ + begin_value(json); + fprintf(json->stream, "\"%s\"", key); + json->next_punctuation = COLON; +} + +void json_add_string(Json* json, const char* value) +{ + begin_value(json); + fprintf(json->stream, "\"%s\"", value); + end_value(json); +} + +void json_add_long(Json* json, long value) +{ + begin_value(json); + fprintf(json->stream, "%ld", value); + end_value(json); +} + +void json_add_double(Json* json, double value) +{ + begin_value(json); + fprintf(json->stream, "%f", value); + end_value(json); +} + +void json_add_bool(Json* json, bool value) +{ + begin_value(json); + const char* s; + if (value) { + s = "true"; + } else { + s = "false"; + } + fputs(s, json->stream); + end_value(json); +} + +void json_add_null(Json* json) +{ + begin_value(json); + fputs("null", json->stream); + end_value(json); +} + +void begin_value(Json* json) +{ + switch (json->next_punctuation) { + case BEGINNING: + break; + case NONE: + indent(json); + break; + case COMMA: + fputc(',', json->stream); + indent(json); + break; + case COLON: + putc(':', json->stream); + if (json->whitespace != MINIFIED) { + fputc(' ', json->stream); + } + break; + } +} + +void end_value(Json* json) +{ + json->next_punctuation = COMMA; +} + +void push_indentation(Json* json) +{ + json->indent_level += 1; +} + +void pop_indentation(Json* json) +{ + json->indent_level -= 1; +} + +void indent(Json* json) +{ + char indent_char = ' '; + int n; + switch (json->whitespace) { + case MINIFIED: + return; + case INDENT_1: + n = 1 * json->indent_level; + break; + case INDENT_2: + n = 2 * json->indent_level; + break; + case INDENT_3: + n = 3 * json->indent_level; + break; + case INDENT_4: + n = 4 * json->indent_level; + break; + case INDENT_8: + n = 8 * json->indent_level; + break; + case INDENT_TAB: + indent_char = '\t'; + n = json->indent_level; + break; + } + putc('\n', json->stream); + for (int i = 0; i < n; i++) { + putc(indent_char, json->stream); + } +} + diff --git a/json.h b/json.h new file mode 100644 index 0000000..aa3a316 --- /dev/null +++ b/json.h @@ -0,0 +1,45 @@ +#ifndef JSON_H +#define JSON_H + +#include + +typedef enum Whitespace { + MINIFIED, + INDENT_1, + INDENT_2, + INDENT_3, + INDENT_4, + INDENT_8, + INDENT_TAB, +} Whitespace; + +typedef enum Punctuation { + BEGINNING, + NONE, + COMMA, + COLON, +} Punctuation; + +typedef struct Json { + FILE* stream; + Whitespace whitespace; + int indent_level; + Punctuation next_punctuation; +} Json; + +Json json_init(FILE* stream, Whitespace whitespace); +void json_free(Json* json); + +void json_begin_array(Json* json); +void json_end_array(Json* json); +void json_begin_object(Json* json); +void json_end_object(Json* json); +void json_add_object_field(Json* json, const char* key); +void json_add_string(Json* json, const char* value); +void json_add_long(Json* json, long value); +void json_add_double(Json* json, double value); +void json_add_bool(Json* json, bool value); +void json_add_null(Json* json); + +#endif // JSON_H + diff --git a/main.c b/main.c new file mode 100644 index 0000000..3a8a6f6 --- /dev/null +++ b/main.c @@ -0,0 +1,50 @@ +#include + +#include "json.h" + +typedef struct Info { + int id; + const char* description; + bool enabled; +} Info; + +#define INFO_LEN 2 +static const Info infos[INFO_LEN] = { + (Info){ + .id = 0, + .description = "test", + .enabled = true, + }, + (Info){ + .id = 1, + .description = "Hello, World!", + .enabled = false, + }, +}; + +int main() +{ + auto json = json_init(stdout, INDENT_2); + + json_begin_array(&json); + for (int i = 0; i < INFO_LEN; i++) { + auto info = infos[i]; + + json_begin_object(&json); + { + json_add_object_field(&json, "id"); + json_add_long(&json, info.id); + + json_add_object_field(&json, "description"); + json_add_string(&json, info.description); + + json_add_object_field(&json, "enabled"); + json_add_bool(&json, info.enabled); + } + json_end_object(&json); + } + json_end_array(&json); + + json_free(&json); +} +