Created
September 30, 2016 22:28
-
-
Save micha/d745299edfefb4b1594b98a748c0ac4a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stddef.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <stdio.h> | |
typedef enum { | |
JS_OBJECT = 1, | |
JS_ARRAY, | |
JS_NULL, | |
JS_NUMBER, | |
JS_TRUE, | |
JS_FALSE, | |
JS_STRING, | |
JS_KEYVAL | |
} jstype_t; | |
enum jserr { | |
JS_ERROR_NOMEM = -1, | |
JS_ERROR_PARSE = -2 | |
}; | |
typedef struct { | |
jstype_t type; | |
size_t start; | |
size_t end; | |
size_t size; | |
size_t up; | |
size_t down; | |
size_t right; | |
} jstok_t; | |
typedef struct { | |
size_t pos; | |
size_t next; | |
size_t parent; | |
} js_parser; | |
int js_errno = 0; | |
size_t js_errloc = SIZE_MAX; | |
jstok_t *js_alloc(js_parser *parser, jstok_t *toks, size_t num_toks) { | |
jstok_t *tok; | |
if (parser->next >= num_toks) | |
return NULL; | |
tok = &toks[parser->next++]; | |
tok->size = 0; | |
tok->up = tok->down = tok->right = tok->start = tok->end = SIZE_MAX; | |
return tok; | |
} | |
/* | |
* Reads zero or more digit characters starting at js, returns a pointer to | |
* the first non-digit character encountered. | |
*/ | |
const char *js_scan_digits(const char *js) { | |
const char *p = js; | |
for (; *p != '\0'; p++) | |
if (!(48 <= *p && *p <= 57)) | |
break; | |
return p; | |
} | |
/* | |
* Given js (pointing to the first character of a JSON null), returns a | |
* pointer to the first character following the null. Note that nulls | |
* must be followed by whitespace, comma, closing brace or closing bracket. | |
*/ | |
const char *js_scan_null(const char *js) { | |
const char *p = js; | |
if (strncmp(p, "null", 4)) | |
return NULL; | |
p += 4; | |
if (!strchr("\t\r\n ,]}", *p)) | |
return NULL; | |
return p; | |
} | |
/* | |
* Given js (pointing to the first character of a JSON true), returns a | |
* pointer to the first character following the true. Note that trues | |
* must be followed by whitespace, comma, closing brace or closing bracket. | |
*/ | |
const char *js_scan_true(const char *js) { | |
const char *p = js; | |
if (strncmp(p, "true", 4)) | |
return NULL; | |
p += 4; | |
if (!strchr("\t\r\n ,]}", *p)) | |
return NULL; | |
return p; | |
} | |
/* | |
* Given js (pointing to the first character of a JSON false), returns a | |
* pointer to the first character following the false. Note that falses | |
* must be followed by whitespace, comma, closing brace or closing bracket. | |
*/ | |
const char *js_scan_false(const char *js) { | |
const char *p = js; | |
if (strncmp(p, "false", 5)) | |
return NULL; | |
p += 5; | |
if (!strchr("\t\r\n ,]}", *p)) | |
return NULL; | |
return p; | |
} | |
/* | |
* Given js (pointing to the first character of a JSON number), returns a | |
* pointer to the first character following the number. Note that numbers | |
* must be followed by whitespace, comma, closing brace or closing bracket. | |
*/ | |
const char *js_scan_number(const char *js) { | |
const char *p = js; | |
if (*p == '-') | |
p++; | |
if (*p == '0') { | |
p++; | |
} else if (48 < *p && *p <= 57) { | |
p = js_scan_digits(p); | |
} else { | |
return NULL; | |
} | |
if (*p == '.') { | |
p++; | |
if (48 <= *p && *p <= 57) { | |
p = js_scan_digits(p); | |
} else { | |
return NULL; | |
} | |
} | |
if (*p == 'e' || *p == 'E') { | |
p++; | |
if (*p == '+' || *p == '-') | |
p++; | |
if (48 <= *p && *p <= 57) { | |
p = js_scan_digits(p); | |
} else { | |
return NULL; | |
} | |
} | |
if (!strchr("\t\r\n ,]}", *p)) | |
return NULL; | |
return p; | |
} | |
/* | |
* Given js (pointing to the opening quote of a JSON string), returns a | |
* a pointer to the closing quote or NULL if the string can't be parsed. | |
*/ | |
const char *js_scan_string(const char *js) { | |
const char *p = js; | |
int i; | |
p++; | |
for (; *p != '\0'; p++) { | |
if (*p == '\"') | |
break; | |
if (*p < 32) | |
return NULL; | |
if (*p == '\\') { | |
switch(*(++p)) { | |
case '\"': | |
case '\\': | |
case '/': | |
case 'b': | |
case 'f': | |
case 'n': | |
case 'r': | |
case 't': | |
continue; | |
case 'u': | |
for (i = 1; i < 5; i++) | |
if (!((48 <= p[i] && p[i] <= 57) || | |
(65 <= p[i] && p[i] <= 70) || | |
(97 <= p[i] && p[i] <= 102))) | |
return NULL; | |
p += 4; | |
break; | |
default: | |
return NULL; | |
} | |
} | |
} | |
if (*p != '\"') | |
return NULL; | |
return p; | |
} | |
void js_parser_init(js_parser *parser) { | |
parser->pos = 0; | |
parser->next = 0; | |
parser->parent = SIZE_MAX; | |
} | |
int main(int argc, char *argv[]) { | |
const char *js = argv[1], *end; | |
js++; | |
if (!(end = js_scan_string(js))) { | |
printf("error!\n"); | |
return 1; | |
} | |
printf("foo: '%.*s'\n", (int) (end - js), js); | |
printf("bar: '%c'\n", *end); | |
js = end + 1; | |
while (strchr("\t\r\n ", *js)) | |
js++; | |
if (!(end = js_scan_number(js))) { | |
printf("error!\n"); | |
return 1; | |
} | |
printf("foo: '%.*s'\n", (int) (end - js), js); | |
printf("bar: '%c'\n", *end); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment