Skip to content

Instantly share code, notes, and snippets.

@micha
Created September 30, 2016 22:28
Show Gist options
  • Save micha/d745299edfefb4b1594b98a748c0ac4a to your computer and use it in GitHub Desktop.
Save micha/d745299edfefb4b1594b98a748c0ac4a to your computer and use it in GitHub Desktop.
#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