fixed issue #51
This commit is contained in:
parent
1cfa8e42c4
commit
715fa54d45
55 changed files with 747 additions and 366 deletions
1
stdex/.gitignore
vendored
Normal file
1
stdex/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*.out
|
15
stdex/RUN.sh
Normal file
15
stdex/RUN.sh
Normal 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
3
stdex/comment.toml
Normal 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
4
stdex/comment.toml.res
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
key = "value",
|
||||
another = "# This is not a comment",
|
||||
}
|
4
stdex/keys00.toml
Normal file
4
stdex/keys00.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
key = "value"
|
||||
bare_key = "value"
|
||||
bare-key = "value"
|
||||
1234 = "value"
|
6
stdex/keys00.toml.res
Normal file
6
stdex/keys00.toml.res
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
key = "value",
|
||||
bare_key = "value",
|
||||
bare-key = "value",
|
||||
1234 = "value",
|
||||
}
|
5
stdex/keys01.toml
Normal file
5
stdex/keys01.toml
Normal 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
7
stdex/keys01.toml.res
Normal 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
1
stdex/keys02.toml
Normal file
|
@ -0,0 +1 @@
|
|||
= "no key name" # INVALID
|
1
stdex/keys02.toml.res
Normal file
1
stdex/keys02.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 1: syntax error
|
1
stdex/keys03.toml
Normal file
1
stdex/keys03.toml
Normal file
|
@ -0,0 +1 @@
|
|||
"" = "blank" # VALID but discouraged
|
3
stdex/keys03.toml.res
Normal file
3
stdex/keys03.toml.res
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
= "blank",
|
||||
}
|
4
stdex/keys04.toml
Normal file
4
stdex/keys04.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
name = "Orange"
|
||||
physical.color = "orange"
|
||||
physical.shape = "round"
|
||||
site."google.com" = true
|
10
stdex/keys04.toml.res
Normal file
10
stdex/keys04.toml.res
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
name = "Orange",
|
||||
physical = {
|
||||
color = "orange",
|
||||
shape = "round",
|
||||
},
|
||||
site = {
|
||||
google.com = true,
|
||||
},
|
||||
}
|
3
stdex/keys05.toml
Normal file
3
stdex/keys05.toml
Normal 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
7
stdex/keys05.toml.res
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
fruit = {
|
||||
name = "banana",
|
||||
color = "yellow",
|
||||
flavor = "banana",
|
||||
},
|
||||
}
|
3
stdex/keys06.toml
Normal file
3
stdex/keys06.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# DO NOT DO THIS
|
||||
name = "Tom"
|
||||
name = "Pradyun"
|
1
stdex/keys06.toml.res
Normal file
1
stdex/keys06.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 3: key exists
|
3
stdex/keys07.toml
Normal file
3
stdex/keys07.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# THIS WILL NOT WORK
|
||||
spelling = "favorite"
|
||||
"spelling" = "favourite"
|
1
stdex/keys07.toml.res
Normal file
1
stdex/keys07.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 3: key exists
|
5
stdex/keys08.toml
Normal file
5
stdex/keys08.toml
Normal 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
8
stdex/keys08.toml.res
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
fruit = {
|
||||
orange = 2,
|
||||
apple = {
|
||||
smooth = true,
|
||||
},
|
||||
},
|
||||
}
|
8
stdex/keys09.toml
Normal file
8
stdex/keys09.toml
Normal 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
1
stdex/keys09.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 8: key exists
|
10
stdex/keys10.toml
Normal file
10
stdex/keys10.toml
Normal 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
12
stdex/keys10.toml.res
Normal 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
9
stdex/keys11.toml
Normal 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
12
stdex/keys11.toml.res
Normal 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
1
stdex/keys12.toml
Normal file
|
@ -0,0 +1 @@
|
|||
3.14159 = "pi"
|
5
stdex/keys12.toml.res
Normal file
5
stdex/keys12.toml.res
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
3 = {
|
||||
14159 = "pi",
|
||||
},
|
||||
}
|
1
stdex/kvpair0.toml
Normal file
1
stdex/kvpair0.toml
Normal file
|
@ -0,0 +1 @@
|
|||
key = "value"
|
3
stdex/kvpair0.toml.res
Normal file
3
stdex/kvpair0.toml.res
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
key = "value",
|
||||
}
|
1
stdex/kvpair1.toml
Normal file
1
stdex/kvpair1.toml
Normal file
|
@ -0,0 +1 @@
|
|||
key = # INVALID
|
1
stdex/kvpair1.toml.res
Normal file
1
stdex/kvpair1.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 1: syntax error
|
1
stdex/kvpair2.toml
Normal file
1
stdex/kvpair2.toml
Normal file
|
@ -0,0 +1 @@
|
|||
first = "Tom" last = "Preston-Werner" # INVALID
|
1
stdex/kvpair2.toml.res
Normal file
1
stdex/kvpair2.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 1: extra chars after value
|
1
stdex/string0.toml
Normal file
1
stdex/string0.toml
Normal 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
3
stdex/string0.toml.res
Normal 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
9
stdex/string1.toml
Normal 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
5
stdex/string1.toml.res
Normal 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
15
stdex/string3.toml
Normal 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
5
stdex/string3.toml.res
Normal 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
7
stdex/string4.toml
Normal 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
6
stdex/string4.toml.res
Normal 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
5
stdex/string5.toml
Normal 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
6
stdex/string5.toml.res
Normal 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
7
stdex/string6.toml
Normal 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
4
stdex/string6.toml.res
Normal 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
4
stdex/string7.toml
Normal 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
4
stdex/string7.toml.res
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
quot15 = "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"",
|
||||
str = "'That,' she said, 'is still pointless.'",
|
||||
}
|
3
stdex/string8.toml
Normal file
3
stdex/string8.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID
|
||||
apos15 = "Here are fifteen apostrophes: '''''''''''''''"
|
||||
|
1
stdex/string8.toml.res
Normal file
1
stdex/string8.toml.res
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: line 2: triple-s-quote inside string lit
|
257
toml.c
257
toml.c
|
@ -2,7 +2,7 @@
|
|||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 - 2019 CK Tan
|
||||
Copyright (c) 2017 - 2021 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
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 {
|
||||
const char* key; /* key to this array */
|
||||
int kind; /* element kind: 'v'alue, 'a'rray, or 't'able */
|
||||
int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp */
|
||||
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, 'm'ixed */
|
||||
|
||||
int nelem; /* number of elements */
|
||||
union {
|
||||
char** val;
|
||||
toml_array_t** arr;
|
||||
toml_table_t** tab;
|
||||
} u;
|
||||
int nitem; /* number of elements */
|
||||
toml_arritem_t* item;
|
||||
};
|
||||
|
||||
|
||||
|
@ -408,6 +412,15 @@ static void** expand_ptrarr(void** p, int n)
|
|||
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,
|
||||
int multiline,
|
||||
|
@ -825,26 +838,41 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
|
|||
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
|
||||
*/
|
||||
static toml_array_t* create_array_in_array(context_t* ctx,
|
||||
toml_array_t* parent)
|
||||
{
|
||||
const int n = parent->nelem;
|
||||
toml_array_t** base;
|
||||
if (0 == (base = (toml_array_t**) expand_ptrarr((void**)parent->u.arr, n))) {
|
||||
const int n = parent->nitem;
|
||||
toml_arritem_t* base = expand_arritem(parent->item, n);
|
||||
if (!base) {
|
||||
e_outofmemory(ctx, FLINE);
|
||||
return 0;
|
||||
}
|
||||
parent->u.arr = base;
|
||||
parent->nelem++;
|
||||
|
||||
if (0 == (base[n] = (toml_array_t*) CALLOC(1, sizeof(*base[n])))) {
|
||||
toml_array_t* ret = (toml_array_t*) CALLOC(1, sizeof(toml_array_t));
|
||||
if (!ret) {
|
||||
e_outofmemory(ctx, FLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parent->u.arr[n];
|
||||
base[n].arr = ret;
|
||||
parent->item = base;
|
||||
parent->nitem++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
toml_array_t* parent)
|
||||
{
|
||||
int n = parent->nelem;
|
||||
toml_table_t** base;
|
||||
if (0 == (base = (toml_table_t**) expand_ptrarr((void**)parent->u.tab, n))) {
|
||||
int n = parent->nitem;
|
||||
toml_arritem_t* base = expand_arritem(parent->item, n);
|
||||
if (!base) {
|
||||
e_outofmemory(ctx, FLINE);
|
||||
return 0;
|
||||
}
|
||||
parent->u.tab = base;
|
||||
|
||||
if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n])))) {
|
||||
toml_table_t* ret = (toml_table_t*) CALLOC(1, sizeof(toml_table_t));
|
||||
if (!ret) {
|
||||
e_outofmemory(ctx, FLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parent->u.tab[parent->nelem++];
|
||||
base[n].tab = ret;
|
||||
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) {
|
||||
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;
|
||||
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 */
|
||||
char** tmp = (char**) expand_ptrarr((void**)arr->u.val, arr->nelem);
|
||||
if (!tmp)
|
||||
toml_arritem_t* newval = create_value_in_array(ctx, arr);
|
||||
if (!newval)
|
||||
return e_outofmemory(ctx, FLINE);
|
||||
|
||||
arr->u.val = tmp;
|
||||
if (! (val = STRNDUP(val, vlen)))
|
||||
if (! (newval->val = STRNDUP(val, vlen)))
|
||||
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. */
|
||||
if (arr->nelem == 1)
|
||||
arr->type = valtype(arr->u.val[0]);
|
||||
else if (arr->type != valtype(val)) {
|
||||
return e_syntax(ctx, ctx->tok.lineno,
|
||||
"array type mismatch while processing array of values");
|
||||
}
|
||||
/* set array type if this is the first entry */
|
||||
if (arr->nitem == 1)
|
||||
arr->type = newval->valtype;
|
||||
else if (arr->type != newval->valtype)
|
||||
arr->type = 'm'; /* mixed */
|
||||
|
||||
if (eat_token(ctx, STRING, 0, FLINE)) return -1;
|
||||
break;
|
||||
|
@ -998,12 +1024,11 @@ static int parse_array(context_t* ctx, toml_array_t* arr)
|
|||
case LBRACKET:
|
||||
{ /* [ [array], [array] ... ] */
|
||||
/* set the array kind if this will be the first entry */
|
||||
if (arr->kind == 0) arr->kind = 'a';
|
||||
/* check array kind */
|
||||
if (arr->kind != 'a') {
|
||||
return e_syntax(ctx, ctx->tok.lineno,
|
||||
"array type mismatch while processing array of arrays");
|
||||
}
|
||||
if (arr->kind == 0)
|
||||
arr->kind = 'a';
|
||||
else if (arr->kind != 'a')
|
||||
arr->kind = 'm';
|
||||
|
||||
toml_array_t* subarr = create_array_in_array(ctx, arr);
|
||||
if (!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:
|
||||
{ /* [ {table}, {table} ... ] */
|
||||
/* set the array kind if this will be the first entry */
|
||||
if (arr->kind == 0) arr->kind = 't';
|
||||
/* check array kind */
|
||||
if (arr->kind != 't') {
|
||||
return e_syntax(ctx, ctx->tok.lineno,
|
||||
"array type mismatch while processing array of tables");
|
||||
}
|
||||
if (arr->kind == 0)
|
||||
arr->kind = 't';
|
||||
else if (arr->kind != 't')
|
||||
arr->kind = 'm';
|
||||
|
||||
toml_table_t* subtab = create_table_in_array(ctx, arr);
|
||||
if (!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')
|
||||
return e_internal(ctx, FLINE);
|
||||
|
||||
if (nextarr->nelem == 0)
|
||||
if (nextarr->nitem == 0)
|
||||
return e_internal(ctx, FLINE);
|
||||
|
||||
nexttab = nextarr->u.tab[nextarr->nelem-1];
|
||||
nexttab = nextarr->item[nextarr->nitem-1].tab;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
|
@ -1294,20 +1318,13 @@ static int parse_select(context_t* ctx)
|
|||
/* add to z[] */
|
||||
toml_table_t* dest;
|
||||
{
|
||||
int n = arr->nelem;
|
||||
toml_table_t** base = (toml_table_t**) expand_ptrarr((void**)arr->u.tab, n);
|
||||
if (0 == base)
|
||||
toml_table_t* t = create_table_in_array(ctx, arr);
|
||||
if (!t) return -1;
|
||||
|
||||
if (0 == (t->key = STRDUP("__anon__")))
|
||||
return e_outofmemory(ctx, FLINE);
|
||||
|
||||
arr->u.tab = base;
|
||||
|
||||
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++];
|
||||
dest = t;
|
||||
}
|
||||
|
||||
ctx->curtab = dest;
|
||||
|
@ -1479,23 +1496,17 @@ static void xfree_arr(toml_array_t* p)
|
|||
if (!p) return;
|
||||
|
||||
xfree(p->key);
|
||||
switch (p->kind) {
|
||||
case 'v':
|
||||
for (int i = 0; i < p->nelem; i++) xfree(p->u.val[i]);
|
||||
xfree(p->u.val);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
for (int i = 0; i < p->nelem; i++) xfree_arr(p->u.arr[i]);
|
||||
xfree(p->u.arr);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
for (int i = 0; i < p->nelem; i++) xfree_tab(p->u.tab[i]);
|
||||
xfree(p->u.tab);
|
||||
break;
|
||||
const int n = p->nitem;
|
||||
for (int i = 0; i < n; i++) {
|
||||
toml_arritem_t* a = &p->item[i];
|
||||
if (a->val)
|
||||
xfree(a->val);
|
||||
else if (a->arr)
|
||||
xfree_arr(a->arr);
|
||||
else if (a->tab)
|
||||
xfree_tab(a->tab);
|
||||
}
|
||||
|
||||
xfree(p->item);
|
||||
xfree(p);
|
||||
}
|
||||
|
||||
|
@ -1584,20 +1595,44 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
|
|||
{
|
||||
char* orig = p;
|
||||
if (0 == strncmp(p, "'''", 3)) {
|
||||
p = strstr(p + 3, "'''");
|
||||
if (0 == p) {
|
||||
char* q = p + 3;
|
||||
|
||||
while (1) {
|
||||
q = strstr(q, "'''");
|
||||
if (0 == q) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 escape = 0;
|
||||
int qcnt = 0; /* count quote */
|
||||
for (p += 3; *p && qcnt < 3; p++) {
|
||||
for (p += 3; p < q; p++) {
|
||||
if (escape) {
|
||||
escape = 0;
|
||||
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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1633,6 +1671,7 @@ static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
|
|||
}
|
||||
|
||||
if ('\"' == *p) {
|
||||
char* tsq = strstr(p, "\'\'\'");
|
||||
int hexreq = 0; /* #hex required */
|
||||
int escape = 0;
|
||||
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");
|
||||
}
|
||||
|
||||
if (tsq && tsq < p) {
|
||||
return e_syntax(ctx, lineno, "triple-s-quote inside string lit");
|
||||
}
|
||||
|
||||
set_token(ctx, STRING, lineno, orig, p + 1 - orig);
|
||||
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)
|
||||
{
|
||||
if (arr->kind != 'v')
|
||||
return 0;
|
||||
if (! (0 <= idx && idx < arr->nelem))
|
||||
return 0;
|
||||
return arr->u.val[idx];
|
||||
return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0;
|
||||
}
|
||||
|
||||
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')
|
||||
return 0;
|
||||
|
||||
if (arr->nelem == 0)
|
||||
if (arr->nitem == 0)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
{
|
||||
return arr->nelem;
|
||||
return arr->nitem;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (arr->kind != 'a')
|
||||
return 0;
|
||||
if (! (0 <= idx && idx < arr->nelem))
|
||||
return 0;
|
||||
return arr->u.arr[idx];
|
||||
return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0;
|
||||
}
|
||||
|
||||
toml_table_t* toml_table_at(const toml_array_t* arr, int idx)
|
||||
{
|
||||
if (arr->kind != 't')
|
||||
return 0;
|
||||
if (! (0 <= idx && idx < arr->nelem))
|
||||
return 0;
|
||||
return arr->u.tab[idx];
|
||||
return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
4
toml.h
4
toml.h
|
@ -124,11 +124,11 @@ TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
|
|||
/*-----------------------------------------------------------------
|
||||
* 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);
|
||||
|
||||
/* 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
|
||||
*/
|
||||
TOML_EXTERN char toml_array_type(const toml_array_t* arr);
|
||||
|
|
257
toml_cat.c
257
toml_cat.c
|
@ -1,26 +1,26 @@
|
|||
/*
|
||||
MIT License
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
Copyright (c) 2017 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef NDEBUG
|
||||
|
@ -33,6 +33,8 @@ SOFTWARE.
|
|||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include "toml.h"
|
||||
|
||||
typedef struct node_t node_t;
|
||||
|
@ -43,114 +45,211 @@ struct node_t {
|
|||
|
||||
node_t stack[20];
|
||||
int stacktop = 0;
|
||||
int indent = 0;
|
||||
|
||||
|
||||
static void print_table_title(const char* arrname)
|
||||
static void prindent()
|
||||
{
|
||||
int i;
|
||||
printf("%s", arrname ? "[[" : "[");
|
||||
for (i = 1; i < stacktop; i++) {
|
||||
printf("%s", stack[i].key);
|
||||
if (i + 1 < stacktop)
|
||||
printf(".");
|
||||
}
|
||||
if (arrname)
|
||||
printf(".%s]]\n", arrname);
|
||||
else
|
||||
printf("]\n");
|
||||
for (int i = 0; i < indent; i++) printf(" ");
|
||||
}
|
||||
|
||||
|
||||
static void print_string(const char* s)
|
||||
{
|
||||
int ok = 1;
|
||||
for (const char* p = s; *p && ok; p++) {
|
||||
int ch = *p;
|
||||
ok = isprint(ch) && ch != '"' && ch != '\\';
|
||||
}
|
||||
|
||||
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_table(toml_table_t* curtab)
|
||||
{
|
||||
toml_datum_t d;
|
||||
int i;
|
||||
const char* key;
|
||||
const char* raw;
|
||||
toml_array_t* arr;
|
||||
toml_table_t* tab;
|
||||
|
||||
|
||||
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);
|
||||
} else if (0 != (arr = toml_array_in(curtab, key))) {
|
||||
if (toml_array_kind(arr) == 't') {
|
||||
print_array_of_tables(arr, key);
|
||||
}
|
||||
else {
|
||||
|
||||
if (0 != (arr = toml_array_in(curtab, key))) {
|
||||
prindent();
|
||||
printf("%s = [\n", key);
|
||||
indent++;
|
||||
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].tab = tab;
|
||||
stacktop++;
|
||||
print_table_title(0);
|
||||
prindent();
|
||||
printf("%s = {\n", key);
|
||||
indent++;
|
||||
print_table(tab);
|
||||
indent--;
|
||||
prindent();
|
||||
printf("},\n");
|
||||
stacktop--;
|
||||
} else {
|
||||
abort();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_array_of_tables(toml_array_t* arr, const char* key)
|
||||
{
|
||||
int i;
|
||||
toml_table_t* tab;
|
||||
printf("\n");
|
||||
for (i = 0; 0 != (tab = toml_table_at(arr, i)); i++) {
|
||||
print_table_title(key);
|
||||
print_table(tab);
|
||||
printf("\n");
|
||||
d = toml_string_in(curtab, key);
|
||||
if (d.ok) {
|
||||
prindent();
|
||||
printf("%s = ", key);
|
||||
print_string(d.u.s);
|
||||
printf(",\n");
|
||||
free(d.u.s);
|
||||
continue;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
toml_datum_t d;
|
||||
toml_array_t* arr;
|
||||
const char* raw;
|
||||
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':
|
||||
for (i = 0; 0 != (raw = toml_raw_at(curarr, i)); i++) {
|
||||
printf(" %d: %s,\n", i, raw);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
for (i = 0; 0 != (arr = toml_array_at(curarr, i)); i++) {
|
||||
printf(" %d: \n", i);
|
||||
if (0 != (arr = toml_array_at(curarr, i))) {
|
||||
prindent();
|
||||
printf("[\n");
|
||||
indent++;
|
||||
print_array(arr);
|
||||
indent--;
|
||||
prindent();
|
||||
printf("],\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
for (i = 0; 0 != (tab = toml_table_at(curarr, i)); i++) {
|
||||
if (0 != (tab = toml_table_at(curarr, i))) {
|
||||
prindent();
|
||||
printf("{\n");
|
||||
indent++;
|
||||
print_table(tab);
|
||||
indent--;
|
||||
prindent();
|
||||
printf("},\n");
|
||||
continue;
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
break;
|
||||
d = toml_string_at(curarr, i);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void cat(FILE* fp)
|
||||
{
|
||||
char errbuf[200];
|
||||
|
@ -164,7 +263,11 @@ static void cat(FILE* fp)
|
|||
stack[stacktop].tab = tab;
|
||||
stack[stacktop].key = "";
|
||||
stacktop++;
|
||||
printf("{\n");
|
||||
indent++;
|
||||
print_table(tab);
|
||||
indent--;
|
||||
printf("}\n");
|
||||
stacktop--;
|
||||
|
||||
toml_free(tab);
|
||||
|
|
Loading…
Reference in a new issue