diff --git a/tutorial01/leptjson.c b/tutorial01/leptjson.c index 5299fe1d..3245c3cd 100644 --- a/tutorial01/leptjson.c +++ b/tutorial01/leptjson.c @@ -24,9 +24,30 @@ static int lept_parse_null(lept_context* c, lept_value* v) { 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 'f': return lept_parse_false(c, v); + case 't': return lept_parse_true(c, v); case '\0': return LEPT_PARSE_EXPECT_VALUE; default: return LEPT_PARSE_INVALID_VALUE; } @@ -34,11 +55,18 @@ static int lept_parse_value(lept_context* c, lept_value* v) { int lept_parse(lept_value* v, const char* json) { lept_context c; + int ret; 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){ + lept_parse_whitespace(&c); + if (*c.json != '\0') + ret = LEPT_PARSE_ROOT_NOT_SINGULAR; + } + return ret; } lept_type lept_get_type(const lept_value* v) { diff --git a/tutorial01/test.c b/tutorial01/test.c index e7672181..00837a4f 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_false() { + lept_value v; + v.type = LEPT_TRUE; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false")); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v)); +} + +static void test_parse_true() { + lept_value v; + v.type = LEPT_FALSE; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); +} + static void test_parse_expect_value() { lept_value v; @@ -59,6 +73,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(); diff --git a/tutorial02/leptjson.c b/tutorial02/leptjson.c index 7693e43b..6ed54431 100644 --- a/tutorial02/leptjson.c +++ b/tutorial02/leptjson.c @@ -1,9 +1,13 @@ #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') +#define MINE 0 typedef struct { const char* json; }lept_context; @@ -15,6 +19,20 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } +static int lept_parse_literal(lept_context* c, lept_value* v, char* key_words, int type){ + EXPECT(c, *key_words); + key_words ++; + do{ + if (*c->json++ != *key_words++) + return LEPT_PARSE_INVALID_VALUE; + } while (*c->json != '\0' && *key_words != '\0'); + if (*key_words != '\0') + return LEPT_PARSE_INVALID_VALUE; + v->type = type; + return LEPT_PARSE_OK; +} + +#if 0 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') @@ -23,7 +41,9 @@ static int lept_parse_true(lept_context* c, lept_value* v) { v->type = LEPT_TRUE; return LEPT_PARSE_OK; } +#endif +#if 0 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') @@ -32,7 +52,9 @@ static int lept_parse_false(lept_context* c, lept_value* v) { v->type = LEPT_FALSE; return LEPT_PARSE_OK; } +#endif +#if 0 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') @@ -41,23 +63,76 @@ static int lept_parse_null(lept_context* c, lept_value* v) { v->type = LEPT_NULL; return LEPT_PARSE_OK; } +#endif static int lept_parse_number(lept_context* c, lept_value* v) { +#if MINE char* end; + int ret = LEPT_PARSE_OK; /* \TODO validate number */ v->n = strtod(c->json, &end); if (c->json == end) return LEPT_PARSE_INVALID_VALUE; - c->json = end; + if (!ISDIGIT(*c->json) && *c->json != '-') + return LEPT_PARSE_INVALID_VALUE; + if (*c->json == '-') + ++c->json; + if (*c->json == '0' && ISDIGIT(*(c->json + 1))){ + v->n = 0.0; + return LEPT_PARSE_ROOT_NOT_SINGULAR; + } + if (*end != NULL) + return LEPT_PARSE_ROOT_NOT_SINGULAR; + while (*c->json++ != '.'); + if (*c->json == '\0') + return LEPT_PARSE_INVALID_VALUE; + if ((v->n == HUGE_VAL || v->n == -HUGE_VAL) && errno == ERANGE) + return LEPT_PARSE_NUMBER_TOO_BIG; + c->json = end; v->type = LEPT_NUMBER; - return LEPT_PARSE_OK; + return ret; +#endif +#if !MINE + char* end; + char* p; + /* \TODO validate number */ + v->n = strtod(c->json, &end); + if (c->json == end) + return LEPT_PARSE_INVALID_VALUE; + 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); + } + if ((v->n == HUGE_VAL || v->n == -HUGE_VAL) && errno == ERANGE) + return LEPT_PARSE_NUMBER_TOO_BIG; + c->json = p; + v->type = LEPT_NUMBER; + return LEPT_PARSE_OK; +#endif } 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 '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 '\0': return LEPT_PARSE_EXPECT_VALUE; } diff --git a/tutorial02/test.c b/tutorial02/test.c index 6e3ebed2..215a02b9 100644 --- a/tutorial02/test.c +++ b/tutorial02/test.c @@ -70,6 +70,22 @@ 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(5E-324, "5E-324"); /*Min subnormal positive double*/ + TEST_NUMBER(1.7976931348623157E308, "1.7976931348623157E+308"); /*Max double*/ + /* the smallest number > 1 */ + TEST_NUMBER(1.0000000000000002, "1.0000000000000002"); + /* minimum denormal */ + TEST_NUMBER(4.9406564584124654e-324, "4.9406564584124654e-324"); + TEST_NUMBER(-4.9406564584124654e-324, "-4.9406564584124654e-324"); + /* Max subnormal double */ + TEST_NUMBER(2.2250738585072009e-308, "2.2250738585072009e-308"); + TEST_NUMBER(-2.2250738585072009e-308, "-2.2250738585072009e-308"); + /* Min normal positive double */ + TEST_NUMBER(2.2250738585072014e-308, "2.2250738585072014e-308"); + TEST_NUMBER(-2.2250738585072014e-308, "-2.2250738585072014e-308"); + /* Max double */ + TEST_NUMBER(1.7976931348623157e+308, "1.7976931348623157e+308"); + TEST_NUMBER(-1.7976931348623157e+308, "-1.7976931348623157e+308"); } #define TEST_ERROR(error, json)\ @@ -89,7 +105,7 @@ static void test_parse_invalid_value() { TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); -#if 0 +#if 1 /* invalid number */ TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+0"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+1"); @@ -105,16 +121,17 @@ static void test_parse_invalid_value() { static void test_parse_root_not_singular() { TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "null x"); -#if 0 +#if 1 /* invalid number */ TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123"); /* after zero should be '.' or nothing */ + TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123."); TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x0"); TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x123"); #endif } static void test_parse_number_too_big() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309"); TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "-1e309"); #endif diff --git a/tutorial03/leptjson.c b/tutorial03/leptjson.c index 07f7e2c7..ab0dad52 100644 --- a/tutorial03/leptjson.c +++ b/tutorial03/leptjson.c @@ -1,3 +1,7 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif #include "leptjson.h" #include /* assert() */ #include /* errno, ERANGE */ @@ -5,6 +9,7 @@ #include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ + #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 #endif @@ -13,6 +18,7 @@ #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) +#define ISUNESCAPED(ch) (((ch) >= 0x20 && (ch) <= 0x21) || ((ch) >= 0x23 && (ch) <= 0x5B) || ((ch) >= 0x5D && (ch) <= 0x10FFFF)) typedef struct { const char* json; @@ -86,6 +92,40 @@ static int lept_parse_number(lept_context* c, lept_value* v) { return LEPT_PARSE_OK; } +static int lept_parse_escape_char(const char* ptr, char* ret) { + char ch = *ptr; + switch (ch) { + case'\"': + *ret = 0x0022; /* " */ + break; + case'\\': + *ret = 0x005C; /* \ */ + break; + case'/': + *ret = 0x002F; /* / */ + break; + case'b': + *ret = 0x0008; /* backspace */ + break; + case'f': + *ret = 0x000C; /* form feed */ + break; + case'n': + *ret = 0x000A; /* line feed */ + break; + case'r': + *ret = 0x000D; /* carriage return */ + break; + case't': + *ret = 0x0009; /* tab */ + break; + default: + return -1; + } + + return 0; +} + static int lept_parse_string(lept_context* c, lept_value* v) { size_t head = c->top, len; const char* p; @@ -102,8 +142,21 @@ static int lept_parse_string(lept_context* c, lept_value* v) { case '\0': c->top = head; return LEPT_PARSE_MISS_QUOTATION_MARK; + case '\\': + if (lept_parse_escape_char(p++, &ch) == 0) + PUTC(c, ch); + else{ + c->top = head; + return LEPT_PARSE_INVALID_STRING_ESCAPE; + } + break; default: - PUTC(c, ch); + if ((unsigned char)ch < 0x20){ + c->json = head; + return LEPT_PARSE_INVALID_STRING_CHAR; + } + else + PUTC(c, ch); } } } @@ -154,11 +207,15 @@ lept_type lept_get_type(const lept_value* v) { int lept_get_boolean(const lept_value* v) { /* \TODO */ - return 0; + 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) { /* \TODO */ + assert(v != NULL); + lept_free(v); + v->type = b; } double lept_get_number(const lept_value* v) { @@ -168,6 +225,10 @@ double lept_get_number(const lept_value* v) { 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/test.c b/tutorial03/test.c index ac788aca..976e3de5 100644 --- a/tutorial03/test.c +++ b/tutorial03/test.c @@ -1,3 +1,7 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif #include #include #include @@ -107,7 +111,7 @@ static void test_parse_number() { static void test_parse_string() { TEST_STRING("", "\"\""); TEST_STRING("Hello", "\"Hello\""); -#if 0 +#if 1 TEST_STRING("Hello\nWorld", "\"Hello\\nWorld\""); TEST_STRING("\" \\ / \b \f \n \r \t", "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); #endif @@ -163,7 +167,7 @@ static void test_parse_missing_quotation_mark() { } static void test_parse_invalid_string_escape() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\v\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\'\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\0\""); @@ -172,7 +176,7 @@ static void test_parse_invalid_string_escape() { } static void test_parse_invalid_string_char() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x01\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x1F\""); #endif @@ -190,10 +194,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_string(&v, "a", 1); + lept_set_boolean(&v, LEPT_TRUE); + EXPECT_TRUE(lept_get_boolean(&v)); + lept_set_boolean(&v, LEPT_FALSE); + 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, 3.14); + EXPECT_EQ_DOUBLE(3.14, lept_get_number(&v)); + lept_free(&v); } static void test_access_string() { @@ -227,6 +245,9 @@ static void test_parse() { } int main() { +#ifdef _WINDOWS + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif test_parse(); printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count); return main_ret;