proper implementation in C
This commit is contained in:
parent
475d2976d9
commit
b2912528bd
27
Makefile
27
Makefile
@ -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
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
1603
library/sds.c
Normal file
File diff suppressed because it is too large
Load Diff
155
library/sds.h
Normal file
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
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
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
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; }");
|
||||
}
|
||||
|
122
source/tbc.cpp
122
source/tbc.cpp
@ -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
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
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
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user