Handle more test cases

This commit is contained in:
CK Tan 2019-08-16 04:35:14 -07:00
parent b701a09579
commit 3bc235331f
3 changed files with 262 additions and 183 deletions

136
toml.c
View file

@ -774,7 +774,10 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
EAT_TOKEN(ctx, LBRACE); EAT_TOKEN(ctx, LBRACE);
for (;;) { for (;;) {
SKIP_NEWLINES(ctx); if (ctx->tok.tok == NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "newline not allowed in inline table");
return; /* not reached */
}
/* until } */ /* until } */
if (ctx->tok.tok == RBRACE) break; if (ctx->tok.tok == RBRACE) break;
@ -784,7 +787,11 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
return; /* not reached */ return; /* not reached */
} }
parse_keyval(ctx, tab); parse_keyval(ctx, tab);
SKIP_NEWLINES(ctx);
if (ctx->tok.tok == NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "newline not allowed in inline table");
return; /* not reached */
}
/* on comma, continue to scan for next keyval */ /* on comma, continue to scan for next keyval */
if (ctx->tok.tok == COMMA) { if (ctx->tok.tok == COMMA) {
@ -1113,15 +1120,17 @@ static void walk_tabpath(context_t* ctx)
/* handle lines like [x.y.z] or [[x.y.z]] */ /* handle lines like [x.y.z] or [[x.y.z]] */
static void parse_select(context_t* ctx) static void parse_select(context_t* ctx)
{ {
int count_lbracket = 0; assert(ctx->tok.tok == LBRACKET);
if (ctx->tok.tok != LBRACKET) {
e_internal_error(ctx, FLINE); /* true if [[ */
return; /* not reached */ int llb = (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == '[');
} /* need to detect '[[' on our own because next_token() will skip whitespace,
count_lbracket++; and '[ [' would be taken as '[[', which is wrong. */
/* eat [ or [[ */
next_token(ctx, 1 /* DOT IS SPECIAL */); next_token(ctx, 1 /* DOT IS SPECIAL */);
if (ctx->tok.tok == LBRACKET) { if (llb) {
count_lbracket++; assert(ctx->tok.tok == LBRACKET);
next_token(ctx, 1 /* DOT IS SPECIAL */); next_token(ctx, 1 /* DOT IS SPECIAL */);
} }
@ -1133,9 +1142,10 @@ static void parse_select(context_t* ctx)
free(ctx->tpath.key[ctx->tpath.top-1]); free(ctx->tpath.key[ctx->tpath.top-1]);
ctx->tpath.top--; ctx->tpath.top--;
/* set up ctx->curtab */
walk_tabpath(ctx); walk_tabpath(ctx);
if (count_lbracket == 1) { if (! llb) {
/* [x.y.z] -> create z = {} in x.y */ /* [x.y.z] -> create z = {} in x.y */
ctx->curtab = create_keytable_in_table(ctx, ctx->curtab, z); ctx->curtab = create_keytable_in_table(ctx, ctx->curtab, z);
} else { } else {
@ -1183,15 +1193,15 @@ static void parse_select(context_t* ctx)
e_syntax_error(ctx, ctx->tok.lineno, "expects ]"); e_syntax_error(ctx, ctx->tok.lineno, "expects ]");
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, RBRACKET); if (llb) {
if (! (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == ']')) {
if (count_lbracket == 2) {
if (ctx->tok.tok != RBRACKET) {
e_syntax_error(ctx, ctx->tok.lineno, "expects ]]"); e_syntax_error(ctx, ctx->tok.lineno, "expects ]]");
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, RBRACKET); EAT_TOKEN(ctx, RBRACKET);
} }
EAT_TOKEN(ctx, RBRACKET);
if (ctx->tok.tok != NEWLINE) { if (ctx->tok.tok != NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]"); e_syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]");
return; /* not reached */ return; /* not reached */
@ -1681,7 +1691,7 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
memset(ret, 0, sizeof(*ret)); memset(ret, 0, sizeof(*ret));
/* parse date */ /* parse date YYYY-MM-DD */
val = 0; val = 0;
if (q - p > 4 && p[4] == '-') { if (q - p > 4 && p[4] == '-') {
for (i = 0; i < 10; i++, p++) { for (i = 0; i < 10; i++, p++) {
@ -1708,7 +1718,7 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
} }
if (q == p) return 0; if (q == p) return 0;
/* parse time */ /* parse time HH:MM:SS */
val = 0; val = 0;
if (q - p < 8) return -1; if (q - p < 8) return -1;
for (i = 0; i < 8; i++, p++) { for (i = 0; i < 8; i++, p++) {
@ -1727,8 +1737,22 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
*ret->minute = val % 100; val /= 100; *ret->minute = val % 100; val /= 100;
*ret->hour = val; *ret->hour = val;
/* skip fractional second */ /* parse millisec */
if (*p == '.') for (p++; '0' <= *p && *p <= '9'; p++); if (*p == '.') {
val = 0;
p++;
if ('0' <= *p && *p <= '9') {
val = (*p++ - '0') * 100;
if ('0' <= *p && *p <= '9') {
val += (*p++ - '0') * 10;
if ('0' <= *p && *p <= '9') {
val += (*p++ - '0');
}
}
}
ret->millisec = &ret->__buffer.millisec;
*ret->millisec = val;
}
if (q == p) return 0; if (q == p) return 0;
/* parse and copy Z */ /* parse and copy Z */
@ -1792,11 +1816,15 @@ int toml_rtoi(const char* src, int64_t* ret_)
int64_t dummy; int64_t dummy;
int64_t* ret = ret_ ? ret_ : &dummy; int64_t* ret = ret_ ? ret_ : &dummy;
if (*s == '+')
*p++ = *s++; /* allow +/- */
else if (*s == '-') if (s[0] == '+' || s[0] == '-')
*p++ = *s++; *p++ = *s++;
/* disallow +_100 */
if (s[0] == '_')
return -1;
/* if 0 ... */ /* if 0 ... */
if ('0' == s[0]) { if ('0' == s[0]) {
switch (s[1]) { switch (s[1]) {
@ -1813,10 +1841,21 @@ int toml_rtoi(const char* src, int64_t* ret_)
/* just strip underscores and pass to strtoll */ /* just strip underscores and pass to strtoll */
while (*s && p < q) { while (*s && p < q) {
int ch = *s++; int ch = *s++;
if (ch == '_') ; else *p++ = ch; switch (ch) {
case '_':
// disallow '__'
if (s[0] == '_') return -1;
continue; /* skip _ */
default:
break;
}
*p++ = ch;
} }
if (*s || p == q) return -1; if (*s || p == q) return -1;
/* last char cannot be '_' */
if (s[-1] == '_') return -1;
/* cap with NUL */ /* cap with NUL */
*p = 0; *p = 0;
@ -1828,31 +1867,54 @@ int toml_rtoi(const char* src, int64_t* ret_)
} }
int toml_rtod(const char* src, double* ret_) int toml_rtod_ex(const char* src, double* ret_, char* buf, int buflen)
{ {
if (!src) return -1; if (!src) return -1;
char buf[100];
char* p = buf; char* p = buf;
char* q = p + sizeof(buf); char* q = p + buflen;
const char* s = src; const char* s = src;
double dummy; double dummy;
double* ret = ret_ ? ret_ : &dummy; double* ret = ret_ ? ret_ : &dummy;
/* check for special cases */
if (s[0] == '+' || s[0] == '-') *p++ = *s++; /* allow +/- */
if (s[0] == '.') return -1; /* no leading zero */ if (s[0] == '+' || s[0] == '-')
if (s[0] == '0') { *p++ = *s++;
/* disallow +_1.00 */
if (s[0] == '_')
return -1;
/* disallow +.99 */
if (s[0] == '.')
return -1;
/* zero must be followed by . or NUL */ /* zero must be followed by . or NUL */
if (s[1] && s[1] != '.') return -1; if (s[0] == '0' && s[1] && s[1] != '.')
} return -1;
/* just strip underscores and pass to strtod */ /* just strip underscores and pass to strtod */
while (*s && p < q) { while (*s && p < q) {
int ch = *s++; int ch = *s++;
if (ch == '_') ; else *p++ = ch; switch (ch) {
case '.':
if (s[-2] == '_') return -1;
if (s[0] == '_') return -1;
break;
case '_':
// disallow '__'
if (s[0] == '_') return -1;
continue; /* skip _ */
default:
break;
} }
if (*s || p == q) return -1; *p++ = ch;
}
if (*s || p == q) return -1; /* reached end of string or buffer is full? */
/* last char cannot be '_' */
if (s[-1] == '_') return -1;
if (p != buf && p[-1] == '.') if (p != buf && p[-1] == '.')
return -1; /* no trailing zero */ return -1; /* no trailing zero */
@ -1867,6 +1929,12 @@ int toml_rtod(const char* src, double* ret_)
return (errno || *endp) ? -1 : 0; return (errno || *endp) ? -1 : 0;
} }
int toml_rtod(const char* src, double* ret_)
{
char buf[100];
return toml_rtod_ex(src, ret_, buf, sizeof(buf));
}
static char* kill_line_ending_backslash(char* str) static char* kill_line_ending_backslash(char* str)
{ {

6
toml.h
View file

@ -112,6 +112,8 @@ TOML_EXTERN int toml_rtoi(const char* s, int64_t* ret);
/* Raw to Double. Return 0 on success, -1 otherwise. */ /* Raw to Double. Return 0 on success, -1 otherwise. */
TOML_EXTERN int toml_rtod(const char* s, double* ret); TOML_EXTERN int toml_rtod(const char* s, double* ret);
/* Same as toml_rtod, but return the sanitized double in string form as well */
TOML_EXTERN int toml_rtod_ex(const char* s, double* ret, char* buf, int buflen);
/* Timestamp types. The year, month, day, hour, minute, second, z /* Timestamp types. The year, month, day, hour, minute, second, z
* fields may be NULL if they are not relevant. e.g. In a DATE * fields may be NULL if they are not relevant. e.g. In a DATE
@ -121,11 +123,11 @@ typedef struct toml_timestamp_t toml_timestamp_t;
struct toml_timestamp_t { struct toml_timestamp_t {
struct { /* internal. do not use. */ struct { /* internal. do not use. */
int year, month, day; int year, month, day;
int hour, minute, second; int hour, minute, second, millisec;
char z[10]; char z[10];
} __buffer; } __buffer;
int *year, *month, *day; int *year, *month, *day;
int *hour, *minute, *second; int *hour, *minute, *second, *millisec;
char* z; char* z;
}; };

View file

@ -58,6 +58,7 @@ static void print_raw(const char* s)
int bval; int bval;
double dval; double dval;
toml_timestamp_t ts; toml_timestamp_t ts;
char dbuf[100];
if (0 == toml_rtos(s, &sval)) { if (0 == toml_rtos(s, &sval)) {
printf("{\"type\":\"string\",\"value\":\""); printf("{\"type\":\"string\",\"value\":\"");
@ -68,19 +69,25 @@ static void print_raw(const char* s)
printf("{\"type\":\"integer\",\"value\":\"%" PRId64 "\"}", ival); printf("{\"type\":\"integer\",\"value\":\"%" PRId64 "\"}", ival);
} else if (0 == toml_rtob(s, &bval)) { } else if (0 == toml_rtob(s, &bval)) {
printf("{\"type\":\"bool\",\"value\":\"%s\"}", bval ? "true" : "false"); printf("{\"type\":\"bool\",\"value\":\"%s\"}", bval ? "true" : "false");
} else if (0 == toml_rtod(s, &dval)) { } else if (0 == toml_rtod_ex(s, &dval, dbuf, sizeof(dbuf))) {
printf("{\"type\":\"float\",\"value\":\"%s\"}", s); printf("{\"type\":\"float\",\"value\":\"%s\"}", dbuf);
} else if (0 == toml_rtots(s, &ts)) { } else if (0 == toml_rtots(s, &ts)) {
char millisec[10];
if (ts.millisec)
sprintf(millisec, ".%d", *ts.millisec);
else
millisec[0] = 0;
if (ts.year && ts.hour) { if (ts.year && ts.hour) {
printf("{\"type\":\"datetime\",\"value\":\"%04d-%02d-%02dT%02d:%02d:%02d%s\"}", printf("{\"type\":\"datetime\",\"value\":\"%04d-%02d-%02dT%02d:%02d:%02d%s%s\"}",
*ts.year, *ts.month, *ts.day, *ts.hour, *ts.minute, *ts.second, *ts.year, *ts.month, *ts.day, *ts.hour, *ts.minute, *ts.second,
millisec,
(ts.z ? ts.z : "")); (ts.z ? ts.z : ""));
} else if (ts.year) { } else if (ts.year) {
printf("{\"type\":\"date\",\"value\":\"%04d-%02d-%02d\"}", printf("{\"type\":\"date\",\"value\":\"%04d-%02d-%02d\"}",
*ts.year, *ts.month, *ts.day); *ts.year, *ts.month, *ts.day);
} else if (ts.hour) { } else if (ts.hour) {
printf("{\"type\":\"time\",\"value\":\"%02d:%02d:%02d\"}", printf("{\"type\":\"time\",\"value\":\"%02d:%02d:%02d%s\"}",
*ts.hour, *ts.minute, *ts.second); *ts.hour, *ts.minute, *ts.second, millisec);
} }
} else { } else {
fprintf(stderr, "unknown type\n"); fprintf(stderr, "unknown type\n");
@ -102,7 +109,9 @@ static void print_table(toml_table_t* curtab)
printf("{"); printf("{");
for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) { for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) {
printf("%s\"%s\":", i > 0 ? "," : "", key); printf("%s\"", i > 0 ? "," : "");
print_escape_string(key);
printf("\":");
if (0 != (raw = toml_raw_in(curtab, key))) { if (0 != (raw = toml_raw_in(curtab, key))) {
print_raw(raw); print_raw(raw);