proper implementation in C

This commit is contained in:
anon 2024-09-14 01:56:14 +02:00
parent 475d2976d9
commit b2912528bd
11 changed files with 2550 additions and 64 deletions

@ -1,9 +1,24 @@
.PHONY: main test
main:
g++ source/tbc.cpp $$(pkg-config --cflags --libs tree-sitter tree-sitter-c) -ggdb
CFLAGS := -std=c2x -Wall -Wpedantic
test:
python source/tbc.py test/convert.tbsp > object/kek.cpp
bake object/kek.cpp
./object/a.out test/input.md
ifeq (${DEBUG}, 1)
LFLAGS += --debug --trace
YFLAGS += --debug
CFLAGS += -O0 -ggdb -fno-inline
CPPFLAGS += -DDEBUG
else
CFLAGS += -O3 -flto=auto -fno-stack-protector
endif
OUT := tbsp
main:
bison ${YFLAGS} --header=object/tbsp.tab.h -o object/tbsp.tab.c source/tbsp.y
flex ${LFLAGS} --header-file=object/tbsp.yy.h -o object/tbsp.yy.c source/tbsp.l
gcc ${CPPFLAGS} ${CFLAGS} -Iobject -Ilibrary object/tbsp.tab.c object/tbsp.yy.c source/tbsp.c library/sds.c -o ${OUT}
run:
./${OUT} test/convert.tbsp > object/test.cpp
bake object/test.cpp
./object/test.out test/input.md

90
library/kvec.h Normal file

@ -0,0 +1,90 @@
/* The MIT License
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
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 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.
*/
/*
An example:
#include "kvec.h"
int main() {
kvec_t(int) array;
kv_init(array);
kv_push(int, array, 10); // append
kv_a(int, array, 20) = 5; // dynamic
kv_A(array, 20) = 4; // static
kv_destroy(array);
return 0;
}
*/
/*
2008-09-22 (0.1.0):
* The initial version.
*/
#ifndef AC_KVEC_H
#define AC_KVEC_H
#include <stdlib.h>
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#define kvec_t(type) struct { size_t n, m; type *a; }
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
#define kv_destroy(v) free((v).a)
#define kv_A(v, i) ((v).a[(i)])
#define kv_pop(v) ((v).a[--(v).n])
#define kv_size(v) ((v).n)
#define kv_max(v) ((v).m)
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
#define kv_copy(type, v1, v0) do { \
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
(v1).n = (v0).n; \
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
} while (0) \
#define kv_push(type, v, x) do { \
if ((v).n == (v).m) { \
(v).m = (v).m? (v).m<<1 : 2; \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
} \
(v).a[(v).n++] = (x); \
} while (0)
#define kv_pushp(type, v) (((v).n == (v).m)? \
((v).m = ((v).m? (v).m<<1 : 2), \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
: 0), ((v).a + ((v).n++))
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
: 0), (v).a[(i)])
#endif

1603
library/sds.c Normal file

File diff suppressed because it is too large Load Diff

155
library/sds.h Normal file

