Skip to content

Commit 5b61b8f

Browse files
committed
Add tutorial03
1 parent 18b5b04 commit 5b61b8f

File tree

9 files changed

+774
-1
lines changed

9 files changed

+774
-1
lines changed

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
1. [启程](tutorial01/tutorial01.md)(2016/9/15 完成):编译环境、JSON 简介、测试驱动、解析器主要函数及各数据结构。练习 JSON 布尔类型的解析。[启程解答编](tutorial01_answer/tutorial01_answer.md)(2016/9/17 完成)。
4242
2. [解析数字](tutorial02/tutorial02.md)(2016/9/18 完成):JSON number 的语法。练习 JSON number 类型的校验。[解析数字解答编](tutorial02_answer/tutorial02_answer.md)(2016/9/20 完成)。
43-
3. 解析字符串:使用 union 存储 variant、自动扩展的堆栈、JSON string 的语法、valgrind。练习最基本的 JSON string 类型的解析、内存释放。
43+
3. [解析字符串](tutorial03/tutorial03.md)(2016/9/22 完成):使用 union 存储 variant、自动扩展的堆栈、JSON string 的语法、valgrind。练习最基本的 JSON string 类型的解析、内存释放。
4444
4. Unicode:Unicode 和 UTF-8 的基本知识、JSON string 的 unicode 处理。练习完成 JSON string 类型的解析。
4545
5. 解析数组:JSON array 的语法。练习完成 JSON array 类型的解析、相关内存释放。
4646
6. 解析对象:JSON object 的语法、重构 string 解析函数。练习完成 JSON object 的解析、相关内存释放。

tutorial03/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
cmake_minimum_required (VERSION 2.6)
2+
project (leptjson_test C)
3+
4+
if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
5+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -pedantic -Wall")
6+
endif()
7+
8+
add_library(leptjson leptjson.c)
9+
add_executable(leptjson_test test.c)
10+
target_link_libraries(leptjson_test leptjson)

tutorial03/images/makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
%.png: %.dot
2+
dot $< -Tpng -o $@
3+
4+
DOTFILES = $(basename $(wildcard *.dot))
5+
all: $(addsuffix .png, $(DOTFILES))

