Learn
.cvs.h- compilation and linking
- CMake
- static libraries
- include paths
- compiler warnings
Build
CMakeLists.txt
include/tinyjson/
src/
tests/
Goal: Build and run a minimal C project confidently.
Learn
structenumtypedef.fieldaccess- separating public types into headers
Build
typedef enum
{
TOKEN_STRING,
TOKEN_NUMBER,
TOKEN_TRUE,
TOKEN_FALSE,
TOKEN_NULL,
TOKEN_ERROR,
TOKEN_EOF
} TokenType;
typedef struct
{
TokenType type;
const char *start;
size_t length;
} Token;Goal: Understand how C represents structured data.
Learn
- addresses
*&- pointer types
- pointer arithmetic
const char *
Build
const char *json = "{\"temperature\": 28.5}";
const char *current = json;Goal: Be completely comfortable with:
current
*current
¤t
Learn
char **- modifying a pointer through another pointer
- why
tokenizer_next(¤t)is necessary
Build
Token tokenizer_next(const char **current);Understand:
current
↓
*current
↓
**current
Goal: Understand const char ** rather than memorizing it.
Learn
whileif- character comparisons
'\0'EOF<ctype.h>isdigit()
Build
skip_whitespace()and basic punctuation scanning.
Goal: Move through an input string safely.
Learn
- pointer subtraction
size_t- non-owning memory
%.*s
Build
typedef struct
{
TokenType type;
const char *start;
size_t length;
} Token;Understand:
start + length
as a view into the original JSON.
Goal: Stop thinking that every token needs a copied string.
Learn
- scanning until a delimiter
- escaped characters
- backslashes
- validating input
Build
scan_string()Support:
"hello"
"hello \"world\""
"hello \\ world"
"hello\nworld"Eventually validate JSON escapes:
\" \\ \/ \b \f \n \r \t \uXXXX
Goal: Understand stateful character scanning.
Learn
- parsing a grammar manually
- optional components
- state/branching
- validating malformed input
Build
scan_number()Support:
0
42
-42
3.14
-3.14
1e10
-2.5e-3
Reject:
01
1.
1e
1e-
-
Goal: Learn how a tokenizer recognizes a structured grammar.
Learn
- fixed-length matching
- arrays of characters
- pointer indexing
Build
scan_true()
scan_false()
scan_null()For example:
(*current)[0]
(*current)[1]
(*current)[2]
(*current)[3]Goal: Understand how strings are inspected without copying them.
Learn
- assertions
- test organization
- expected failures
- regression testing
Build:
tests/
├── test_tokenizer.c
├── test_numbers.c
├── test_strings.c
└── test_literals.c
Start with:
assert(token.type == TOKEN_NUMBER);Then deliberately create failing tests.
Goal: Develop the habit:
Write failing test → implement → make test pass → refactor.
Learn
- unions
- tagged unions
- ownership
- dynamic memory
Build:
typedef enum
{
JSON_NULL,
JSON_BOOL,
JSON_NUMBER,
JSON_STRING,
JSON_ARRAY,
JSON_OBJECT
} JsonType;and:
typedef struct JsonValue
{
JsonType type;
union
{
int boolean;
double number;
char *string;
} data;
} JsonValue;Understand the difference between:
TOKEN_NUMBER
and:
JSON_NUMBER
Learn
- parser control flow
- converting tokens into values
strtod()- error propagation
Build:
parse_value()Handle:
TOKEN_NULL
TOKEN_TRUE
TOKEN_FALSE
TOKEN_NUMBER
TOKEN_STRING
Example:
"28.5"
↓
TOKEN_NUMBER
↓
strtod()
↓
double 28.5
↓
JSON_NUMBER
Goal: Understand the difference between lexing and parsing.
Learn
- dynamic arrays
mallocreallocfree- recursion
Parse:
[1, 2, 3]Then:
[1, "hello", true, null]Then:
[[1, 2], [3, 4]]Goal: Understand recursive data structures.
Learn
- key/value relationships
- dynamic structures
- ownership
- nested parsing
Parse:
{
"name": "John",
"age": 30
}Then:
{
"user": {
"name": "John"
}
}Goal: Understand how a recursive parser represents real JSON.
Learn
- ownership
- allocation
- deallocation
malloccallocreallocfree- avoiding leaks
- use-after-free
Build:
void json_free(JsonValue *value);It should recursively free:
object
├── key
├── value
└── nested value
└── ...
Goal: Be able to explain who owns every allocation.
Once the parser works, don't immediately optimize it.
First make it reliable.
Learn:
- error types
- error positions
- propagating errors
- useful diagnostics
Move from:
TOKEN_ERROR
to something like:
invalid exponent at position 17
Learn:
- fuzzing
- malformed input
- crash detection
- invariants
The fundamental requirement becomes:
ANY INPUT
↓
tinyjson
↓
valid result OR clean error
Never crash.
Learn:
AddressSanitizer
UndefinedBehaviorSanitizer
Use them to find:
- buffer overflows
- use-after-free
- invalid memory access
- undefined behavior
Learn:
- UTF-8
- Unicode code points
- JSON
\uXXXX - surrogate pairs
- byte vs character concepts
This is a particularly valuable C lesson because it forces you to understand the difference between:
bytes
characters
code points
Learn defensive programming.
Support limits such as:
max_depth
max_string_length
max_input_lengthUnderstand why this matters when parsing untrusted input.
Turn internal implementation into a clean public API:
JsonValue *json_parse(const char *input);
void json_free(JsonValue *value);Keep implementation details private.
Document:
- installation
- API
- supported JSON
- errors
- memory ownership
- limitations
- examples
Compare tinyjson against mature JSON implementations.
Not as a dependency.
Instead:
same input
↓
┌─────────┴─────────┐
↓ ↓
tinyjson reference parser
↓ ↓
└─────────┬─────────┘
↓
compare
This is where a production library becomes much more trustworthy.
Only now learn:
- profiling
- allocations
- cache behavior
- benchmarks
- avoiding unnecessary copies
- parser throughput
Then optimize based on measurements rather than guesses.
C FUNDAMENTALS
│
├── structs
├── enums
├── typedef
├── pointers
├── double pointers
├── arrays
├── strings
└── memory
│
▼
TOKENIZER
│
├── whitespace
├── punctuation
├── strings
├── numbers
└── literals
│
▼
TESTS
│
▼
JSON VALUE
│
├── union
├── ownership
└── dynamic memory
│
▼
RECURSIVE PARSER
│
├── primitives
├── arrays
└── objects
│
▼
MEMORY MANAGEMENT
│
▼
ERROR HANDLING
│
▼
FUZZ TESTING
│
▼
SANITIZERS
│
▼
UTF-8 / UNICODE
│
▼
RESOURCE LIMITS
│
▼
PUBLIC API
│
▼
COMPATIBILITY TESTS
│
▼
BENCHMARKS
│
▼
PRODUCTION RELEASE
You're around Phase 2, steps 7–9:
✅ project structure
✅ CMake
✅ structs/enums/typedef
✅ pointers
✅ double pointers
✅ whitespace
✅ punctuation
✅ basic strings
✅ numbers
✅ true
✅ false
🔨 null
🔨 strict number validation
🔨 robust string escapes
⬜ tokenizer tests
⬜ parser
⬜ JsonValue
tinyjson
│
├── tokenizer
│ ├── strings
│ ├── numbers
│ ├── true
│ ├── false
│ └── null
│
├── parser
│ ├── primitive values
│ ├── arrays
│ └── objects
│
├── JsonValue
│
├── memory management
│
├── errors
│
└── tests
So don't jump to arrays, objects, Unicode, or optimization yet.
Your next learning milestone should be:
Finish a strict, well-tested tokenizer.
Once that's solid, we'll move into JsonValue and the parser.