From 80ae2004b38e3beab8658ff7abfd55f8ebfe7676 Mon Sep 17 00:00:00 2001 From: dennis zhuang Date: Tue, 3 Jan 2017 09:03:25 +0800 Subject: [PATCH 1/3] (feat) tutorial01 --- tutorial01/leptjson.c | 61 +++++++++++++++++++++++++++++++++++++++++-- tutorial01/test.c | 24 +++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/tutorial01/leptjson.c b/tutorial01/leptjson.c index 5299fe1d..3ae5f3dd 100644 --- a/tutorial01/leptjson.c +++ b/tutorial01/leptjson.c @@ -1,6 +1,7 @@ #include "leptjson.h" #include /* assert() */ #include /* NULL */ +#include #define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) @@ -15,6 +16,13 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } +static int lept_parse_end(lept_context* c) { + if (*c->json != '\0') + return LEPT_PARSE_ROOT_NOT_SINGULAR; + else + return LEPT_PARSE_OK; +} + static int lept_parse_null(lept_context* c, lept_value* v) { EXPECT(c, 'n'); if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') @@ -24,21 +32,70 @@ static int lept_parse_null(lept_context* c, lept_value* v) { return LEPT_PARSE_OK; } +static int lept_parse_literal(lept_context* c, lept_type t, + char* l, lept_value* v) { + int i = 0; + const char *p = c->json; + + while (l[i] == p[i] && l[i] != '\0' + && p[i] != '\0') + i++; + + if(l[i] != '\0') + return LEPT_PARSE_INVALID_VALUE; + + c->json += i; + v->type = t; + + return LEPT_PARSE_OK; +} + +static int lept_parse_true(lept_context* c, lept_value* v) { + EXPECT(c, 't'); + if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e') + return LEPT_PARSE_INVALID_VALUE; + c->json += 3; + v->type = LEPT_TRUE; + return LEPT_PARSE_OK; +} + +static int lept_parse_false(lept_context* c, lept_value* v) { + EXPECT(c, 'f'); + if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' + || c->json[3] != 'e') + return LEPT_PARSE_INVALID_VALUE; + c->json += 4; + v->type = LEPT_FALSE; + return LEPT_PARSE_OK; +} + static int lept_parse_value(lept_context* c, lept_value* v) { switch (*c->json) { - case 'n': return lept_parse_null(c, v); + case 'n': return lept_parse_literal(c, LEPT_NULL, "null", v); + case 't': return lept_parse_literal(c, LEPT_TRUE, "true", v); + case 'f': return lept_parse_literal(c, LEPT_FALSE, "false", v); case '\0': return LEPT_PARSE_EXPECT_VALUE; default: return LEPT_PARSE_INVALID_VALUE; } } int lept_parse(lept_value* v, const char* json) { + + int ret; + lept_context c; assert(v != NULL); c.json = json; v->type = LEPT_NULL; lept_parse_whitespace(&c); - return lept_parse_value(&c, v); + + ret = lept_parse_value(&c, v); + if (ret != LEPT_PARSE_OK) { + return ret; + } else { + lept_parse_whitespace(&c); + return lept_parse_end(&c); + } } lept_type lept_get_type(const lept_value* v) { diff --git a/tutorial01/test.c b/tutorial01/test.c index e7672181..04a12db1 100644 --- a/tutorial01/test.c +++ b/tutorial01/test.c @@ -27,6 +27,20 @@ static void test_parse_null() { EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); } +static void test_parse_true() { + lept_value v; + v.type = LEPT_NULL; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); +} + +static void test_parse_false() { + lept_value v; + v.type = LEPT_NULL; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false")); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v)); +} + static void test_parse_expect_value() { lept_value v; @@ -48,6 +62,14 @@ static void test_parse_invalid_value() { v.type = LEPT_FALSE; EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "?")); EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + + v.type = LEPT_FALSE; + EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "tru")); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + + v.type = LEPT_FALSE; + EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "fals")); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); } static void test_parse_root_not_singular() { @@ -59,6 +81,8 @@ static void test_parse_root_not_singular() { static void test_parse() { test_parse_null(); + test_parse_true(); + test_parse_false(); test_parse_expect_value(); test_parse_invalid_value(); test_parse_root_not_singular(); From 969f0e0bbd47de92f1c2e7e4a9eb0fb863e02189 Mon Sep 17 00:00:00 2001 From: dennis zhuang Date: Wed, 4 Jan 2017 11:48:09 +0800 Subject: [PATCH 2/3] (feat) p2 --- tutorial02/leptjson.c | 91 +++++++++++++++++++++++++++++-------------- tutorial02/test.c | 11 ++++-- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/tutorial02/leptjson.c b/tutorial02/leptjson.c index 7693e43b..ff2c026e 100644 --- a/tutorial02/leptjson.c +++ b/tutorial02/leptjson.c @@ -1,8 +1,12 @@ #include "leptjson.h" #include /* assert() */ #include /* NULL, strtod() */ +#include +#include #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') typedef struct { const char* json; @@ -15,49 +19,78 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } -static int lept_parse_true(lept_context* c, lept_value* v) { - EXPECT(c, 't'); - if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e') - return LEPT_PARSE_INVALID_VALUE; - c->json += 3; - v->type = LEPT_TRUE; +static int lept_parse_literal(lept_context* c, lept_type t, + char* l, lept_value* v) { + int i = 0; + const char *p = c->json; + + while (l[i] == p[i] && l[i] != '\0' + && p[i] != '\0') + i++; + + if(l[i] != '\0') + return LEPT_PARSE_INVALID_VALUE; + + c->json += i; + v->type = t; + return LEPT_PARSE_OK; } -static int lept_parse_false(lept_context* c, lept_value* v) { - EXPECT(c, 'f'); - if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') +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)) + while(ISDIGIT(*p)) + p++; + else return LEPT_PARSE_INVALID_VALUE; - c->json += 4; - v->type = LEPT_FALSE; - return LEPT_PARSE_OK; -} + } -static int lept_parse_null(lept_context* c, lept_value* v) { - EXPECT(c, 'n'); - if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') + if (*p == '.') { + p++; + if(ISDIGIT(*p)) + while(ISDIGIT(*p)) + p++; + else return LEPT_PARSE_INVALID_VALUE; - c->json += 3; - v->type = LEPT_NULL; - return LEPT_PARSE_OK; -} + } -static int lept_parse_number(lept_context* c, lept_value* v) { - char* end; - /* \TODO validate number */ - v->n = strtod(c->json, &end); - if (c->json == end) + if (*p == 'e' || *p == 'E') { + p++; + if (*p == '+' || *p == '-') + p++; + + if(ISDIGIT(*p)) + while(ISDIGIT(*p)) + p++; + else return LEPT_PARSE_INVALID_VALUE; - c->json = end; + } + + errno = 0; + v->n = strtod(c->json, NULL); + if (errno == ERANGE && (v->n == HUGE_VAL || v->n == -HUGE_VAL)) + return LEPT_PARSE_NUMBER_TOO_BIG; + + c->json = p; + v->type = LEPT_NUMBER; return LEPT_PARSE_OK; } static int lept_parse_value(lept_context* c, lept_value* v) { switch (*c->json) { - case 't': return lept_parse_true(c, v); - case 'f': return lept_parse_false(c, v); - case 'n': return lept_parse_null(c, v); + case 'n': return lept_parse_literal(c, LEPT_NULL, "null", v); + case 't': return lept_parse_literal(c, LEPT_TRUE, "true", v); + case 'f': return lept_parse_literal(c, LEPT_FALSE, "false", v); default: return lept_parse_number(c, v); case '\0': return LEPT_PARSE_EXPECT_VALUE; } diff --git a/tutorial02/test.c b/tutorial02/test.c index 6e3ebed2..0831343b 100644 --- a/tutorial02/test.c +++ b/tutorial02/test.c @@ -70,6 +70,9 @@ static void test_parse_number() { TEST_NUMBER(1.234E+10, "1.234E+10"); TEST_NUMBER(1.234E-10, "1.234E-10"); TEST_NUMBER(0.0, "1e-10000"); /* must underflow */ + TEST_NUMBER(1.0000000000000002, "1.0000000000000002"); + TEST_NUMBER(2.2250738585072009E-308, "2.2250738585072009E-308"); + TEST_NUMBER(1.7976931348623157E308, "1.7976931348623157E308"); } #define TEST_ERROR(error, json)\ @@ -87,19 +90,21 @@ static void test_parse_expect_value() { static void test_parse_invalid_value() { TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "fase"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "tru"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); -#if 0 /* invalid number */ TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+0"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+1"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, ".123"); /* at least one digit before '.' */ TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "1."); /* at least one digit after '.' */ + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "1e"); /* at least one digit after 'e' */ + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "1E"); /* at least one digit after 'E' */ TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "INF"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "inf"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "NAN"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nan"); -#endif } static void test_parse_root_not_singular() { @@ -114,10 +119,8 @@ static void test_parse_root_not_singular() { } static void test_parse_number_too_big() { -#if 0 TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309"); TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "-1e309"); -#endif } static void test_parse() { From 36acae4b04c4e446495d816bebf70d917a41dbc7 Mon Sep 17 00:00:00 2001 From: dennis zhuang Date: Thu, 12 Jan 2017 23:08:46 +0800 Subject: [PATCH 3/3] (feat) answer 4 --- tutorial03/leptjson.c | 44 ++++++++++++++++++--- tutorial03/leptjson.h | 10 +++-- tutorial03/test.c | 24 +++++++----- tutorial04/leptjson.c | 75 ++++++++++++++++++++++++++++++++++-- tutorial04_answer/leptjson.c | 2 +- 5 files changed, 134 insertions(+), 21 deletions(-) diff --git a/tutorial03/leptjson.c b/tutorial03/leptjson.c index 07f7e2c7..3fdaac28 100644 --- a/tutorial03/leptjson.c +++ b/tutorial03/leptjson.c @@ -102,7 +102,27 @@ static int lept_parse_string(lept_context* c, lept_value* v) { case '\0': c->top = head; return LEPT_PARSE_MISS_QUOTATION_MARK; + case '\\': + ch = *p++; + switch(ch){ + 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; default: + if ((unsigned char)ch < 0x20) { + c->top = head; + return LEPT_PARSE_INVALID_STRING_CHAR; + } PUTC(c, ch); } } @@ -153,21 +173,35 @@ lept_type lept_get_type(const lept_value* v) { } int lept_get_boolean(const lept_value* v) { - /* \TODO */ + assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE)); + + if (v->type == LEPT_TRUE) + return 1; + else return 0; } void lept_set_boolean(lept_value* v, int b) { - /* \TODO */ + assert(v != NULL); + + lept_free(v); + if (b) + v->type = LEPT_TRUE; + else + v->type = LEPT_FALSE; } double lept_get_number(const lept_value* v) { - assert(v != NULL && v->type == LEPT_NUMBER); - return v->u.n; + assert(v != NULL && v->type == LEPT_NUMBER); + return v->u.n; } void lept_set_number(lept_value* v, double n) { - /* \TODO */ + assert(v != NULL); + + lept_free(v); + v->u.n = n; + v->type = LEPT_NUMBER; } const char* lept_get_string(const lept_value* v) { diff --git a/tutorial03/leptjson.h b/tutorial03/leptjson.h index d1d4e9d1..332138c8 100644 --- a/tutorial03/leptjson.h +++ b/tutorial03/leptjson.h @@ -3,12 +3,16 @@ #include /* size_t */ -typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type; +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 */ + struct { + char* s; + size_t len; + }s; /* string: null-terminated string, string length */ + double n; /* number */ }u; lept_type type; }lept_value; diff --git a/tutorial03/test.c b/tutorial03/test.c index ac788aca..39e99fc2 100644 --- a/tutorial03/test.c +++ b/tutorial03/test.c @@ -107,10 +107,8 @@ static void test_parse_number() { static void test_parse_string() { TEST_STRING("", "\"\""); TEST_STRING("Hello", "\"Hello\""); -#if 0 TEST_STRING("Hello\nWorld", "\"Hello\\nWorld\""); TEST_STRING("\" \\ / \b \f \n \r \t", "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); -#endif } #define TEST_ERROR(error, json)\ @@ -163,19 +161,15 @@ static void test_parse_missing_quotation_mark() { } static void test_parse_invalid_string_escape() { -#if 0 TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\v\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\'\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\0\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\x12\""); -#endif } static void test_parse_invalid_string_char() { -#if 0 TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x01\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x1F\""); -#endif } static void test_access_null() { @@ -188,12 +182,24 @@ static void test_access_null() { } static void test_access_boolean() { - /* \TODO */ - /* Use EXPECT_TRUE() and EXPECT_FALSE() */ + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 1); + EXPECT_TRUE(lept_get_boolean(&v)); + lept_set_boolean(&v, 0); + EXPECT_FALSE(lept_get_boolean(&v)); + lept_free(&v); } static void test_access_number() { - /* \TODO */ + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_number(&v, 0.0); + EXPECT_EQ_DOUBLE(0.0, lept_get_number(&v)); + lept_set_number(&v, 3.141592); + EXPECT_EQ_DOUBLE(3.141592, lept_get_number(&v)); + lept_free(&v); } static void test_access_string() { diff --git a/tutorial04/leptjson.c b/tutorial04/leptjson.c index 0a123bf2..c574bc05 100644 --- a/tutorial04/leptjson.c +++ b/tutorial04/leptjson.c @@ -1,3 +1,4 @@ + #ifdef _WINDOWS #define _CRTDBG_MAP_ALLOC #include @@ -8,6 +9,7 @@ #include /* HUGE_VAL */ #include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ +#include #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 @@ -90,13 +92,76 @@ static int lept_parse_number(lept_context* c, lept_value* v) { return LEPT_PARSE_OK; } +static int get_n (const char* x, unsigned* n) { + if (*x >= '0' && *x <= '9') { + *n = *x - '0'; + return 0; + } + if (*x >= 'a' && *x <= 'f') { + *n = *x - 'a' + 10; + return 0; + } + if (*x >= 'A' && *x <= 'F') { + *n = *x - 'A' + 10; + return 0; + } + return 1; +} + static const char* lept_parse_hex4(const char* p, unsigned* u) { - /* \TODO */ + int i, j; + unsigned n; + + n = *u = 0; + + for (i=3; i>=0; i--) { + if (get_n(p, &n)){ + return NULL; + } + for(j=0; j= 0xDC00 && l <= 0xDFFF){ + *u = 0x10000 + (*u - 0xD800) * 0x400 + (l - 0xDC00); + return p; + } else + return NULL; +} + static void lept_encode_utf8(lept_context* c, unsigned u) { - /* \TODO */ + printf("%d\n", u); + if (u <= 0x007E) { + PUTC(c, u & 0xFF); + } else if (u >= 0x0080 && u <= 0x07FF) { + PUTC(c, 0xC0 | ((u >> 6) & 0xFF)); /* 0xC0 = 11000000 */ + PUTC(c, 0x80 | ( u & 0x3F)); /* 0x80 = 10000000 */ + } else if (u >= 0x0800 && u <= 0xFFFF) { + PUTC(c, 0xE0 | ((u >> 12) & 0xFF)); /* 0xE0 = 11100000 */ + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); /* 0x80 = 10000000 */ + PUTC(c, 0x80 | ( u & 0x3F)); /* 0x3F = 00111111 */ + } else if (u >= 0x10000 && u <= 0x10FFFF) { + PUTC(c, 0xF0 | ((u >> 18) & 0xFF)); /* 0xF0 = 11110000 */ + PUTC(c, 0x80 | ((u >> 12) & 0x3F)); /* 0x80 = 10000000 */ + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); /* 0x80 = 10000000 */ + PUTC(c, 0x80 | ( u & 0x3F)); /* 0x3F = 00111111 */ + } else{ + /* TODO */ + } } #define STRING_ERROR(ret) do { c->top = head; return ret; } while(0) @@ -128,7 +193,11 @@ static int lept_parse_string(lept_context* c, lept_value* v) { case 'u': if (!(p = lept_parse_hex4(p, &u))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); - /* \TODO surrogate handling */ + if (u >= 0xD800 && u <= 0xDBFF) { + if(!(p = lept_parse_surrogate(p, &u))){ + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + } + } lept_encode_utf8(c, u); break; default: diff --git a/tutorial04_answer/leptjson.c b/tutorial04_answer/leptjson.c index 590d1220..8fdc824c 100644 --- a/tutorial04_answer/leptjson.c +++ b/tutorial04_answer/leptjson.c @@ -105,7 +105,7 @@ static const char* lept_parse_hex4(const char* p, unsigned* u) { } static void lept_encode_utf8(lept_context* c, unsigned u) { - if (u <= 0x7F) + if (u <= 0x7F) PUTC(c, u & 0xFF); else if (u <= 0x7FF) { PUTC(c, 0xC0 | ((u >> 6) & 0xFF));