tutorial03/images/union_layout.dot

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
digraph {
2+
rankdir=LR
3+
compound=true
4+
fontname="Inconsolata, Consolas"
5+
fontsize=10
6+
margin="0,0"
7+
ranksep=0.5
8+
nodesep=1
9+
penwidth=0.5
10+
11+
node [shape=Mrecord, fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5, style=filled, colorscheme=spectral7]
12+
edge [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
13+
14+
old [fillcolor=6, label="double n (8 bytes) |const char* s (4 bytes) |size_t len (4 bytes)|int type (4 bytes)"]
15+
new [fillcolor=5, label="{double n (8 bytes)|{const char* s (4 bytes)|size_t len (4 bytes)}}|int type (4 bytes)"]
16+
old -> new [style=invis]
17+
}

tutorial03/images/union_layout.png

21.7 KB
Loading

tutorial03/leptjson.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#include "leptjson.h"
2+
#include <assert.h> /* assert() */
3+
#include <errno.h> /* errno, ERANGE */
4+
#include <math.h> /* HUGE_VAL */
5+
#include <stdlib.h> /* NULL, malloc(), realloc(), free(), strtod() */
6+
#include <string.h> /* memcpy() */
7+
8+
#ifndef LEPT_PARSE_STACK_INIT_SIZE
9+
#define LEPT_PARSE_STACK_INIT_SIZE 256
10+
#endif
11+
12+
#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)
13+
#define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9')
14+
#define ISDIGIT1TO9(ch) ((ch) >= '1' && (ch) <= '9')
15+
#define PUTC(c, ch) do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)
16+
17+
typedef struct {
18+
const char* json;
19+
char* stack;
20+
size_t size, top;
21+
}lept_context;
22+
23+
static void* lept_context_push(lept_context* c, size_t size) {
24+
void* ret;
25+
assert(size > 0);
26+
if (c->top + size >= c->size) {
27+
if (c->size == 0)
28+
c->size = LEPT_PARSE_STACK_INIT_SIZE;
29+
while (c->top + size >= c->size)
30+
c->size += c->size >> 1; /* c->size * 1.5 */
31+
c->stack = (char*)realloc(c->stack, c->size);
32+
}
33+
ret = c->stack + c->top;
34+
c->top += size;
35+
return ret;
36+
}
37+
38+
static void* lept_context_pop(lept_context* c, size_t size) {
39+
assert(c->top >= size);
40+
return c->stack + (c->top -= size);
41+
}
42+
43+
static void lept_parse_whitespace(lept_context* c) {
44+
const char *p = c->json;
45+
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
46+
p++;
47+
c->json = p;
48+
}
49+
50+
static int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {
51+
size_t i;
52+
EXPECT(c, literal[0]);
53+
for (i = 0; literal[i + 1]; i++)
54+
if (c->json[i] != literal[i + 1])
55+
return LEPT_PARSE_INVALID_VALUE;
56+
c->json += i;
57+
v->type = type;
58+
return LEPT_PARSE_OK;
59+
}
60+
61+
static int lept_parse_number(lept_context* c, lept_value* v) {
62+
const char* p = c->json;
63+
if (*p == '-') p++;
64+
if (*p == '0') p++;
65+
else {
66+
if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;
67+
for (p++; ISDIGIT(*p); p++);
68+
}
69+
if (*p == '.') {
70+
p++;
71+
if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;
72+
for (p++; ISDIGIT(*p); p++);
73+
}
74+
if (*p == 'e' || *p == 'E') {
75+
p++;
76+
if (*p == '+' || *p == '-') p++;
77+
if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;
78+
for (p++; ISDIGIT(*p); p++);
79+
}
80+
errno = 0;
81+
v->u.n = strtod(c->json, NULL);
82+
if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))
83+
return LEPT_PARSE_NUMBER_TOO_BIG;
84+
v->type = LEPT_NUMBER;
85+
c->json = p;
86+
return LEPT_PARSE_OK;
87+
}
88+
89+
static int lept_parse_string(lept_context* c, lept_value* v) {
90+
size_t head = c->top, len;
91+
const char* p;
92+
EXPECT(c, '\"');
93+
p = c->json;
94+
for (;;) {
95+
char ch = *p++;
96+
switch (ch) {
97+
case '\"':
98+
len = c->top - head;
99+
lept_set_string(v, lept_context_pop(c, len), len);
100+
c->json = p;
101+
return LEPT_PARSE_OK;
102+
case '\0':
103+
c->top = head;
104+
return LEPT_PARSE_MISS_QUOTATION_MARK;
105+
default:
106+
PUTC(c, ch);
107+
}
108+
}
109+
}
110+
111+
static int lept_parse_value(lept_context* c, lept_value* v) {
112+
switch (*c->json) {
113+
case 't': return lept_parse_literal(c, v, "true", LEPT_TRUE);
114+
case 'f': return lept_parse_literal(c, v, "false", LEPT_FALSE);
115+
case 'n': return lept_parse_literal(c, v, "null", LEPT_NULL);
116+
default: return lept_parse_number(c, v);
117+
case '"': return lept_parse_string(c, v);
118+
case '\0': return LEPT_PARSE_EXPECT_VALUE;
119+
}
120+
}
121+
122+
int lept_parse(lept_value* v, const char* json) {
123+
lept_context c;
124+
int ret;
125+
assert(v != NULL);
126+
c.json = json;
127+
c.stack = NULL;
128+
c.size = c.top = 0;
129+
lept_init(v);
130+
lept_parse_whitespace(&c);
131+
if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {
132+
lept_parse_whitespace(&c);
133+
if (*c.json != '\0') {
134+
v->type = LEPT_NULL;
135+
ret = LEPT_PARSE_ROOT_NOT_SINGULAR;
136+
}
137+
}
138+
assert(c.top == 0);
139+
free(c.stack);
140+
return ret;
141+
}
142+
143+
void lept_free(lept_value* v) {
144+
assert(v != NULL);
145+
if (v->type == LEPT_STRING)
146+
free(v->u.s.s);
147+
v->type = LEPT_NULL;
148+
}
149+
150+
lept_type lept_get_type(const lept_value* v) {
151+
assert(v != NULL);
152+
return v->type;
153+
}
154+
155+
int lept_get_boolean(const lept_value* v) {
156+
/* \TODO */
157+
return 0;
158+
}
159+
160+
void lept_set_boolean(lept_value* v, int b) {
161+
/* \TODO */
162+
}
163+
164+
double lept_get_number(const lept_value* v) {
165+
assert(v != NULL && v->type == LEPT_NUMBER);
166+
return v->u.n;
167+
}
168+
169+
void lept_set_number(lept_value* v, double n) {
170+
/* \TODO */
171+
}
172+
173+
const char* lept_get_string(const lept_value* v) {
174+
assert(v != NULL && v->type == LEPT_STRING);
175+
return v->u.s.s;
176+
}
177+
178+
size_t lept_get_string_length(const lept_value* v) {
179+
assert(v != NULL && v->type == LEPT_STRING);
180+
return v->u.s.len;
181+
}
182+
183+
void lept_set_string(lept_value* v, const char* s, size_t len) {
184+
assert(v != NULL && (s != NULL || len == 0));
185+
lept_free(v);
186+
v->u.s.s = (char*)malloc(len + 1);
187+
memcpy(v->u.s.s, s, len);
188+
v->u.s.s[len] = '\0';
189+
v->u.s.len = len;
190+
v->type = LEPT_STRING;
191+
}

tutorial03/leptjson.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef LEPTJSON_H__
2+
#define LEPTJSON_H__
3+
4+
#include <stddef.h> /* size_t */
5+
6+
typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;
7+
8+
typedef struct {
9+
union {
10+
struct { char* s; size_t len; }s; /* string: null-terminated string, string length */
11+
double n; /* number */
12+
}u;
13+
lept_type type;
14+
}lept_value;
15+
16+
enum {
17+
LEPT_PARSE_OK = 0,
18+
LEPT_PARSE_EXPECT_VALUE,
19+
LEPT_PARSE_INVALID_VALUE,
20+
LEPT_PARSE_ROOT_NOT_SINGULAR,
21+
LEPT_PARSE_NUMBER_TOO_BIG,
22+
LEPT_PARSE_MISS_QUOTATION_MARK,
23+
LEPT_PARSE_INVALID_STRING_ESCAPE,
24+
LEPT_PARSE_INVALID_STRING_CHAR
25+
};
26+
27+
#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)
28+
29+
int lept_parse(lept_value* v, const char* json);
30+
31+
void lept_free(lept_value* v);
32+
33+
lept_type lept_get_type(const lept_value* v);
34+
35+
#define lept_set_null(v) lept_free(v)
36+
37+
int lept_get_boolean(const lept_value* v);
38+
void lept_set_boolean(lept_value* v, int b);
39+
40+
double lept_get_number(const lept_value* v);
41+
void lept_set_number(lept_value* v, double n);
42+
43+
const char* lept_get_string(const lept_value* v);
44+
size_t lept_get_string_length(const lept_value* v);
45+
void lept_set_string(lept_value* v, const char* s, size_t len);
46+
47+
#endif /* LEPTJSON_H__ */

0 commit comments

Comments
 (0)