add config support

This commit is contained in:
2026-07-30 13:18:41 +02:00
parent 215ed9705e
commit dc1aa5e5b6
124 changed files with 7402 additions and 8 deletions
@@ -0,0 +1,2 @@
/driver
/out
@@ -0,0 +1,21 @@
CFLAGS := -O0 -g -fsanitize=address,undefined -std=c17 -Wmissing-declarations -Wall -Wextra -MMD
all: driver
driver: scanvalue.c
$(CC) $(CFLAGS) -o $@ scanvalue.c -lm
test: all
bash run.sh
-include driver.d
clean:
rm -f *.o *.d driver
distclean: clean
format:
clang-format -i *.[ch]
.PHONY: all clean distclean format test
@@ -0,0 +1,16 @@
= """
Roses are red
Violets are blue"""
# On a Unix system, the above multi-line string will most likely be the same as:
= "Roses are red\nViolets are blue"
# On a Windows system, it will most likely be equivalent to:
= "Roses are red\r\nViolets are blue"
= """Here are two quotation marks: "". Simple enough."""
= """Here are three quotation marks: ""\"."""
= """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
# "This," she said, "is just a pointless statement."
= """"This," she said, "is just a pointless statement.""""# What you see is what you get.
@@ -0,0 +1,12 @@
# integers
= [ 1, 2, 3 ]
# colors
= [ "red", "yellow", "green" ]
# nested
= [ [ 1, 2 ], [3, 4, 5] ]
= [ [ 1, 2 ], ["a", "b", "c"] ]
= [ "all", 'strings', """are the same""", '''type''' ]
@@ -0,0 +1,6 @@
# Mixed-type arrays are allowed
= [ 0.1, 0.2, 0.5, 1.0, 2, 5 ]
= [
"Foo Bar <foo@example.com>",
{ }
]
@@ -0,0 +1,11 @@
# multiline
= [
1, 2, 3
]
# extra comma at end
= [
1,
2, # this is ok
]
@@ -0,0 +1,2 @@
= { }
@@ -0,0 +1,17 @@
# all results are the same
= "abc"
= """
a\
b\
c"""
= """\
a\
b\
c\
"""
@@ -0,0 +1,9 @@
# line-ending backslash with whitespace "geeee\ "
# result should be "heeee\ngeeee"
= """
heeee
geeee\
"""
@@ -0,0 +1,2 @@
= "\t tab tab tab \t"
= "\e There is no escape! \e"
@@ -0,0 +1 @@
= inf
@@ -0,0 +1,4 @@
= 2023-01-01T12:34:56.123456789Z
= 2023-01-01T12:34:56.123456789
= 07:32:00.123456789
@@ -0,0 +1,11 @@
= 'C:\Users\nodejs\templates'
= '\\ServerX\admin$\system32\'
= 'Tom "Dubs" Preston-Werner'
= '<\i\c*\s*>'
= '''I [dw]on't need \d{2} apples'''
= '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''
@@ -0,0 +1,7 @@
= '''Here are fifteen quotation marks: """""""""""""""'''
# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID
= "Here are fifteen apostrophes: '''''''''''''''"
# 'That,' she said, 'is still pointless.'
= ''''That,' she said, 'is still pointless.''''
@@ -0,0 +1,10 @@
= "pi"
= +99
= 42
= 0
= -17
= 1_000
= 5_349_221
= 53_49_221 # Indian number system grouping
= 1_2_3_4_5 # VALID but discouraged
@@ -0,0 +1,11 @@
# hexadecimal with prefix `0x`
= 0xDEADBEEF
= 0xdeadbeef
= 0xdead_beef
# octal with prefix `0o`
= 0o01234567
= 0o755 # useful for Unix file permissions
# binary with prefix `0b`
= 0b11010110
@@ -0,0 +1,15 @@
# fractional
= +1.0
= 3.1415
= -0.01
# exponent
= 5e+22
= 1e06
= -2E-2
# both
= 6.626e-34
= 224_617.445_991_228
@@ -0,0 +1,10 @@
# infinity
= inf # positive infinity
= +inf # positive infinity
= -inf # negative infinity
# not a number
= nan # actual sNaN/qNaN encoding is implementation-specific
= +nan # same as `nan`
= -nan # valid, actual encoding is implementation-specific
@@ -0,0 +1,3 @@
= true
= false
@@ -0,0 +1,13 @@
= 1979-05-27T07:32:00Z
= 1979-05-27T00:32:00-07:00
= 1979-05-27T00:32:00.999999-07:00
= 1979-05-27 07:32:00Z
= 1979-05-27T07:32:00
= 1979-05-27T00:32:00.999999
= 1979-05-27
= 07:32:00
= 00:32:00.999999
# lower
= 1987-07-05t17:45:00z
@@ -0,0 +1,2 @@
# trailing .
= 1997-09-09T09:09:09.
@@ -0,0 +1,2 @@
# timezone offset overflow minute
= 1985-06-18 17:04:07+12:60
@@ -0,0 +1,20 @@
#!/bin/bash
mkdir -p out
echo
echo =========================
echo == scanvalue test
echo =========================
for fname in {1..100} e{1..100}; do
IN="in/$fname"
if [ -f $IN ]; then
echo test $fname
OUT="out/$fname.out"
GOOD="good/$fname.out"
./driver $IN &> $OUT
diff $GOOD $OUT || { echo '--- FAILED ---'; exit 1; }
fi
done
echo DONE
@@ -0,0 +1,161 @@
#include "../../src/tomlc17.c"
#include <inttypes.h>
#include <stdlib.h>
const char **g_argv = 0;
int g_argc = 0;
static void usage() {
fprintf(stderr, "Usage: %s fname\n", g_argv[0]);
exit(1);
}
static void printspecial(const char *p, int n) {
for (int i = 0; i < n; i++, p++) {
int ch = (*p == '\n') ? '_' : *p;
putchar(ch);
}
}
static const char *fmt_double(double f, char *buf, int buflen) {
snprintf(buf, buflen, "%.16g", f);
if (!strchr(buf, 'e') && !strchr(buf, '.') && !strchr(buf, 'n')) {
// add a .0 if the number is an int, and not inf or nan.
snprintf(buf, buflen, "%.16g%s", f, ".0");
}
if (isnan(f) && signbit(f)) {
snprintf(buf, buflen, "%s", "-nan");
}
return buf;
}
static void printtok(char *content, const token_t tok) {
// clang-format off
#define CASESTR(x) case TOK_ ## x: s = #x; break
// clang-format on
char *s = 0;
switch (tok.toktyp) {
CASESTR(DOT);
CASESTR(EQUAL);
CASESTR(COMMA);
CASESTR(LBRACK);
CASESTR(RBRACK);
CASESTR(LBRACE);
CASESTR(RBRACE);
CASESTR(STRING);
CASESTR(MLSTRING);
CASESTR(LITSTRING);
CASESTR(MLLITSTRING);
CASESTR(TIME);
CASESTR(DATE);
CASESTR(DATETIME);
CASESTR(DATETIMETZ);
CASESTR(INTEGER);
CASESTR(FLOAT);
CASESTR(BOOL);
CASESTR(LIT);
CASESTR(ENDL);
CASESTR(FIN);
default:
s = "UNKNOWN";
break;
}
printf("%s %ld %d ", s, tok.str.ptr - content, tok.str.len);
printspecial(tok.str.ptr, tok.str.len);
char buf[50];
switch (tok.toktyp) {
case TOK_INTEGER:
printf(" %" PRId64, tok.u.int64);
break;
case TOK_FLOAT:
printf(" %s", fmt_double(tok.u.fp64, buf, sizeof(buf)));
break;
case TOK_BOOL:
printf(" %s", tok.u.b1 ? "true" : "false");
break;
default:
break;
}
printf("\n");
}
static char *readfile(const char *fname, int *ret_len) {
FILE *fp = fopen(fname, "r");
if (!fp) {
perror("fopen");
exit(1);
}
// Seek to the end of the file to determine its size
if (fseek(fp, 0, SEEK_END) != 0) {
perror("fseek");
exit(1);
}
long file_size = ftell(fp);
if (file_size == -1) {
perror("ftell");
exit(1);
}
rewind(fp); // Go back to the beginning of the file
// Allocate memory for the file content, plus one for the null terminator
char *content = malloc(file_size + 1);
if (!content) {
perror("out of memory");
exit(1);
}
// Read the file into the buffer
size_t read_size = fread(content, 1, file_size, fp);
if (read_size != (size_t)file_size) {
perror("fread");
exit(1);
}
content[file_size] = '\0'; // Null-terminate the string
fclose(fp);
*ret_len = file_size;
return content;
}
int main(int argc, const char *argv[]) {
g_argc = argc;
g_argv = argv;
if (argc != 2) {
usage();
}
int len;
char *content = readfile(argv[1], &len);
char errbuf[200];
scanner_t scanner;
scanner_t *sp = &scanner;
scan_init(sp, content, len, errbuf, sizeof(errbuf));
for (;;) {
(void)scan_key; // silent compiler
token_t tok;
if (scan_value(sp, &tok)) {
printf("%s\n", errbuf);
goto bail;
}
if (tok.toktyp == TOK_FIN) {
break;
}
printtok(content, tok);
}
if (sp->errmsg) {
printf("ERROR: %s (line %d)\n", sp->errmsg, sp->lineno);
goto bail;
}
free(content);
return 0;
bail:
free(content);
return 1;
}