@ -0,0 +1,155 @@
/* SDSLib 2.3 -- A C dynamic strings library
*
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2015, Oran Agra
* Copyright (c) 2015, Redis Labs, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SDS_H
#define __SDS_H
#define SDS_MAX_PREALLOC (1024*1024)
extern const char *SDS_NOINIT;
#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>
typedef char *sds;
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
#define SDS_TYPE_5 0
#define SDS_TYPE_8 1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
size_t sdslen(const sds s);
size_t sdsavail(const sds s);
void sdssetlen(sds s, size_t newlen);
void sdsinclen(sds s, size_t inc);
size_t sdsalloc(const sds s);
void sdssetalloc(sds s, size_t newlen);
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty(void);
sds sdsdup(const sds s);
void sdsfree(sds s);
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscatsds(sds s, const sds t);
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
#endif
sds sdscatfmt(sds s, char const *fmt, ...);
void sdstrim(sds s, const char *cset);
void sdssubstr(sds s, size_t start, size_t len);
void sdsrange(sds s, ssize_t start, ssize_t end);
void sdsupdatelen(sds s);
void sdsclear(sds s);
int sdscmp(const sds s1, const sds s2);
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, const char *p, size_t len);
sds *sdssplitargs(const char *line, int *argc);
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
sds sdsjoin(char **argv, int argc, char *sep);
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
int sdsneedsrepr(const sds s);
/* Low level functions exposed to the user API */
sds sdsMakeRoomFor(sds s, size_t addlen);
void sdsIncrLen(sds s, ssize_t incr);
sds sdsRemoveFreeSpace(sds s);
sds sdsResize(sds s, size_t size);
size_t sdsAllocSize(sds s);
void *sdsAllocPtr(sds s);
/* Export the allocator used by SDS to the program using SDS.
* Sometimes the program SDS is linked to, may use a different set of
* allocators, but may want to allocate or free things that SDS will
* respectively free or allocate. */
void *sds_malloc(size_t size);
void *sds_realloc(void *ptr, size_t size);
void sds_free(void *ptr);
#ifdef REDIS_TEST
int sdsTest(void);
#endif
#undef inline
#endif

47
library/sdsalloc.h Normal file

@ -0,0 +1,47 @@
/* SDSLib 2.2 -- A C dynamic strings library
*
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2015, Oran Agra
* Copyright (c) 2015, Redis Labs, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* SDS allocator selection.
*
* This file is used in order to change the SDS allocator at compile time.
* Just define the following defines to what you want to use. Also add
* the include of your alternate allocator if needed (not needed in order
* to use the default libc allocator). */
#ifndef __SDS_ALLOC_H__
#define __SDS_ALLOC_H__
#define s_malloc malloc
#define s_realloc realloc
#define s_free free
#endif

122
source/TBSP_strings.inc Normal file

@ -0,0 +1,122 @@
const char * const TBSP_header = "\
#include <stdio.h>\n\
#include <string.h>\n\
\n\
#include <tree_sitter/api.h>\n\
#ifdef __cplusplus\n\
extern \"C\" {\n\
#endif\n\
extern const TSLanguage * tree_sitter_%s(void);\n\
#ifdef __cplusplus\n\
}\n\
#endif\n\
const TSLanguage * (*tblanguage_function)(void) = tree_sitter_%s;\n\
\n\
typedef struct {\n\
const char * const string;\n\
const int case_number;\n\
} tbcase_t;\n\
\n\
// XXX better search algo\n\
int determine_case(const tbcase_t * const ordered_array, const char * const string) {\n\
const tbcase_t * c = ordered_array;\n\
for (; c->string != NULL; c++) {\n\
if (!strcmp(c->string, string)) { break; }\n\
}\n\
\n\
return c->case_number;\n\
}\n\
\n\
char * tbtext(const char * const code, TSNode node) {\n\
int tblen = ts_node_end_byte(node) - ts_node_start_byte(node);\n\
char * r = (char *)malloc(sizeof(char) * (tblen + 1));\n\
\n\
memcpy(r, code + ts_node_start_byte(node), tblen);\n\
r[tblen] = '\\0';\n\
\n\
return r;\n\
}\n\
\n\
#define GET_TBTEXT tbtext(code, current_node)\n\
";
const char * const TBSP_case = "\
(tbcase_t) { .string = \"%s\", .case_number = %d },\n\
";
const char * const TBSP_traverse_top = "\
int tbtraverse(const char * const code) {\n\
// init\n\
TSParser * parser;\n\
TSTree * tree;\n\
TSTreeCursor cursor;\n\
TSNode current_node;\n\
\n\
int tb_case;\n\
\n\
parser = ts_parser_new();\n\
\n\
ts_parser_set_language(parser, tblanguage_function());\n\
\n\
tree = ts_parser_parse_string(parser, NULL, code, strlen(code));\n\
cursor = ts_tree_cursor_new(ts_tree_root_node(tree));\n\
current_node = ts_tree_root_node(tree);\n\
\n\
const tbcase_t * current_cases = tb_enter_cases;\n\
\n\
// meat\n\
while (true) {\n\
current_node = ts_tree_cursor_current_node(&cursor);\n\
\n\
tb_case = determine_case(current_cases, ts_node_type(current_node));\n\
\n\
// XXX INJECTION\n\
#if defined(TBDEBUG) && TBDEBUG == 1\n\
puts(ts_node_string(current_node));\n\
#endif\n\
switch (tb_case) {\n\
";
const char * const TBSP_traverse_bottom = "\
default: { ; } break;\n\
}\n\
\n\
if (ts_node_child_count(current_node)\n\
&& current_cases == tb_enter_cases) {\n\
ts_tree_cursor_goto_first_child(&cursor);\n\
continue;\n\
}\n\
\n\
logic:\n\
if (!ts_node_is_null(ts_node_next_sibling(current_node))) {\n\
if (current_cases == tb_enter_cases) {\n\
current_cases = tb_leave_cases;\n\
continue;\n\
} else {\n\
ts_tree_cursor_goto_next_sibling(&cursor);\n\
current_cases = tb_enter_cases;\n\
continue;\n\
}\n\
}\n\
\n\
if (current_cases == tb_enter_cases) {\n\
current_cases = tb_leave_cases;\n\
continue;\n\
}\n\
\n\
if (ts_tree_cursor_goto_parent(&cursor)) {\n\
current_cases = tb_enter_cases;\n\
goto logic;\n\
}\n\
\n\
break;\n\
}\n\
\n\
// deinit\n\
ts_tree_delete(tree);\n\
ts_parser_delete(parser);\n\
ts_tree_cursor_delete(&cursor);\n\
\n\
return 0;\n\
}\n\
";

