fixed issue #51

This commit is contained in:
CK Tan 2021-03-06 23:54:24 -08:00
parent 1cfa8e42c4
commit 715fa54d45
55 changed files with 747 additions and 366 deletions

1
stdex/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/*.out

15
stdex/RUN.sh Normal file
View file

@ -0,0 +1,15 @@
rm -f *.out
for i in *.toml; do
echo -n $i
../toml_cat $i >& $i.out
if [ -f $i.res ]; then
if $(diff $i.out $i.res >& /dev/null); then
echo " [OK]"
else
echo " [FAILED]"
fi
else
echo " [??]"
fi
done

3
stdex/comment.toml Normal file
View file

@ -0,0 +1,3 @@
# This is a full-line comment
key = "value" # This is a comment at the end of a line
another = "# This is not a comment"

4
stdex/comment.toml.res Normal file
View file

@ -0,0 +1,4 @@
{
key = "value",
another = "# This is not a comment",
}

4
stdex/keys00.toml Normal file
View file

@ -0,0 +1,4 @@
key = "value"
bare_key = "value"
bare-key = "value"
1234 = "value"

6
stdex/keys00.toml.res Normal file
View file

@ -0,0 +1,6 @@
{
key = "value",
bare_key = "value",
bare-key = "value",
1234 = "value",
}

5
stdex/keys01.toml Normal file
View file

@ -0,0 +1,5 @@
"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"
'key2' = "value"
'quoted "value"' = "value"

7
stdex/keys01.toml.res Normal file
View file

@ -0,0 +1,7 @@
{
127.0.0.1 = "value",
character encoding = "value",
ʎǝʞ = "value",
key2 = "value",
quoted "value" = "value",
}

1
stdex/keys02.toml Normal file
View file

@ -0,0 +1 @@
= "no key name" # INVALID

1
stdex/keys02.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 1: syntax error

1
stdex/keys03.toml Normal file
View file

@ -0,0 +1 @@
"" = "blank" # VALID but discouraged

3
stdex/keys03.toml.res Normal file
View file

@ -0,0 +1,3 @@
{
= "blank",
}

4
stdex/keys04.toml Normal file
View file

@ -0,0 +1,4 @@
name = "Orange"
physical.color = "orange"
physical.shape = "round"
site."google.com" = true

10
stdex/keys04.toml.res Normal file
View file

@ -0,0 +1,10 @@
{
name = "Orange",
physical = {
color = "orange",
shape = "round",
},
site = {
google.com = true,
},
}

3
stdex/keys05.toml Normal file
View file

@ -0,0 +1,3 @@
fruit.name = "banana" # this is best practice
fruit. color = "yellow" # same as fruit.color
fruit . flavor = "banana" # same as fruit.flavor

7
stdex/keys05.toml.res Normal file
View file

@ -0,0 +1,7 @@
{
fruit = {
name = "banana",
color = "yellow",
flavor = "banana",
},
}

3
stdex/keys06.toml Normal file
View file

@ -0,0 +1,3 @@
# DO NOT DO THIS
name = "Tom"
name = "Pradyun"

1
stdex/keys06.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 3: key exists

3
stdex/keys07.toml Normal file
View file

@ -0,0 +1,3 @@
# THIS WILL NOT WORK
spelling = "favorite"
"spelling" = "favourite"

1
stdex/keys07.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 3: key exists

5
stdex/keys08.toml Normal file
View file

@ -0,0 +1,5 @@
# This makes the key "fruit" into a table.
fruit.apple.smooth = true
# So then you can add to the table "fruit" like so:
fruit.orange = 2

8
stdex/keys08.toml.res Normal file
View file

@ -0,0 +1,8 @@
{
fruit = {
orange = 2,
apple = {
smooth = true,
},
},
}

8
stdex/keys09.toml Normal file
View file

@ -0,0 +1,8 @@
# THE FOLLOWING IS INVALID
# This defines the value of fruit.apple to be an integer.
fruit.apple = 1
# But then this treats fruit.apple like it's a table.
# You can't turn an integer into a table.
fruit.apple.smooth = true

1
stdex/keys09.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 8: key exists

10
stdex/keys10.toml Normal file
View file

@ -0,0 +1,10 @@
# VALID BUT DISCOURAGED
apple.type = "fruit"
orange.type = "fruit"
apple.skin = "thin"
orange.skin = "thick"
apple.color = "red"
orange.color = "orange"

12
stdex/keys10.toml.res Normal file
View file

@ -0,0 +1,12 @@
{
apple = {
type = "fruit",
skin = "thin",
color = "red",
},
orange = {
type = "fruit",
skin = "thick",
color = "orange",
},
}

9
stdex/keys11.toml Normal file
View file

@ -0,0 +1,9 @@
# RECOMMENDED
apple.type = "fruit"
apple.skin = "thin"
apple.color = "red"
orange.type = "fruit"
orange.skin = "thick"
orange.color = "orange"

12
stdex/keys11.toml.res Normal file
View file

@ -0,0 +1,12 @@
{
apple = {
type = "fruit",
skin = "thin",
color = "red",
},
orange = {
type = "fruit",
skin = "thick",
color = "orange",
},
}

1
stdex/keys12.toml Normal file
View file

@ -0,0 +1 @@
3.14159 = "pi"

5
stdex/keys12.toml.res Normal file
View file

@ -0,0 +1,5 @@
{
3 = {
14159 = "pi",
},
}

1
stdex/kvpair0.toml Normal file
View file

@ -0,0 +1 @@
key = "value"

3
stdex/kvpair0.toml.res Normal file
View file

@ -0,0 +1,3 @@
{
key = "value",
}

1
stdex/kvpair1.toml Normal file
View file

@ -0,0 +1 @@
key = # INVALID

1
stdex/kvpair1.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 1: syntax error

1
stdex/kvpair2.toml Normal file
View file

@ -0,0 +1 @@
first = "Tom" last = "Preston-Werner" # INVALID

1
stdex/kvpair2.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 1: extra chars after value

1
stdex/string0.toml Normal file
View file

@ -0,0 +1 @@
str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."

3
stdex/string0.toml.res Normal file
View file

@ -0,0 +1,3 @@
{
str = "I'm a string. \"You can quote me\". Name\tJos\0xc3\0xa9\nLocation\tSF.",
}

9
stdex/string1.toml Normal file
View file

@ -0,0 +1,9 @@
str1 = """
Roses are red
Violets are blue"""
# On a Unix system, the above multi-line string will most likely be the same as:
str2 = "Roses are red\nViolets are blue"
# On a Windows system, it will most likely be equivalent to:
str3 = "Roses are red\r\nViolets are blue"

5
stdex/string1.toml.res Normal file
View file

@ -0,0 +1,5 @@
{
str1 = "Roses are red\nViolets are blue",
str2 = "Roses are red\nViolets are blue",
str3 = "Roses are red\r\nViolets are blue",
}

15
stdex/string3.toml Normal file
View file

@ -0,0 +1,15 @@
# The following strings are byte-for-byte equivalent:
str1 = "The quick brown fox jumps over the lazy dog."
str2 = """
The quick brown \
fox jumps over \
the lazy dog."""
str3 = """\
The quick brown \
fox jumps over \
the lazy dog.\
"""

5
stdex/string3.toml.res Normal file
View file

@ -0,0 +1,5 @@
{
str1 = "The quick brown fox jumps over the lazy dog.",
str2 = "The quick brown fox jumps over the lazy dog.",
str3 = "The quick brown fox jumps over the lazy dog.",
}

7
stdex/string4.toml Normal file
View file

@ -0,0 +1,7 @@
str4 = """Here are two quotation marks: "". Simple enough."""
# str5 = """Here are three quotation marks: """.""" # INVALID
str5 = """Here are three quotation marks: ""\"."""
str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
# "This," she said, "is just a pointless statement."
str7 = """"This," she said, "is just a pointless statement.""""

6
stdex/string4.toml.res Normal file
View file

@ -0,0 +1,6 @@
{
str4 = "Here are two quotation marks: \"\". Simple enough.",
str5 = "Here are three quotation marks: \"\"\".",
str6 = "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".",
str7 = "\"This,\" she said, \"is just a pointless statement.\"",
}

5
stdex/string5.toml Normal file
View file

@ -0,0 +1,5 @@
# What you see is what you get.
winpath = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
quoted = 'Tom "Dubs" Preston-Werner'
regex = '<\i\c*\s*>'

6
stdex/string5.toml.res Normal file
View file

@ -0,0 +1,6 @@
{
winpath = "C:\\Users\\nodejs\\templates",
winpath2 = "\\\\ServerX\\admin$\\system32\\",
quoted = "Tom \"Dubs\" Preston-Werner",
regex = "<\\i\\c*\\s*>",
}

7
stdex/string6.toml Normal file
View file

@ -0,0 +1,7 @@
regex2 = '''I [dw]on't need \d{2} apples'''
lines = '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''

4
stdex/string6.toml.res Normal file
View file

@ -0,0 +1,4 @@
{
regex2 = "I [dw]on't need \\d{2} apples",
lines = "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n",
}

4
stdex/string7.toml Normal file
View file

@ -0,0 +1,4 @@
quot15 = '''Here are fifteen quotation marks: """""""""""""""'''
# 'That,' she said, 'is still pointless.'
str = ''''That,' she said, 'is still pointless.''''

4
stdex/string7.toml.res Normal file
View file

@ -0,0 +1,4 @@
{
quot15 = "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"",
str = "'That,' she said, 'is still pointless.'",
}

3
stdex/string8.toml Normal file
View file

@ -0,0 +1,3 @@
# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID
apos15 = "Here are fifteen apostrophes: '''''''''''''''"

1
stdex/string8.toml.res Normal file
View file

@ -0,0 +1 @@
ERROR: line 2: triple-s-quote inside string lit

257
toml.c
View file

@ -2,7 +2,7 @@
MIT License MIT License
Copyright (c) 2017 - 2019 CK Tan Copyright (c) 2017 - 2021 CK Tan
https://github.com/cktan/tomlc99 https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -268,18 +268,22 @@ struct toml_keyval_t {
const char* val; /* the raw value */ const char* val; /* the raw value */
}; };
typedef struct toml_arritem_t toml_arritem_t;
struct toml_arritem_t {
int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp */
char* val;
toml_array_t* arr;
toml_table_t* tab;
};
struct toml_array_t { struct toml_array_t {
const char* key; /* key to this array */ const char* key; /* key to this array */
int kind; /* element kind: 'v'alue, 'a'rray, or 't'able */ int kind; /* element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed */
int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp */ int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp, 'm'ixed */
int nelem; /* number of elements */ int nitem; /* number of elements */
union { toml_arritem_t* item;
char** val;
toml_array_t** arr;
toml_table_t** tab;
} u;
}; };
@ -408,6 +412,15 @@ static void** expand_ptrarr(void** p, int n)
return s; return s;
} }
static toml_arritem_t* expand_arritem(toml_arritem_t* p, int n)
{
toml_arritem_t* pp = expand(p, n*sizeof(*p), (n+1)*sizeof(*p));
if (!pp) return 0;
memset(&pp[n], 0, sizeof(pp[n]));
return pp;
}
static char* norm_lit_str(const char* src, int srclen, static char* norm_lit_str(const char* src, int srclen,
int multiline, int multiline,
@ -825,26 +838,41 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
return dest; return dest;
} }
static toml_arritem_t* create_value_in_array(context_t* ctx,
toml_array_t* parent)
{
const int n = parent->nitem;
toml_arritem_t* base = expand_arritem(parent->item, n);
if (!base) {
e_outofmemory(ctx, FLINE);
return 0;
}
parent->item = base;
parent->nitem++;
return &parent->item[n];
}
/* Create an array in an array /* Create an array in an array
*/ */
static toml_array_t* create_array_in_array(context_t* ctx, static toml_array_t* create_array_in_array(context_t* ctx,
toml_array_t* parent) toml_array_t* parent)
{ {
const int n = parent->nelem; const int n = parent->nitem;
toml_array_t** base; toml_arritem_t* base = expand_arritem(parent->item, n);
if (0 == (base = (toml_array_t**) expand_ptrarr((void**)parent->u.arr, n))) { if (!base) {
e_outofmemory(ctx, FLINE); e_outofmemory(ctx, FLINE);
return 0; return 0;
} }
parent->u.arr = base; toml_array_t* ret = (toml_array_t*) CALLOC(1, sizeof(toml_array_t));
parent->nelem++; if (!ret) {
if (0 == (base[n] = (toml_array_t*) CALLOC(1, sizeof(*base[n])))) {
e_outofmemory(ctx, FLINE); e_outofmemory(ctx, FLINE);
return 0; return 0;
} }
base[n].arr = ret;
return parent->u.arr[n]; parent->item = base;
parent->nitem++;
return ret;
} }
/* Create a table in an array /* Create a table in an array
@ -852,20 +880,21 @@ static toml_array_t* create_array_in_array(context_t* ctx,
static toml_table_t* create_table_in_array(context_t* ctx, static toml_table_t* create_table_in_array(context_t* ctx,
toml_array_t* parent) toml_array_t* parent)
{ {
int n = parent->nelem; int n = parent->nitem;
toml_table_t** base; toml_arritem_t* base = expand_arritem(parent->item, n);
if (0 == (base = (toml_table_t**) expand_ptrarr((void**)parent->u.tab, n))) { if (!base) {
e_outofmemory(ctx, FLINE); e_outofmemory(ctx, FLINE);
return 0; return 0;
} }
parent->u.tab = base; toml_table_t* ret = (toml_table_t*) CALLOC(1, sizeof(toml_table_t));
if (!ret) {
if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n])))) {
e_outofmemory(ctx, FLINE); e_outofmemory(ctx, FLINE);
return 0; return 0;
} }
base[n].tab = ret;
return parent->u.tab[parent->nelem++]; parent->item = base;
parent->nitem++;
return ret;
} }
@ -963,33 +992,30 @@ static int parse_array(context_t* ctx, toml_array_t* arr)
switch (ctx->tok.tok) { switch (ctx->tok.tok) {
case STRING: case STRING:
{ {
/* set array kind if this will be the first entry */
if (arr->kind == 0)
arr->kind = 'v';
else if (arr->kind != 'v')
arr->kind = 'm';
char* val = ctx->tok.ptr; char* val = ctx->tok.ptr;
int vlen = ctx->tok.len; int vlen = ctx->tok.len;
/* set array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 'v';
/* check array kind */
if (arr->kind != 'v')
return e_syntax(ctx, ctx->tok.lineno, "a string array can only contain strings");
/* make a new value in array */ /* make a new value in array */
char** tmp = (char**) expand_ptrarr((void**)arr->u.val, arr->nelem); toml_arritem_t* newval = create_value_in_array(ctx, arr);
if (!tmp) if (!newval)
return e_outofmemory(ctx, FLINE); return e_outofmemory(ctx, FLINE);
arr->u.val = tmp; if (! (newval->val = STRNDUP(val, vlen)))
if (! (val = STRNDUP(val, vlen)))
return e_outofmemory(ctx, FLINE); return e_outofmemory(ctx, FLINE);
arr->u.val[arr->nelem++] = val; newval->valtype = valtype(newval->val);
/* set array type if this is the first entry, or check that the types matched. */ /* set array type if this is the first entry */
if (arr->nelem == 1) if (arr->nitem == 1)
arr->type = valtype(arr->u.val[0]); arr->type = newval->valtype;
else if (arr->type != valtype(val)) { else if (arr->type != newval->valtype)
return e_syntax(ctx, ctx->tok.lineno, arr->type = 'm'; /* mixed */
"array type mismatch while processing array of values");
}
if (eat_token(ctx, STRING, 0, FLINE)) return -1; if (eat_token(ctx, STRING, 0, FLINE)) return -1;
break; break;
@ -998,12 +1024,11 @@ static int parse_array(context_t* ctx, toml_array_t* arr)
case LBRACKET: case LBRACKET:
{ /* [ [array], [array] ... ] */ { /* [ [array], [array] ... ] */
/* set the array kind if this will be the first entry */ /* set the array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 'a'; if (arr->kind == 0)
/* check array kind */ arr->kind = 'a';
if (arr->kind != 'a') { else if (arr->kind != 'a')
return e_syntax(ctx, ctx->tok.lineno, arr->kind = 'm';
"array type mismatch while processing array of arrays");
}
toml_array_t* subarr = create_array_in_array(ctx, arr); toml_array_t* subarr = create_array_in_array(ctx, arr);
if (!subarr) return -1; if (!subarr) return -1;
if (parse_array(ctx, subarr)) return -1; if (parse_array(ctx, subarr)) return -1;
@ -1013,12 +1038,11 @@ static int parse_array(context_t* ctx, toml_array_t* arr)
case LBRACE: case LBRACE:
{ /* [ {table}, {table} ... ] */ { /* [ {table}, {table} ... ] */
/* set the array kind if this will be the first entry */ /* set the array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 't'; if (arr->kind == 0)
/* check array kind */ arr->kind = 't';
if (arr->kind != 't') { else if (arr->kind != 't')
return e_syntax(ctx, ctx->tok.lineno, arr->kind = 'm';
"array type mismatch while processing array of tables");
}
toml_table_t* subtab = create_table_in_array(ctx, arr); toml_table_t* subtab = create_table_in_array(ctx, arr);
if (!subtab) return -1; if (!subtab) return -1;
if (parse_table(ctx, subtab)) return -1; if (parse_table(ctx, subtab)) return -1;
@ -1199,10 +1223,10 @@ static int walk_tabpath(context_t* ctx)
if (nextarr->kind != 't') if (nextarr->kind != 't')
return e_internal(ctx, FLINE); return e_internal(ctx, FLINE);
if (nextarr->nelem == 0) if (nextarr->nitem == 0)
return e_internal(ctx, FLINE); return e_internal(ctx, FLINE);
nexttab = nextarr->u.tab[nextarr->nelem-1]; nexttab = nextarr->item[nextarr->nitem-1].tab;
break; break;
case 'v': case 'v':
@ -1294,20 +1318,13 @@ static int parse_select(context_t* ctx)
/* add to z[] */ /* add to z[] */
toml_table_t* dest; toml_table_t* dest;
{ {
int n = arr->nelem; toml_table_t* t = create_table_in_array(ctx, arr);
toml_table_t** base = (toml_table_t**) expand_ptrarr((void**)arr->u.tab, n); if (!t) return -1;
if (0 == base)
if (0 == (t->key = STRDUP("__anon__")))
return e_outofmemory(ctx, FLINE); return e_outofmemory(ctx, FLINE);
arr->u.tab = base; dest = t;
if (0 == (base[n] = CALLOC(1, sizeof(*base[n]))))
return e_outofmemory(ctx, FLINE);
if (0 == (base[n]->key = STRDUP("__anon__")))
return e_outofmemory(ctx, FLINE);
dest = arr->u.tab[arr->nelem++];
} }
ctx->curtab = dest; ctx->curtab = dest;
@ -1479,23 +1496,17 @@ static void xfree_arr(toml_array_t* p)
if (!p) return; if (!p) return;
xfree(p->key); xfree(p->key);
switch (p->kind) { const int n = p->nitem;
case 'v': for (int i = 0; i < n; i++) {
for (int i = 0; i < p->nelem; i++) xfree(p->u.val[i]); toml_arritem_t* a = &p->item[i];
xfree(p->u.val); if (a->val)
break; xfree(a->val);
else if (a->arr)
case 'a': xfree_arr(a->arr);
for (int i = 0; i < p->nelem; i++) xfree_arr(p->u.arr[i]); else if (a->tab)
xfree(p->u.arr); xfree_tab(a->tab);
break;
case 't':
for (int i = 0; i < p->nelem; i++) xfree_tab(p->u.tab[i]);
xfree(p->u.tab);
break;
} }
xfree(p->item);
xfree(p); xfree(p);
} }
@ -1584,20 +1595,44 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
{ {
char* orig = p; char* orig = p;
if (0 == strncmp(p, "'''", 3)) { if (0 == strncmp(p, "'''", 3)) {
p = strstr(p + 3, "'''"); char* q = p + 3;
if (0 == p) {
while (1) {
q = strstr(q, "'''");
if (0 == q) {
return e_syntax(ctx, lineno, "unterminated triple-s-quote"); return e_syntax(ctx, lineno, "unterminated triple-s-quote");
} }
while (q[3] == '\'') q++;
break;
}
set_token(ctx, STRING, lineno, orig, p + 3 - orig); set_token(ctx, STRING, lineno, orig, q + 3 - orig);
return 0; return 0;
} }
if (0 == strncmp(p, "\"\"\"", 3)) { if (0 == strncmp(p, "\"\"\"", 3)) {
char* q = p + 3;
while (1) {
q = strstr(q, "\"\"\"");
if (0 == q) {
return e_syntax(ctx, lineno, "unterminated triple-d-quote");
}
if (q[-1] == '\\') {
q++;
continue;
}
while (q[3] == '\"') q++;
break;
}
char* tsq = strstr(p, "\'\'\'");
// the string is [p+3, q-1]
int hexreq = 0; /* #hex required */ int hexreq = 0; /* #hex required */
int escape = 0; int escape = 0;
int qcnt = 0; /* count quote */ for (p += 3; p < q; p++) {
for (p += 3; *p && qcnt < 3; p++) {
if (escape) { if (escape) {
escape = 0; escape = 0;
if (strchr("btnfr\"\\", *p)) continue; if (strchr("btnfr\"\\", *p)) continue;
@ -1612,13 +1647,16 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
return e_syntax(ctx, lineno, "expect hex char"); return e_syntax(ctx, lineno, "expect hex char");
} }
if (*p == '\\') { escape = 1; continue; } if (*p == '\\') { escape = 1; continue; }
qcnt = (*p == '"') ? qcnt + 1 : 0;
}
if (qcnt != 3) {
return e_syntax(ctx, lineno, "unterminated triple-quote");
} }
if (escape)
return e_syntax(ctx, lineno, "expect an escape char");
if (hexreq)
return e_syntax(ctx, lineno, "expected more hex char");
set_token(ctx, STRING, lineno, orig, p - orig); if (tsq && tsq < q) {
return e_syntax(ctx, lineno, "triple-s-quote inside string lit");
}
set_token(ctx, STRING, lineno, orig, q + 3 - orig);
return 0; return 0;
} }
@ -1633,6 +1671,7 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
} }
if ('\"' == *p) { if ('\"' == *p) {
char* tsq = strstr(p, "\'\'\'");
int hexreq = 0; /* #hex required */ int hexreq = 0; /* #hex required */
int escape = 0; int escape = 0;
for (p++; *p; p++) { for (p++; *p; p++) {
@ -1656,6 +1695,10 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
return e_syntax(ctx, lineno, "unterminated quote"); return e_syntax(ctx, lineno, "unterminated quote");
} }
if (tsq && tsq < p) {
return e_syntax(ctx, lineno, "triple-s-quote inside string lit");
}
set_token(ctx, STRING, lineno, orig, p + 1 - orig); set_token(ctx, STRING, lineno, orig, p + 1 - orig);
return 0; return 0;
} }
@ -1779,11 +1822,7 @@ toml_table_t* toml_table_in(const toml_table_t* tab, const char* key)
toml_raw_t toml_raw_at(const toml_array_t* arr, int idx) toml_raw_t toml_raw_at(const toml_array_t* arr, int idx)
{ {
if (arr->kind != 'v') return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0;
return 0;
if (! (0 <= idx && idx < arr->nelem))
return 0;
return arr->u.val[idx];
} }
char toml_array_kind(const toml_array_t* arr) char toml_array_kind(const toml_array_t* arr)
@ -1796,7 +1835,7 @@ char toml_array_type(const toml_array_t* arr)
if (arr->kind != 'v') if (arr->kind != 'v')
return 0; return 0;
if (arr->nelem == 0) if (arr->nitem == 0)
return 0; return 0;
return arr->type; return arr->type;
@ -1805,7 +1844,7 @@ char toml_array_type(const toml_array_t* arr)
int toml_array_nelem(const toml_array_t* arr) int toml_array_nelem(const toml_array_t* arr)
{ {
return arr->nelem; return arr->nitem;
} }
const char* toml_array_key(const toml_array_t* arr) const char* toml_array_key(const toml_array_t* arr)
@ -1835,20 +1874,12 @@ const char* toml_table_key(const toml_table_t* tab)
toml_array_t* toml_array_at(const toml_array_t* arr, int idx) toml_array_t* toml_array_at(const toml_array_t* arr, int idx)
{ {
if (arr->kind != 'a') return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0;
return 0;
if (! (0 <= idx && idx < arr->nelem))
return 0;
return arr->u.arr[idx];
} }
toml_table_t* toml_table_at(const toml_array_t* arr, int idx) toml_table_t* toml_table_at(const toml_array_t* arr, int idx)
{ {
if (arr->kind != 't') return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0;
return 0;
if (! (0 <= idx && idx < arr->nelem))
return 0;
return arr->u.tab[idx];
} }

4
toml.h
View file

@ -124,11 +124,11 @@ TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
/*----------------------------------------------------------------- /*-----------------------------------------------------------------
* lesser used * lesser used
*/ */
/* Return the array kind: 't'able, 'a'rray, 'v'alue */ /* Return the array kind: 't'able, 'a'rray, 'v'alue, 'm'ixed */
TOML_EXTERN char toml_array_kind(const toml_array_t* arr); TOML_EXTERN char toml_array_kind(const toml_array_t* arr);
/* For array kind 'v'alue, return the type of values /* For array kind 'v'alue, return the type of values
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp, 'm'ixed
0 if unknown 0 if unknown
*/ */
TOML_EXTERN char toml_array_type(const toml_array_t* arr); TOML_EXTERN char toml_array_type(const toml_array_t* arr);

View file

@ -1,26 +1,26 @@
/* /*
MIT License MIT License
Copyright (c) 2017 CK Tan Copyright (c) 2017 CK Tan
https://github.com/cktan/tomlc99 https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#ifdef NDEBUG #ifdef NDEBUG
@ -33,6 +33,8 @@ SOFTWARE.
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h> #include <assert.h>
#include <inttypes.h>
#include <ctype.h>
#include "toml.h" #include "toml.h"
typedef struct node_t node_t; typedef struct node_t node_t;
@ -43,114 +45,211 @@ struct node_t {
node_t stack[20]; node_t stack[20];
int stacktop = 0; int stacktop = 0;
int indent = 0;
static void prindent()
static void print_table_title(const char* arrname)
{ {
int i; for (int i = 0; i < indent; i++) printf(" ");
printf("%s", arrname ? "[[" : "["); }
for (i = 1; i < stacktop; i++) {
printf("%s", stack[i].key);
if (i + 1 < stacktop) static void print_string(const char* s)
printf("."); {
} int ok = 1;
if (arrname) for (const char* p = s; *p && ok; p++) {
printf(".%s]]\n", arrname); int ch = *p;
else ok = isprint(ch) && ch != '"' && ch != '\\';
printf("]\n"); }
if (ok) {
printf("\"%s\"", s);
return;
}
int len = strlen(s);
printf("\"");
for ( ; len; len--, s++) {
int ch = *s;
if (isprint(ch) && ch != '"' && ch != '\\') {
putchar(ch);
continue;
}
switch (ch) {
case 0x8: printf("\\b"); continue;
case 0x9: printf("\\t"); continue;
case 0xa: printf("\\n"); continue;
case 0xc: printf("\\f"); continue;
case 0xd: printf("\\r"); continue;
case '"': printf("\\\""); continue;
case '\\': printf("\\\\"); continue;
default: printf("\\0x%02x", ch & 0xff); continue;
}
}
printf("\"");
} }
static void print_array_of_tables(toml_array_t* arr, const char* key);
static void print_array(toml_array_t* arr); static void print_array(toml_array_t* arr);
static void print_table(toml_table_t* curtab) static void print_table(toml_table_t* curtab)
{ {
toml_datum_t d;
int i; int i;
const char* key; const char* key;
const char* raw;
toml_array_t* arr; toml_array_t* arr;
toml_table_t* tab; toml_table_t* tab;
for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) { for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) {
if (0 != (raw = toml_raw_in(curtab, key))) {
printf("%s = %s\n", key, raw); if (0 != (arr = toml_array_in(curtab, key))) {
} else if (0 != (arr = toml_array_in(curtab, key))) { prindent();
if (toml_array_kind(arr) == 't') {
print_array_of_tables(arr, key);
}
else {
printf("%s = [\n", key); printf("%s = [\n", key);
indent++;
print_array(arr); print_array(arr);
printf(" ]\n"); indent--;
prindent();
printf("],\n");
continue;
} }
} else if (0 != (tab = toml_table_in(curtab, key))) {
if (0 != (tab = toml_table_in(curtab, key))) {
stack[stacktop].key = key; stack[stacktop].key = key;
stack[stacktop].tab = tab; stack[stacktop].tab = tab;
stacktop++; stacktop++;
print_table_title(0); prindent();
printf("%s = {\n", key);
indent++;
print_table(tab); print_table(tab);
indent--;
prindent();
printf("},\n");
stacktop--; stacktop--;
} else { continue;
abort();
} }
}
}
static void print_array_of_tables(toml_array_t* arr, const char* key) d = toml_string_in(curtab, key);
{ if (d.ok) {
int i; prindent();
toml_table_t* tab; printf("%s = ", key);
printf("\n"); print_string(d.u.s);
for (i = 0; 0 != (tab = toml_table_at(arr, i)); i++) { printf(",\n");
print_table_title(key); free(d.u.s);
print_table(tab); continue;
printf("\n"); }
d = toml_bool_in(curtab, key);
if (d.ok) {
prindent();
printf("%s = %s,\n", key, d.u.b ? "true" : "false");
continue;
}
d = toml_int_in(curtab, key);
if (d.ok) {
prindent();
printf("%s = %" PRId64 ",\n", key, d.u.i);
continue;
}
d = toml_double_in(curtab, key);
if (d.ok) {
prindent();
printf("%s = %g,\n", key, d.u.d);
continue;
}
d = toml_timestamp_in(curtab, key);
if (d.ok) {
prindent();
printf(" %s = %s,\n", key, toml_raw_in(curtab, key));
free(d.u.ts);
continue;
}
abort();
} }
} }
static void print_array(toml_array_t* curarr) static void print_array(toml_array_t* curarr)
{ {
toml_datum_t d;
toml_array_t* arr; toml_array_t* arr;
const char* raw;
toml_table_t* tab; toml_table_t* tab;
int i; const int n = toml_array_nelem(curarr);
switch (toml_array_kind(curarr)) { for (int i = 0; i < n; i++) {
case 'v': if (0 != (arr = toml_array_at(curarr, i))) {
for (i = 0; 0 != (raw = toml_raw_at(curarr, i)); i++) { prindent();
printf(" %d: %s,\n", i, raw); printf("[\n");
} indent++;
break;
case 'a':
for (i = 0; 0 != (arr = toml_array_at(curarr, i)); i++) {
printf(" %d: \n", i);
print_array(arr); print_array(arr);
indent--;
prindent();
printf("],\n");
continue;
} }
break;
case 't': if (0 != (tab = toml_table_at(curarr, i))) {
for (i = 0; 0 != (tab = toml_table_at(curarr, i)); i++) { prindent();
printf("{\n");
indent++;
print_table(tab); print_table(tab);
indent--;
prindent();
printf("},\n");
continue;
} }
printf("\n");
break;
case '\0': d = toml_string_at(curarr, i);
break; if (d.ok) {
prindent();
print_string(d.u.s);
printf(",\n");
free(d.u.s);
continue;
}
d = toml_bool_at(curarr, i);
if (d.ok) {
prindent();
printf("%s,\n", d.u.b ? "true" : "false");
continue;
}
d = toml_int_at(curarr, i);
if (d.ok) {
prindent();
printf("%" PRId64 ",\n", d.u.i);
continue;
}
d = toml_double_at(curarr, i);
if (d.ok) {
prindent();
printf("%g,\n", d.u.d);
continue;
}
d = toml_timestamp_at(curarr, i);
if (d.ok) {
prindent();
printf("%s,\n", toml_raw_at(curarr, i));
free(d.u.ts);
continue;
}
default:
abort(); abort();
} }
} }
static void cat(FILE* fp) static void cat(FILE* fp)
{ {
char errbuf[200]; char errbuf[200];
@ -164,7 +263,11 @@ static void cat(FILE* fp)
stack[stacktop].tab = tab; stack[stacktop].tab = tab;
stack[stacktop].key = ""; stack[stacktop].key = "";
stacktop++; stacktop++;
printf("{\n");
indent++;
print_table(tab); print_table(tab);
indent--;
printf("}\n");
stacktop--; stacktop--;
toml_free(tab); toml_free(tab);