alecAbstraction Layer for Escape Codes |
git clone git://git.dimitrijedobrota.com/alec.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | |
commit | 386477550b0ec2d523464ba780be4693f1250a83 |
parent | 424633da3f889e83f581ae158cc4841d29d9bc4c |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Fri, 1 Mar 2024 23:45:25 +0000 |
Extract generator logic to generator.c * Modernize bison code segments
Diffstat:M | src/CMakeLists.txt | | | +- |
A | src/generator.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/parser.y | | | ++++------------------------------------------------------------------------------ |
3 files changed, 277 insertions(+), 269 deletions(-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -14,7 +14,7 @@ FLEX_TARGET(LEXER lexer.l "${LEXER_OUT}" DEFINES_FILE "${PARSER_DIR}/scanner.h"
BISON_TARGET(PARSER parser.y "${PARSER_OUT}" DEFINES_FILE "${PARSER_DIR}/parser.h" COMPILE_FLAGS "${FLAGS}")
ADD_FLEX_BISON_DEPENDENCY(LEXER PARSER)
add_executable(generator "${LEXER_OUT}" "${PARSER_OUT}")
add_executable(generator "${LEXER_OUT}" "${PARSER_OUT}" generator.c)
target_include_directories(generator PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
set(GENERATE_OUT "${CMAKE_BINARY_DIR}/bin")
diff --git a/src/generator.c b/src/generator.c
@@ -0,0 +1,265 @@
#include "generator.h"
#include "parser.h"
#define MALLOC(x) \
do { \
x = malloc(sizeof(*x)); \
if (!x) yyerror("out of space"), exit(1); \
} while (0);
#ifdef YYDEBUG
int yydebug = 1;
#endif
list_t records = { 0 };
list_t after = { 0 };
list_t before = { 0 };
int main(const int argc, char *argv[]) {
if (argc < 2) {
yyparse();
return 0;
}
for (int i = 1; i < argc; i++) {
FILE *f = fopen(argv[i], "r");
if (!f) {
perror(argv[1]);
return -1;
}
yyrestart(f);
yyparse();
fclose(f);
}
// print before section
for (node_t *p = before.head; p; p = p->next) {
printf("%s", p->data);
}
list_t dupes = {0};
record_dupes(&dupes, &records);
printf("\n/* Template compile-time variables */\n\n");
record_print_dupes(&dupes);
for (node_t *p = records.head; p; p = p->next) {
const struct record *r = (const struct record *)p->data;
record_print_template(r, list_find(&dupes, r->name, scmp));
}
printf("\n/* Run-time functions */\n\n");
for (node_t *p = records.head; p; p = p->next) {
const struct record *r = (const struct record *)p->data;
record_print_function(r);
}
// print after section
for (node_t *p = after.head; p; p = p->next) {
printf("%s", p->data);
}
list_free(&dupes, 0);
list_free(&before, free);
list_free(&records, record_free);
list_free(&after, free);
yylex_destroy();
}
node_t *node_new(char *data) {
node_t *n;
MALLOC(n);
*n = (node_t){
.data = data,
.next = NULL,
};
return n;
}
list_t *list_new(char *data) {
list_t *l;
MALLOC(l);
*l = (list_t){0};
if (data) list_append(l, node_new(data));
return l;
}
void list_free(list_t *l, void (*free_data)(void *)) {
if (!l) return;
node_t *c = l->head, *t;
while (c) {
t = c;
c = c->next;
if (free_data) free_data((void *)t->data);
free(t);
}
}
void list_append(list_t *l, node_t *n) {
if (!l->head) l->head = l->tail = n;
else
l->tail = l->tail->next = n;
}
struct record *record_new(char *name, list_t *args, list_t *rules, list_t *recipe) {
struct record *rec;
MALLOC(rec);
*rec = (struct record){
.name = name,
.args = args,
.rules = rules,
.recipe = recipe,
};
return rec;
}
void record_free(void *rp) {
struct record *r = (struct record *)rp;
if (r->args) list_free(r->args, free), free(r->args);
if (r->rules) list_free(r->rules, free), free(r->rules);
if (r->recipe) list_free(r->recipe, free), free(r->recipe);
free(r->name);
free(r);
}
int list_find(list_t *l, void *data, cmp_f cmp) {
node_t *c = l->head;
while (c) {
if (!cmp(c->data, data)) return 1;
c = c->next;
}
return 0;
}
void record_print_dupes(const list_t *l) {
node_t *c = l->head;
while (c) {
printf("template <auto... val> static const char *%s_v;\n", c->data);
c = c->next;
}
printf("\n");
}
void record_print_function(const struct record *r) {
list_t dummy = {0};
if (!r->recipe) { // comment
printf("%s\n\n", r->name);
return;
}
printf("static constexpr auto %s(", r->name);
if (r->args) {
for (node_t *p = r->args->head; p; p = p->next) {
list_append(&dummy, node_new(strchr(p->data, ' ') + 1));
printf("%s", p->data);
if (p->next) printf(", ");
}
}
printf(") {\n");
if (r->rules) {
for (node_t *p = dummy.head; p; p = p->next) {
printf("\tassert(");
for (node_t *q = r->rules->head; q; q = q->next) {
printf("%s(%s)", q->data, p->data);
if (q->next) printf(" && ");
}
printf(");\n");
}
}
if (r->args) {
printf("\treturn details::helper::make(");
for (node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if (p->next) printf(", ");
}
printf(")");
} else {
printf("\treturn %s_v", r->name);
}
printf(";\n}\n\n");
list_free(&dummy, NULL);
}
void record_print_template(const struct record *r, int dup) {
list_t dummy = {0};
if (!r->recipe) { // comment
printf("%s\n\n", r->name);
return;
}
if (r->args) {
printf("template <");
for (node_t *p = r->args->head; p; p = p->next) {
list_append(&dummy, node_new(strchr(p->data, ' ') + 1));
printf("%s", p->data);
if (p->next) printf(", ");
}
printf(">\n");
}
if (r->rules) {
printf("\trequires ");
for (node_t *p = dummy.head; p; p = p->next) {
for (node_t *q = r->rules->head; q; q = q->next) {
printf("%s_v<%s>", q->data, p->data);
if (q->next) printf(" && ");
}
if (p->next) printf(" && ");
}
printf("\n");
}
printf("static constexpr auto %s_v", r->name);
if (dup) {
printf("<");
for (node_t *p = dummy.head; p; p = p->next) {
printf("%s", p->data);
if (p->next) printf(", ");
}
printf(">");
}
if (r->recipe->head && r->recipe->head->data[0] == '"') {
printf("\n\t = details::escape_literal<%s>;", r->recipe->head->data);
} else {
printf("\n\t = details::escape<");
for (node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if (p->next) printf(", ");
}
printf(">;");
}
printf("\n\n");
list_free(&dummy, NULL);
}
int scmp(const void *a, const void *b) { return strcmp((const char *)a, (const char *)b); }
void record_dupes(list_t *d, list_t *l) {
list_t s = {0};
for (node_t *p = records.head; p; p = p->next) {
const struct record *r = (const struct record *)p->data;
if (!list_find(&s, r->name, scmp)) list_append(&s, node_new(r->name));
else if (!list_find(d, r->name, scmp))
list_append(d, node_new(r->name));
}
list_free(&s, NULL);
}
diff --git a/src/parser.y b/src/parser.y
@@ -1,26 +1,23 @@
%{
%code requires {
#include "generator.h"
#include <stdarg.h>
}
#define MALLOC(x) do { \
x = malloc(sizeof(*x)); \
if(!x) yyerror("out of space"), exit(1); \
} while(0);
%union {
struct record *r;
list_t *l;
char *n;
}
%code provides {
int yylex(void);
int yyparse(void);
void yyrestart(FILE *);
int yylex_destroy(void);
list_t records = { 0 };
list_t after = { 0 };
list_t before = { 0 };
%}
%union {
struct record *r;
list_t *l;
char *n;
extern list_t records;
extern list_t after;
extern list_t before;
}
%token <n> LITERAL COMMENT BEFORE AFTER
@@ -66,260 +63,6 @@ items: LITERAL { $$ = list_new($1); }
%%
#ifdef YYDEBUG
int yydebug = 1;
#endif
int main(const int argc, char *argv[]) {
if(argc < 2) {
yyparse();
return 0;
}
for(int i = 1; i < argc; i++) {
FILE *f = fopen(argv[i], "r");
if(!f) {
perror(argv[1]);
return -1;
}
yyrestart(f);
yyparse();
fclose(f);
}
// print before section
for(node_t *p = before.head; p; p = p->next) {
printf("%s", p->data);
}
list_t dupes = { 0 };
record_dupes(&dupes, &records);
printf("\n/* Template compile-time variables */\n\n");
record_print_dupes(&dupes);
for(node_t *p = records.head; p; p = p->next) {
const struct record * r = (const struct record *)p->data;
record_print_template(r, list_find(&dupes, r->name, scmp));
}
printf("\n/* Run-time functions */\n\n");
for(node_t *p = records.head; p; p = p->next) {
const struct record * r = (const struct record *)p->data;
record_print_function(r);
}
// print after section
for(node_t *p = after.head; p; p = p->next) {
printf("%s", p->data);
}
list_free(&dupes, 0);
list_free(&before, free);
list_free(&records, record_free);
list_free(&after, free);
yylex_destroy();
}
node_t *node_new(char *data) {
node_t *n;
MALLOC(n);
*n = (node_t) {
.data = data,
.next = NULL,
};
return n;
}
list_t *list_new(char *data) {
list_t *l;
MALLOC(l);
*l = (list_t) { 0 };
if(data) list_append(l, node_new(data));
return l;
}
void list_free(list_t *l, void (*free_data)(void *)) {
if(!l) return;
node_t *c = l->head, *t;
while(c) {
t = c;
c = c->next;
if(free_data) free_data((void *)t->data);
free(t);
}
}
void list_append(list_t *l, node_t *n) {
if(!l->head) l->head = l->tail = n;
else l->tail = l->tail->next = n;
}
struct record *record_new(char *name, list_t *args, list_t *rules, list_t *recipe){
struct record* rec;
MALLOC(rec);
*rec = (struct record) {
.name = name,
.args = args,
.rules = rules,
.recipe = recipe,
};
return rec;
}
void record_free(void *rp) {
struct record *r = (struct record *)rp;
if(r->args) list_free(r->args, free), free(r->args);
if(r->rules) list_free(r->rules, free), free(r->rules);
if(r->recipe) list_free(r->recipe, free), free(r->recipe);
free(r->name);
free(r);
}
int list_find(list_t *l, void *data, cmp_f cmp) {
node_t *c = l->head;
while(c) {
if(!cmp(c->data, data)) return 1;
c = c->next;
}
return 0;
}
void record_print_dupes(const list_t *l) {
node_t *c = l->head;
while(c) {
printf("template <auto... val> static const char *%s_v;\n", c->data);
c = c->next;
}
printf("\n");
}
void record_print_function(const struct record *r) {
list_t dummy = { 0 };
if(!r->recipe) { // comment
printf("%s\n\n", r->name);
return;
}
printf("static constexpr auto %s(", r->name);
if(r->args) {
for(node_t *p = r->args->head; p; p = p->next) {
list_append(&dummy, node_new(strchr(p->data, ' ') + 1));
printf("%s", p->data);
if(p->next) printf(", ");
}
}
printf(") {\n");
if(r->rules) {
for(node_t *p = dummy.head; p; p = p->next) {
printf("\tassert(");
for(node_t *q = r->rules->head; q; q = q->next) {
printf("%s(%s)", q->data, p->data);
if(q->next) printf(" && ");
}
printf(");\n");
}
}
if(r->args) {
printf("\treturn details::helper::make(");
for(node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}
printf(")");
} else {
printf("\treturn %s_v", r->name);
}
printf(";\n}\n\n");
list_free(&dummy, NULL);
}
void record_print_template(const struct record *r, int dup) {
list_t dummy = { 0 };
if(!r->recipe) { // comment
printf("%s\n\n", r->name);
return;
}
if(r->args) {
printf("template <");
for(node_t *p = r->args->head; p; p = p->next) {
list_append(&dummy, node_new(strchr(p->data, ' ') + 1));
printf("%s", p->data);
if(p->next) printf(", ");
}
printf(">\n");
}
if(r->rules) {
printf("\trequires ");
for(node_t *p = dummy.head; p; p = p->next) {
for(node_t *q = r->rules->head; q; q = q->next) {
printf("%s_v<%s>", q->data, p->data);
if(q->next) printf(" && ");
}
if(p->next) printf(" && ");
}
printf("\n");
}
printf("static constexpr auto %s_v", r->name);
if(dup) {
printf("<");
for(node_t *p = dummy.head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}
printf(">");
}
if(r->recipe->head && r->recipe->head->data[0] == '"') {
printf("\n\t = details::escape_literal<%s>;", r->recipe->head->data);
} else {
printf("\n\t = details::escape<");
for(node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}
printf(">;");
}
printf("\n\n");
list_free(&dummy, NULL);
}
int scmp(const void *a, const void *b) {
return strcmp((const char *)a, (const char *)b);
}
void record_dupes(list_t *d, list_t *l) {
list_t s = { 0 };
for(node_t *p = records.head; p; p = p->next) {
const struct record * r = (const struct record *)p->data;
if(!list_find(&s, r->name, scmp)) list_append(&s, node_new(r->name));
else if(!list_find(d, r->name, scmp)) list_append(d, node_new(r->name));
}
list_free(&s, NULL);
}
void yyerror(char *s, ...) {
va_list ap;
va_start(ap, s);