116
source/tbc.c Normal file

@ -0,0 +1,116 @@
#include <stdio.h>
#include <string.h>
#include <tree_sitter/api.h>
extern const TSLanguage * tree_sitter_c(void);
typedef struct {
const char * const string;
const int case_number;
} tbcase_t;
const tbcase_t tb_enter_cases[] = {
(tbcase_t) { .string = "function_definition", .case_number = 1 },
(tbcase_t) { .string = "number_literal", .case_number = 2 },
(tbcase_t) { .string = NULL, .case_number = 0 },
};
const tbcase_t tb_leave_cases[] = {
(tbcase_t) { .string = "function_definition", .case_number = 3 },
(tbcase_t) { .string = NULL, .case_number = 0 },
};
// XXX better search algo
int determine_case(const tbcase_t * const ordered_array, const char * const string) {
const tbcase_t * c = ordered_array;
for (; c->string != NULL; c++) {
if (!strcmp(c->string, string)) { break; }
}
return c->case_number;
}
char * tbtext(const char * const code, TSNode node) {
int tblen = ts_node_end_byte(node) - ts_node_start_byte(node);
char * r = (char *)malloc(sizeof(char) * (tblen + 1));
memcpy(r, code + ts_node_start_byte(node), tblen);
r[tblen] = '\0';
return r;
}
#define GET_TBTEXT tbtext(code, current_node)
int tbtraverse(const char * const code) {
// init
TSParser * parser;
TSTree * tree;
TSTreeCursor cursor;
TSNode current_node;
int tb_case;
parser = ts_parser_new();
ts_parser_set_language(parser, tree_sitter_c());
tree = ts_parser_parse_string(parser, NULL, code, strlen(code));
cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
current_node = ts_tree_root_node(tree);
// meat
while (true) {
current_node = ts_tree_cursor_current_node(&cursor);
tb_case = determine_case(tb_enter_cases, ts_node_type(current_node));
// XXX INJECTION
eval:
switch (tb_case) {
case 1: {
puts("ack");
char * mytbtext = GET_TBTEXT;
puts(mytbtext);
free(mytbtext);
} break;
case 2: {
puts("++");
} break;
case 3: {
puts("^^df");
} break;
default: { ; } break;
}
if (ts_tree_cursor_goto_first_child(&cursor)
|| ts_tree_cursor_goto_next_sibling(&cursor)) {
continue;
}
while (ts_tree_cursor_goto_parent(&cursor)) {
current_node = ts_tree_cursor_current_node(&cursor);
if (ts_tree_cursor_goto_next_sibling(&cursor)) {
tb_case = determine_case(tb_leave_cases, ts_node_type(current_node));
goto eval;
}
}
break;
}
// deinit
ts_tree_delete(tree);
ts_parser_delete(parser);
ts_tree_cursor_delete(&cursor);
return 0;
}
// @BAKE gcc $@ $(pkg-config --cflags --libs tree-sitter tree-sitter-c) -ggdb
signed main() {
tbtraverse("int main() { return 0; }");
}

