forked from miloyip/json-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
706 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
cmake_minimum_required (VERSION 2.6) | ||
project (leptjson_test C) | ||
|
||
if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") | ||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -pedantic -Wall") | ||
endif() | ||
|
||
add_library(leptjson leptjson.c) | ||
add_executable(leptjson_test test.c) | ||
target_link_libraries(leptjson_test leptjson) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
#include "leptjson.h" | ||
#include <assert.h> /* assert() */ | ||
#include <errno.h> /* errno, ERANGE */ | ||
#include <math.h> /* HUGE_VAL */ | ||
#include <stdlib.h> /* NULL, malloc(), realloc(), free(), strtod() */ | ||
#include <string.h> /* memcpy() */ | ||
|
||
#ifndef LEPT_PARSE_STACK_INIT_SIZE | ||
#define LEPT_PARSE_STACK_INIT_SIZE 256 | ||
#endif | ||
|
||
#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) | ||
#define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9') | ||
#define ISDIGIT1TO9(ch) ((ch) >= '1' && (ch) <= '9') | ||
#define PUTC(c, ch) do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0) | ||
|
||
typedef struct { | ||
const char* json; | ||
char* stack; | ||
size_t size, top; | ||
}lept_context; | ||
|
||
static void* lept_context_push(lept_context* c, size_t size) { | ||
void* ret; | ||
assert(size > 0); | ||
if (c->top + size >= c->size) { | ||
if (c->size == 0) | ||
c->size = LEPT_PARSE_STACK_INIT_SIZE; | ||
while (c->top + size >= c->size) | ||
c->size += c->size >> 1; /* c->size * 1.5 */ | ||
c->stack = (char*)realloc(c->stack, c->size); | ||
} | ||
ret = c->stack + c->top; | ||
c->top += size; | ||
return ret; | ||
} | ||
|
||
static void* lept_context_pop(lept_context* c, size_t size) { | ||
assert(c->top >= size); | ||
return c->stack + (c->top -= size); | ||
} | ||
|
||
static void lept_parse_whitespace(lept_context* c) { | ||
const char *p = c->json; | ||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') | ||
p++; | ||
c->json = p; | ||
} | ||
|
||
static int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) { | ||
size_t i; | ||
EXPECT(c, literal[0]); | ||
for (i = 0; literal[i + 1]; i++) | ||
if (c->json[i] != literal[i + 1]) | ||
return LEPT_PARSE_INVALID_VALUE; | ||
c->json += i; | ||
v->type = type; | ||
return LEPT_PARSE_OK; | ||
} | ||
|
||
static int lept_parse_number(lept_context* c, lept_value* v) { | ||
const char* p = c->json; | ||
if (*p == '-') p++; | ||
if (*p == '0') p++; | ||
else { | ||
if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE; | ||
for (p++; ISDIGIT(*p); p++); | ||
} | ||
if (*p == '.') { | ||
p++; | ||
if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; | ||
for (p++; ISDIGIT(*p); p++); | ||
} | ||
if (*p == 'e' || *p == 'E') { | ||
p++; | ||
if (*p == '+' || *p == '-') p++; | ||
if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; | ||
for (p++; ISDIGIT(*p); p++); | ||
} | ||
errno = 0; | ||
v->u.n = strtod(c->json, NULL); | ||
if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL)) | ||
return LEPT_PARSE_NUMBER_TOO_BIG; | ||
v->type = LEPT_NUMBER; | ||
c->json = p; | ||
return LEPT_PARSE_OK; | ||
} | ||
|
||
static int lept_parse_string(lept_context* c, lept_value* v) { | ||
size_t head = c->top, len; | ||
const char* p; | ||
EXPECT(c, '\"'); | ||
p = c->json; | ||
for (;;) { | ||
char ch = *p++; | ||
switch (ch) { | ||
case '\"': | ||
len = c->top - head; | ||
lept_set_string(v, lept_context_pop(c, len), len); | ||
c->json = p; | ||
return LEPT_PARSE_OK; | ||
case '\\': | ||
switch (*p++) { | ||
case '\"': PUTC(c, '\"'); break; | ||
case '\\': PUTC(c, '\\'); break; | ||
case '/': PUTC(c, '/' ); break; | ||
case 'b': PUTC(c, '\b'); break; | ||
case 'f': PUTC(c, '\f'); break; | ||
case 'n': PUTC(c, '\n'); break; | ||
case 'r': PUTC(c, '\r'); break; | ||
case 't': PUTC(c, '\t'); break; | ||
default: | ||
c->top = head; | ||
return LEPT_PARSE_INVALID_STRING_ESCAPE; | ||
} | ||
break; | ||
case '\0': | ||
c->top = head; | ||
return LEPT_PARSE_MISS_QUOTATION_MARK; | ||
default: | ||
if ((unsigned char)ch < 0x20) { | ||
c->top = head; | ||
return LEPT_PARSE_INVALID_STRING_CHAR; | ||
} | ||
PUTC(c, ch); | ||
} | ||
} | ||
} | ||
|
||
static int lept_parse_value(lept_context* c, lept_value* v) { | ||
switch (*c->json) { | ||
case 't': return lept_parse_literal(c, v, "true", LEPT_TRUE); | ||
case 'f': return lept_parse_literal(c, v, "false", LEPT_FALSE); | ||
case 'n': return lept_parse_literal(c, v, "null", LEPT_NULL); | ||
default: return lept_parse_number(c, v); | ||
case '"': return lept_parse_string(c, v); | ||
case '\0': return LEPT_PARSE_EXPECT_VALUE; | ||
} | ||
} | ||
|
||
int lept_parse(lept_value* v, const char* json) { | ||
lept_context c; | ||
int ret; | ||
assert(v != NULL); | ||
c.json = json; | ||
c.stack = NULL; | ||
c.size = c.top = 0; | ||
lept_init(v); | ||
lept_parse_whitespace(&c); | ||
if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) { | ||
lept_parse_whitespace(&c); | ||
if (*c.json != '\0') { | ||
v->type = LEPT_NULL; | ||
ret = LEPT_PARSE_ROOT_NOT_SINGULAR; | ||
} | ||
} | ||
assert(c.top == 0); | ||
free(c.stack); | ||
return ret; | ||
} | ||
|
||
void lept_free(lept_value* v) { | ||
assert(v != NULL); | ||
if (v->type == LEPT_STRING) | ||
free(v->u.s.s); | ||
v->type = LEPT_NULL; | ||
} | ||
|
||
lept_type lept_get_type(const lept_value* v) { | ||
assert(v != NULL); | ||
return v->type; | ||
} | ||
|
||
int lept_get_boolean(const lept_value* v) { | ||
assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE)); | ||
return v->type == LEPT_TRUE; | ||
} | ||
|
||
void lept_set_boolean(lept_value* v, int b) { | ||
lept_free(v); | ||
v->type = b ? LEPT_TRUE : LEPT_FALSE; | ||
} | ||
|
||
double lept_get_number(const lept_value* v) { | ||
assert(v != NULL && v->type == LEPT_NUMBER); | ||
return v->u.n; | ||
} | ||
|
||
void lept_set_number(lept_value* v, double n) { | ||
lept_free(v); | ||
v->u.n = n; | ||
v->type = LEPT_NUMBER; | ||
} | ||
|
||
const char* lept_get_string(const lept_value* v) { | ||
assert(v != NULL && v->type == LEPT_STRING); | ||
return v->u.s.s; | ||
} | ||
|
||
size_t lept_get_string_length(const lept_value* v) { | ||
assert(v != NULL && v->type == LEPT_STRING); | ||
return v->u.s.len; | ||
} | ||
|
||
void lept_set_string(lept_value* v, const char* s, size_t len) { | ||
assert(v != NULL && (s != NULL || len == 0)); | ||
lept_free(v); | ||
v->u.s.s = (char*)malloc(len + 1); | ||
memcpy(v->u.s.s, s, len); | ||
v->u.s.s[len] = '\0'; | ||
v->u.s.len = len; | ||
v->type = LEPT_STRING; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#ifndef LEPTJSON_H__ | ||
#define LEPTJSON_H__ | ||
|
||
#include <stddef.h> /* size_t */ | ||
|
||
typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type; | ||
|
||
typedef struct { | ||
union { | ||
struct { char* s; size_t len; }s; /* string: null-terminated string, string length */ | ||
double n; /* number */ | ||
}u; | ||
lept_type type; | ||
}lept_value; | ||
|
||
enum { | ||
LEPT_PARSE_OK = 0, | ||
LEPT_PARSE_EXPECT_VALUE, | ||
LEPT_PARSE_INVALID_VALUE, | ||
LEPT_PARSE_ROOT_NOT_SINGULAR, | ||
LEPT_PARSE_NUMBER_TOO_BIG, | ||
LEPT_PARSE_MISS_QUOTATION_MARK, | ||
LEPT_PARSE_INVALID_STRING_ESCAPE, | ||
LEPT_PARSE_INVALID_STRING_CHAR | ||
}; | ||
|
||
#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0) | ||
|
||
int lept_parse(lept_value* v, const char* json); | ||
|
||
void lept_free(lept_value* v); | ||
|
||
lept_type lept_get_type(const lept_value* v); | ||
|
||
#define lept_set_null(v) lept_free(v) | ||
|
||
int lept_get_boolean(const lept_value* v); | ||
void lept_set_boolean(lept_value* v, int b); | ||
|
||
double lept_get_number(const lept_value* v); | ||
void lept_set_number(lept_value* v, double n); | ||
|
||
const char* lept_get_string(const lept_value* v); | ||
size_t lept_get_string_length(const lept_value* v); | ||
void lept_set_string(lept_value* v, const char* s, size_t len); | ||
|
||
#endif /* LEPTJSON_H__ */ |
Oops, something went wrong.