Skip to content

Commit 887c2e4

Browse files
committed
my homework01
1 parent 97a11ee commit 887c2e4

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

my_note.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## tutorial01学习笔记
2+
3+
要点:
4+
5+
* CMake
6+
* API设计(.h)与实现(.c)
7+
* 测试驱动TDD
8+
9+
### 关于测试驱动的开发流程
10+
11+
1. 加入一个测试
12+
2. 运行所有测试,新测试将会失败
13+
3. 编写实现代码
14+
4. 运行所有测试,若测试失败,回到3
15+
5. 重构代码
16+
6. 回到1
17+
18+
TDD是先写测试后实现,优点:需求明确,刚好满足测试,不会写不需要的代码,可靠。
19+
20+
不论TDD还是先实现后测试,都应该保证足够的覆盖率的单元测试。
21+
22+
我认为可以写一个test.h,设计一些基础的宏,然后每个文件写对应的测试用例。本代码为了简便只有test.c
23+
24+
### 学到的知识
25+
milo yip的代码写得极好!思路非常清晰且层次分明,我试着还原一下思路,
26+
27+
#### API的设计思路
28+
29+
(1)先考虑最终的API效果
30+
31+
首先是宏观的总体目标,是对外的pulic API
32+
33+
`int lept_parse(lept_value* v, const char* json);`
34+
35+
parse取词的话,返回值用于表示结果对错,参数有两个,out为一个struct lept_value,用于保存结果,in为字符串。
36+
37+
那么这个数据结构里面的内容呢?这个后续交代,
38+
39+
想合理地表达null,boolean,string,array,object,float这六种JSON类型,可能的struct应该会是:
40+
41+
``` c
42+
typedef enum { LEPT_NULL, LEPT_TRUE, LEPT_FALSE, LEPT_ARRAY, LEPT_OBJECT, LEPT_FLOAT } lept_type;
43+
typedef struct lept_value_t {
44+
lept_type t; //保存数据类型
45+
} lept_value;
46+
```
47+
48+
注意这里的lept_value数据结构只包含一个枚举值,这是null和boolean特有的简化版,
49+
50+
如果是array或者其他需要修改此结构,对此我还没有比较好的思路,因为如果子结构包含信息过多,对于null和boolean而言又略显多余。
51+
52+
如果是一个较少信息量的类型,估计应该是加入一个`void*`类型指针,用于指向特定结构,例如string指向字符串,array指向`lept_value_t*`数值.
53+
54+
是否需要size等信息,拭目以待,我觉得如果不放在lept_value_t里面也会放在子类型里面。可能放在子类型里面更好,毕竟很多类型不需要。
55+
56+
(2)余下内容太多,自行体会吧,不写了
57+
58+
59+
#### TDD的设计思路
60+
首先写一个test的宏来做测试,为什么要用宏呢?因为c没有泛型编程嘛。
61+
62+
**Milo的解答中说,原因是为了出错后显示的__LINE__宏找到对应位置,而不是每次都在该函数一个位置**
63+
64+
Milo Yip用了 EXPECT_EQ_BASE这个宏作为EXPECT_EQ_INT的辅助宏非常巧妙,
65+
具体参见`test.c`
66+
67+
EXPECT_EQ_BASE中包含了与类型无关的内容,用format还获得实际不同类型的情况,
68+
以及equality表达式更像是一个谓词Predicate,这样不仅可用于相等表达式。
69+
70+
我个人认为这种通用性强的代码应该在重构中写,实现的时候暂时别想这么复杂。
71+
72+
#### 解答篇收获
73+
74+

tutorial01/leptjson.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
#include <assert.h> /* assert() */
33
#include <stdlib.h> /* NULL */
44

5+
/* 这个EXPECT有副作用啊,而不是单纯地assert,意味着EXPECT不能注释掉。*/
56
#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)
67

8+
/* 因为此字符串多次使用,为了减少传递多个参数,故放到一个struct中 */
79
typedef struct {
810
const char* json;
911
}lept_context;
@@ -20,13 +22,49 @@ static int lept_parse_null(lept_context* c, lept_value* v) {
2022
if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')
2123
return LEPT_PARSE_INVALID_VALUE;
2224
c->json += 3;
25+
26+
lept_parse_whitespace(c);
27+
if (c->json[0] != '\0')
28+
return LEPT_PARSE_ROOT_NOT_SINGULAR;
29+
2330
v->type = LEPT_NULL;
2431
return LEPT_PARSE_OK;
2532
}
2633

34+
35+
static int lept_parse_false(lept_context* c, lept_value* v) {
36+
EXPECT(c, 'f');
37+
if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')
38+
return LEPT_PARSE_INVALID_VALUE;
39+
c->json += 4;
40+
41+
lept_parse_whitespace(c);
42+
if (c->json[0] != '\0')
43+
return LEPT_PARSE_ROOT_NOT_SINGULAR;
44+
45+
v->type = LEPT_FALSE;
46+
return LEPT_PARSE_OK;
47+
}
48+
49+
static int lept_parse_true(lept_context* c, lept_value* v) {
50+
EXPECT(c, 't');
51+
if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
52+
return LEPT_PARSE_INVALID_VALUE;
53+
c->json += 3;
54+
55+
lept_parse_whitespace(c);
56+
if (c->json[0] != '\0')
57+
return LEPT_PARSE_ROOT_NOT_SINGULAR;
58+
59+
v->type = LEPT_TRUE;
60+
return LEPT_PARSE_OK;
61+
}
62+
2763
static int lept_parse_value(lept_context* c, lept_value* v) {
2864
switch (*c->json) {
2965
case 'n': return lept_parse_null(c, v);
66+
case 'f': return lept_parse_false(c, v);
67+
case 't': return lept_parse_true(c, v);
3068
case '\0': return LEPT_PARSE_EXPECT_VALUE;
3169
default: return LEPT_PARSE_INVALID_VALUE;
3270
}

tutorial01/test.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ static void test_parse_null() {
2525
v.type = LEPT_FALSE;
2626
EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null"));
2727
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
28+
29+
v.type = LEPT_FALSE;
30+
EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true"));
31+
EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));
32+
33+
v.type = LEPT_TRUE;
34+
EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false"));
35+
EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));
2836
}
2937

3038
static void test_parse_expect_value() {
@@ -48,6 +56,14 @@ static void test_parse_invalid_value() {
4856
v.type = LEPT_FALSE;
4957
EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "?"));
5058
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
59+
60+
v.type = LEPT_FALSE;
61+
EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "tru"));
62+
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
63+
64+
v.type = LEPT_FALSE;
65+
EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "fals"));
66+
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
5167
}
5268

5369
static void test_parse_root_not_singular() {

0 commit comments

Comments
 (0)