diff options
author | Joey Adams | 2011-03-28 05:18:04 +0000 |
---|---|---|
committer | Joey Adams | 2011-03-28 14:51:33 +0000 |
commit | efe9481e0a005efae7ad54648af1327fdc09ad01 (patch) | |
tree | 25474bea1a9d42e142831884f285db47397c6b0e | |
parent | ade7ee9de18622821f6fa79bd309fe938669a255 (diff) |
json.c: Added explanation of parsing functions/macros
-rw-r--r-- | json.c | 83 |
1 files changed, 61 insertions, 22 deletions
@@ -31,15 +31,51 @@ static void append_indent(StringInfo buf, const char *space, size_t space_len int indent); /* -In all of these parsers, s is the parsing cursor, and also used to indicate failure. -e is the pointer referring to the end of the text (or "slice", if you will). - -Functions and macros starting with expect_ return a pointer to the end of the accepted -fragment. The other macros update s statefully. -*/ + * Parsing functions and macros + * + * The functions and macros that follow are used to simplify the implementation + * of the recursive descent parser used for JSON validation. See the + * implementation of expect_object() for a good example of them in action. + * + * These functions/macros use a few unifying concepts: + * + * * const char *s and const char *e form a "slice" within a string, + * where s points to the first character and e points after the last + * character. Hence, the length of a slice is e - s. Although it would be + * simpler to just get rid of const char *e and rely on strings being + * null-terminated, varlena texts are not guaranteed to be null-terminated, + * meaning we would have to copy them to parse them. + * + * * Every expect_* function sees if the beginning of a slice matches what it + * expects, and returns the end of the slice on success. To illustrate: + * + * s expect_number(s, e) e + * | | | + * {"pi": 3.14159265, "e": 2.71828183, "phi": 1.61803399} + * + * * When a parse error occurs, s is set to NULL. Moreover, the parser + * functions and macros check to see if s is NULL before using it. + * This means parser functions built entirely of parser functions can proceed + * with the illusion that the input will always be valid, rather than having + * to do a NULL check on every line (see expect_number, which has no explicit + * checks). However, one must ensure that the parser will always halt, + * even in the NULL case. + * + * Bear in mind that while pop_*, optional_*, and skip_* update s, + * the expect_* functions do not. Example: + * + * s = expect_char(s, e, '{'); + * s = expect_space(s, e); + * + * if (optional_char(s, e, '}')) + * return s; + * + * Also, note that functions traversing an already-validated JSON text + * can take advantage of the assumption that the input is valid. + * For example, stringify_value does not perform NULL checks, nor does it + * check if s < e before dereferencing s. + */ -/* Every expect_ function returns NULL if s == NULL. - * Normally called like this: s = expect_*(s, e); */ static const char *expect_value(const char *s, const char *e); static const char *expect_object(const char *s, const char *e); static const char *expect_array(const char *s, const char *e); @@ -60,6 +96,21 @@ static const char *expect_space(const char *s, const char *e); */ /* + * expect_char: Expect the next character to be @c, and consume it. + * expect_char_pred: Expect pred(next character) to hold, and consume it. + * expect_char_cond: Expect a character to be available and cond to hold, and consume. + * expect_eof: Expect there to be no more input left. + * + * These macros, like any expect_ macros/functions, return a new pointer + * rather than updating @s. + */ +#define expect_char(s, e, c) expect_char_cond(s, e, *(s) == (c)) +#define expect_char_pred(s, e, pred) expect_char_cond(s, e, pred(*(s))) +#define expect_char_cond(s, e, cond) \ + ((s) != NULL && (s) < (e) && (cond) ? (s) + 1 : NULL) +#define expect_eof(s, e) ((s) != NULL && (s) == (e) ? (s) : NULL) + +/* * next_char: Get the next character, but do not consume it. * next_char_pred: Apply pred to the next character. * next_char_cond: Evaluate cond if a character is available. @@ -85,19 +136,6 @@ static const char *expect_space(const char *s, const char *e); ((s) != NULL && (s) < (e) ? (s)++, pred((s)[-1]) : false) /* - * expect_char: Expect the next character to be @c, and consume it. - * expect_char_pred: Expect pred(next character) to hold, and consume it. - * expect_char_cond: Expect a character to be available and cond to hold, and consume. - * - * These macros, like any expect_ macros/functions, return a new pointer - * rather than updating @s. - */ -#define expect_char(s, e, c) expect_char_cond(s, e, *(s) == (c)) -#define expect_char_pred(s, e, pred) expect_char_cond(s, e, pred(*(s))) -#define expect_char_cond(s, e, cond) \ - ((s) != NULL && (s) < (e) && (cond) ? (s) + 1 : NULL) - -/* * optional_char: If the next character is @c, consume it. * optional_char_pred: If pred(next character) holds, consume it. * optional_char_cond: If a character is available, and cond holds, consume. @@ -147,8 +185,9 @@ json_validate(const char *str, size_t length) s = expect_space(s, e); s = expect_value(s, e); s = expect_space(s, e); + s = expect_eof(s, e); - return s != NULL && s == e; + return s != NULL; } /* |