@ -1,8 +1,33 @@
#include <stdio.h>
#include <string.h>
extern "C" {
#include <tree_sitter/api.h>
extern const TSLanguage * tree_sitter_c(void);
#include <tree_sitter/api.h>
extern const TSLanguage * tree_sitter_c(void);
typedef struct {
const char * const string;
const int case_number;
} tbcase_t;
const tbcase_t tb_enter_cases[] = {
(tbcase_t) { .string = "function_definition", .case_number = 1 },
(tbcase_t) { .string = "number_literal", .case_number = 2 },
(tbcase_t) { .string = NULL, .case_number = 0 },
};
const tbcase_t tb_leave_cases[] = {
(tbcase_t) { .string = "function_definition", .case_number = 3 },
(tbcase_t) { .string = NULL, .case_number = 0 },
};
// XXX better search algo
int determine_case(tbcase_t * ordered_array, const char * const string) {
tbcase_t * c;
for (; c->string != NULL; c++) {
if (!strcmp(c->string, string)) { break; }
}
return c->case_number;
}
int tbtraverse(const char * const code) {
@ -13,6 +38,8 @@ int tbtraverse(const char * const code) {
TSNode current_node;
TSNode previous_node;
int tb_case;
parser = ts_parser_new();
ts_parser_set_language(parser, tree_sitter_c());
@ -22,71 +49,51 @@ int tbtraverse(const char * const code) {
current_node = ts_tree_root_node(tree);
// meat
do {
while (true) {
current_node = ts_tree_cursor_current_node(&cursor);
const char * previous_node_type = NULL;
int tblen = ts_node_end_byte(current_node) - ts_node_start_byte(current_node);
char * tbtext = (char *)malloc(sizeof(char) * (tblen + 1));
memcpy(tbtext, code + ts_node_start_byte(current_node), tblen);
tbtext[tblen] = '\0';
tb_case = determine_case(tb_enter_cases, ts_node_type(current_node));
// XXX INJECTION
if (!strcmp("function_definition", ts_node_type(current_node))) {
puts("ack");
puts(tbtext);
goto end;
}
if (!strcmp("number_literal", ts_node_type(current_node))) {
puts("++");
goto end;
}
end:
free(tbtext);
} while ([&] {
bool r = false;
previous_node = current_node;
if (ts_tree_cursor_goto_first_child(&cursor)
|| ts_tree_cursor_goto_next_sibling(&cursor)) {
r = true;
goto eval;
}
while (ts_tree_cursor_goto_parent(&cursor)) {
if (!strcmp(ts_node_type(current_node), "translation_unit")) {
r = false;
break;
}
if (ts_tree_cursor_goto_next_sibling(&cursor)) {
r = true;
}
eval:
if (!strcmp("function_definition", ts_node_type(previous_node))) {
eval:
switch (tb_case) {
case 1: {
puts("ack");
puts(tbtext);
} break;
case 2: {
puts("++");
} break;
case 3: {
puts("^^df");
goto end;
}
end:
if (r) { break; }
} break;
[[likely]] default: { ; } break;
}
return r;
}());
free(tbtext);
if (ts_tree_cursor_goto_first_child(&cursor)
|| ts_tree_cursor_goto_next_sibling(&cursor)) {
current_node = ts_tree_cursor_current_node(&cursor);
tb_case = determine_case(tb_leave_cases, ts_node_type);
goto eval;
}
while (ts_tree_cursor_goto_parent(&cursor)) {
current_node = ts_tree_cursor_current_node(&cursor);
if (ts_tree_cursor_goto_next_sibling(&cursor)) {
tb_case = determine_case(tb_leave_cases, ts_node_type);
goto eval;
}
}
break;
}
// deinit
ts_tree_delete(tree);
@ -102,4 +109,3 @@ int tbtraverse(const char * const code) {
signed main() {
tbtraverse("int main() { return 0; }");
}

112
source/tbsp.c Normal file

@ -0,0 +1,112 @@
#define _GNU_SOURCE
#include <stdio.h>
#include "tbsp.yy.h"
#include "tbsp.tab.h"
// XXX i am so desperate for #embed, you would not believe
#include "TBSP_strings.inc"
extern int tbsp_yy_init(void);
extern int tbsp_yy_deinit(void);
char * language = NULL;
char * verbatim = NULL;
char * top = NULL;
void put_rule_table(const char * const name, rule_type_t type_mask) {
char * sprint_buffer;
int sprint_r;
(void)sprint_r;
fputs("const tbcase_t tb_", yyout);
fputs(name, yyout);
fputs("[] = {\n", yyout);
for (int i = 0; i < kv_size(rules); i++) {
if (!(kv_A(rules, i).type & type_mask)) { continue; }
sprint_r = asprintf(&sprint_buffer,
TBSP_case,
kv_A(rules, i).string,
kv_A(rules, i).target
);
fputs(sprint_buffer, yyout);
free(sprint_buffer);
}
fputs(" (tbcase_t) { .string = NULL, .case_number = 0 },\n", yyout);
fputs("};\n\n", yyout);
}
signed main(const int argc, const char * const * const argv) {
#ifdef DEBUG
yydebug = 1;
#endif
if (argc < 2) {
printf("%s <file>", argv[0]);
}
tbsp_yy_init();
tbsp_tab_init();
yyin = fopen(argv[1], "r");
if (!yyin) {
puts("Failed to open file");
return 1;
}
//yyout = fopen("tbsp.c", "w");
yyout = stdout;
int yyparse_r = yyparse();
if (yyparse_r) {
return 1;
}
char * sprint_buffer;
int sprint_r;
(void)sprint_r;
// Header
sprint_r = asprintf(&sprint_buffer, TBSP_header, language, language);
fputs(sprint_buffer, yyout);
free(sprint_buffer);
// Definition section
fputs(top, yyout);
// Rule section
put_rule_table("enter_cases", ENTER_RULE);
put_rule_table("leave_cases", LEAVE_RULE);
fputs(TBSP_traverse_top, yyout);
for (int i = 0; i < kv_size(rules); i++) {
const char * const case_string = "\
case %d: {\n\
%s\n\
} break;\n\
";
sprint_r = asprintf(&sprint_buffer,
case_string,
kv_A(rules, i).target,
kv_A(rules, i).code
);
fputs(sprint_buffer, yyout);
free(sprint_buffer);
}
fputs(TBSP_traverse_bottom, yyout);
// Code section
fputs(verbatim, yyout);
// Deinit
for (int i = 0; i < kv_size(rules); i++) {
free(kv_A(rules, i).string);
free(kv_A(rules, i).code);
}
tbsp_yy_deinit();
free(verbatim);
free(language);
free(top);
return 0;
}

94
source/tbsp.l Normal file

@ -0,0 +1,94 @@
%{
#include <sds.h>
#include "tbsp.tab.h"
int code_nesting = 0;
int code_caller;
sds buffer;
%}
identifier [a-zA-z][-a-zA-z0-9_]*
%x IN_DEFINITION_SECTION IN_RULE_SECTION IN_CODE_SECTION
%x IN_CODE
%option nodefault
%option noyywrap
%%
. { yyless(0); BEGIN IN_DEFINITION_SECTION; }
<IN_DEFINITION_SECTION>{
\%top[[:space:]]+\{ {
code_caller = IN_DEFINITION_SECTION;
BEGIN IN_CODE;
return TOP;
}
\%language[[:space:]] {
return LANGUAGE;
}
{identifier} {
yylval.strval = strdup(yytext);
return IDENTIFIER;
}
[[:space:]] { ; }
\%\% {
BEGIN IN_RULE_SECTION;
return SEPARATOR;
}
}
<IN_RULE_SECTION>{
\{ {
code_caller = IN_RULE_SECTION;
BEGIN IN_CODE;
}
\} { ; }
[[:space:]]* { ; }
enter[[:space:]] { return ENTER; }
leave[[:space:]] { return LEAVE; }
{identifier} {
yylval.strval = strdup(yytext);
return IDENTIFIER;
}
\%\% {
BEGIN IN_CODE_SECTION;
return SEPARATOR;
}
}
<IN_CODE>{
\{ { ++code_nesting; }
\} {
if (!code_nesting) {
yylval.strval = strdup(buffer);
sdsclear(buffer);
BEGIN code_caller;
return CODE_BLOB;
}
--code_nesting;
;
}
.|\n { buffer = sdscat(buffer, yytext); }
}
<IN_CODE_SECTION>{
(.|\n)* {
yylval.strval = strdup(yytext);
BEGIN IN_DEFINITION_SECTION;
return CODE_BLOB;
}
}
%%
int tbsp_yy_init(void) {
buffer = sdsnew("");
return 0;
}
int tbsp_yy_deinit(void) {
sdsfree(buffer);
return 0;
}

126
source/tbsp.y Normal file

@ -0,0 +1,126 @@
%{
#include "tbsp.yy.h"
void yyerror([[maybe_unused]] const char * const s);
extern char * language;
extern char * top;
extern char * verbatim;
int target_counter = 1;
#define COMA ,
%}
%code requires {
typedef enum {
ENTER_RULE = 0b0001,
LEAVE_RULE = 0b0010,
} rule_type_t;
typedef struct {
rule_type_t type;
int target;
char * string;
char * code;
} rule_t;
#include <kvec.h>
typedef kvec_t(rule_t) rule_vector_t;
extern rule_vector_t rules;
}
%code provides {
void tbsp_tab_init(void);
void tbsp_tab_deinit(void);
}
%union{
char * strval;
rule_type_t ruleval;
}
%token SEPARATOR
%token TOP LANGUAGE
%token ENTER LEAVE
%token<strval> IDENTIFIER CODE_BLOB
%type<ruleval> rule_type
%type<strval> rule_selector
%%
document
: %empty
| definition_section SEPARATOR rule_section SEPARATOR code_section
;
definition_section
: %empty
| top definition_section
| language definition_section
;
top
: TOP CODE_BLOB {
if (top) {
puts("error: reee");
return 1;
}
top = $2;
}
;
language
: LANGUAGE IDENTIFIER {
language = $2;
}
;
rule_section
: %empty
| rule rule_section
;
rule
: rule_type rule_selector CODE_BLOB {
kv_push(rule_t, rules, (rule_t) {
.type = $1 COMA
.target = target_counter COMA
.string = $2 COMA
.code = $3 COMA
});
++target_counter;
}
;
rule_type
: %empty { $$ = 0; }
| ENTER rule_type { $$ |= ENTER_RULE; }
| LEAVE rule_type { $$ |= LEAVE_RULE; }
;
rule_selector
: IDENTIFIER { $$ = $1; }
;
code_section
: CODE_BLOB {
verbatim = $1;
}
;
%%
rule_vector_t rules;
void yyerror(const char * const s) {
puts("yyerror");
}
void tbsp_tab_init(void) {
kv_init(rules);
}
void tbsp_tab_deinit(void) {
for (int i = 0; i < kv_size(rules); i++) {
free(kv_A(rules, i).string);
free(kv_A(rules, i).code);
}
kv_destroy(rules);
}