alec

Abstraction Layer for Escape Codes
git clone git://git.dimitrijedobrota.com/alec.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commit9aecfcedea481a45b5bef86c2c41d64733d9bcd5
parent0cdc598bd65b3fcccb48a9537586eefa9dd577f2
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateFri, 1 Mar 2024 22:08:08 +0000

Switch to left recursion for improved performance * Improved list handling * Typedef for struct node and list * '|' must be used for empty rule lines * Update README to reflect changes

Diffstat:
MCMakeLists.txt|+-
MREADME.md|+++++------
Msrc/generator.h|++++++++++++++++++++++++-------------
Msrc/lexer.l|+-
Msrc/parser.y|+++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------

5 files changed, 137 insertions(+), 101 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
Alec
VERSION 1.0.1
VERSION 1.0.2
DESCRIPTION "Abstraction Layer for Escape Codes"
HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git
LANGUAGES C CXX

diff --git a/README.md b/README.md

@@ -140,6 +140,10 @@ Configuration file `alec.rules.hpp` is used to customize the output of

output file whilst the second section contains rules for generating template
and function code.
* Rules can be separated by an arbitrary number of blank lines.
* Everything can have arbitrary indentation that is not carried to the resulting file.
* There could be C-style comments that are copied to the resulting file.
Each rule consists of 4 lines:
1. Name: name of the generated function, must be valid C++ name
2. Parameters: list of `type name` pairs separated by a comma

@@ -147,12 +151,7 @@ Each rule consists of 4 lines:

4. Rules: list of chars and ints (or expressions producing them) separated by a
comma, or a single string literal
> It is recommended to use single `|` character in place of an empty list rule
for better readability
* Rules can be separated by an arbitrary number of blank lines.
* Everything can have arbitrary indentation that is not carried to the resulting file.
* There can also be C-style comments that are copied to the resulting file.
> You *must* use `|` character in place of empty list rule
### Constraints

diff --git a/src/generator.h b/src/generator.h

@@ -8,35 +8,46 @@

extern int yylineno;
void yyerror(char *s, ...);
struct list {
typedef struct node node_t;
typedef struct list list_t;
struct node {
char *data;
struct list *next;
node_t *next;
};
struct list {
node_t *head;
node_t *tail;
};
struct record {
char *name;
struct list *args;
struct list *rules;
struct list *recipe;
list_t *args;
list_t *rules;
list_t *recipe;
};
typedef void(*free_f)(void *);
typedef int(*cmp_f)(const void *, const void *);
typedef void (*free_f)(void *);
typedef int (*cmp_f)(const void *, const void *);
int scmp(const void *a, const void *b);
struct list *list_new(char *data, struct list *list);
void list_free(struct list *l, free_f free_data);
node_t *node_new(char *data);
list_t *list_new(char *data);
void list_free(list_t *l, free_f free_data);
void list_append(list_t *l, node_t *n);
int list_find(struct list *l, void *data, cmp_f cmp);
int list_find(list_t *l, void *data, cmp_f cmp);
struct record *record_new(char *name, struct list *args, struct list *rules, struct list *recipe);
struct record *record_new(char *name, list_t *args, list_t *rules, list_t *recipe);
void record_free(void *rp);
struct list *record_dupes(struct list *l);
list_t *record_dupes(list_t *l);
void record_print_template(const struct record *r, int dup);
void record_print_function(const struct record *r);
void record_print_dupes(const struct list *l);
void record_print_dupes(const list_t *l);
#endif

diff --git a/src/lexer.l b/src/lexer.l

@@ -19,7 +19,7 @@ LINE_END (\n|\r|\r\n)

<GEN>{LINE_END} { return EOL; }
<GEN>^[\t ]*{LINE_END} { return EOL; }
<GEN>^[\t ]*\|*[\t ]*{LINE_END} { return EOL; }
<GEN>^[\t ]*\|*[\t ]*{LINE_END} { return EMPTY; }
<GEN>^[\t ]*"//".* { yylval.n = strdup(yytext); return COMMENT; }

diff --git a/src/parser.y b/src/parser.y

@@ -7,57 +7,58 @@

void yyrestart(FILE *);
int yylex_destroy(void);
struct list *records = NULL;
struct list *before = NULL;
struct list *after = NULL;
list_t records = { 0 };
list_t after = { 0 };
list_t before = { 0 };
%}
%union {
struct record *r;
struct list *l;
list_t *l;
char *n;
}
%token <n> LITERAL COMMENT BEFORE AFTER
%token EOL COMMA SWITCH
%token EOL COMMA SWITCH EMPTY
%type <r> record
%type <l> list
%type <l> list items
%type <n> name
%start document
%%
document: before SWITCH mid SWITCH after
document: before mid after
before: %empty
| BEFORE before { before = list_new($1, before); }
after: %empty
| AFTER after { after = list_new($1, after); }
mid: %empty
| EOL mid
| record mid { records = list_new((char *)$1, records); }
| COMMENT mid {
records = list_new((char *)record_new(
$1, NULL, NULL, NULL
), records);
}
| before BEFORE { list_append(&before, node_new($2)); }
;
after: SWITCH
| after AFTER { list_append(&after, node_new($2)); }
;
mid: SWITCH
| mid EOL
| mid record { list_append(&records, node_new((char *)$2)); }
;
record: name list list list { $$ = record_new($1, $2, $3, $4); }
| COMMENT { $$ = record_new($1, NULL, NULL, NULL); }
;
name: LITERAL EOL { $$ = $1; }
;
list: EOL { $$ = NULL; }
| LITERAL EOL { $$ = list_new($1, NULL); }
| LITERAL COMMA list { $$ = list_new($1, $3); }
list: EMPTY { $$ = NULL; }
| items EOL { $$ = $1; }
;
items: LITERAL { $$ = list_new($1); }
| items COMMA LITERAL { list_append($1, node_new($3)); $$ = $1; }
;
%%
#ifdef YYDEBUG

@@ -85,66 +86,87 @@ int main(const int argc, char *argv[]) {

// print before section
for(struct list *p = before; p; p = p->next) {
for(node_t *p = before.head; p; p = p->next) {
printf("%s", p->data);
}
struct list *dupes = record_dupes(records);
list_t *dupes = record_dupes(&records);
printf("\n/* Template compile-time variables */\n\n");
record_print_dupes(dupes);
for(struct list *p = records; p; p = p->next) {
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(struct list *p = records; p; p = p->next) {
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(struct list *p = after; p; p = p->next) {
for(node_t *p = after.head; p; p = p->next) {
printf("%s", p->data);
}
list_free(dupes, 0);
free(dupes);
list_free(before, free);
list_free(records, record_free);
list_free(after, free);
list_free(&before, free);
list_free(&records, record_free);
list_free(&after, free);
yylex_destroy();
}
struct list *list_new(char *data, struct list *list) {
struct list *l = malloc(sizeof(struct list));
node_t *node_new(char *data) {
node_t *n = malloc(sizeof(node_t));
if(!l) {
if(!n) {
yyerror("out of space");
exit(1);
}
*l = (struct list) {
*n = (node_t) {
.data = data,
.next = list,
.next = NULL,
};
return n;
}
list_t *list_new(char *data) {
list_t *l = calloc(1, sizeof(list_t));
if(!l) {
yyerror("out of space");
exit(1);
}
if(data) list_append(l, node_new(data));
return l;
}
void list_free(struct list *l, void (*free_data)(void *)) {
struct list *t;
while(l) {
t = l;
l = l->next;
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);
}
}
struct record *record_new(char *name, struct list *args, struct list *rules, struct list *recipe){
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(sizeof(struct record));
if(!rec) {

@@ -164,31 +186,33 @@ struct record *record_new(char *name, struct list *args, struct list *rules, str

void record_free(void *rp) {
struct record *r = (struct record *)rp;
list_free(r->args, free);
list_free(r->rules, free);
list_free(r->recipe, free);
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(struct list *l, void *data, cmp_f cmp) {
while(l) {
if(!cmp(l->data, data)) return 1;
l = l->next;
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 struct list *l) {
while(l) {
printf("template <auto... val> static const char *%s_v;\n", l->data);
l = l->next;
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) {
struct list dummy = { 0 }, *c = &dummy;
list_t dummy = { 0 };
if(!r->recipe) { // comment
printf("%s\n\n", r->name);

@@ -196,17 +220,19 @@ void record_print_function(const struct record *r) {

}
printf("static constexpr auto %s(", r->name);
for(struct list *p = r->args; p; p = p->next) {
c = c->next = list_new(strchr(p->data, ' ') + 1, NULL);
printf("%s", p->data);
if(p->next) printf(", ");
}
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(struct list *p = dummy.next; p; p = p->next) {
for(node_t *p = dummy.head; p; p = p->next) {
printf("\tassert(");
for(struct list *q = r->rules; q; q = q->next) {
for(node_t *q = r->rules->head; q; q = q->next) {
printf("%s(%s)", q->data, p->data);
if(q->next) printf(" && ");
}

@@ -216,7 +242,7 @@ void record_print_function(const struct record *r) {

if(r->args) {
printf("\treturn details::helper::make(");
for(struct list *p = r->recipe; p; p = p->next) {
for(node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}

@@ -226,11 +252,11 @@ void record_print_function(const struct record *r) {

}
printf(";\n}\n\n");
list_free(dummy.next, NULL);
list_free(&dummy, NULL);
}
void record_print_template(const struct record *r, int dup) {
struct list dummy = { 0 }, *c = &dummy;
list_t dummy = { 0 };
if(!r->recipe) { // comment
printf("%s\n\n", r->name);

@@ -239,8 +265,8 @@ void record_print_template(const struct record *r, int dup) {

if(r->args) {
printf("template <");
for(struct list *p = r->args; p; p = p->next) {
c = c->next = list_new(strchr(p->data, ' ') + 1, NULL);
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(", ");
}

@@ -249,8 +275,8 @@ void record_print_template(const struct record *r, int dup) {

if(r->rules) {
printf("\trequires ");
for(struct list *p = dummy.next; p; p = p->next) {
for(struct list *q = r->rules; q; q = q->next) {
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(" && ");
}

@@ -262,19 +288,18 @@ void record_print_template(const struct record *r, int dup) {

printf("static constexpr auto %s_v", r->name);
if(dup) {
printf("<");
for(struct list *p = dummy.next; p; p = p->next) {
for(node_t *p = dummy.head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}
printf(">");
}
if(!r->recipe->next && r->recipe->data[0] == '"') {
// string literal
printf("\n\t = details::escape_literal<%s>;", r->recipe->data);
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(struct list *p = r->recipe; p; p = p->next) {
for(node_t *p = r->recipe->head; p; p = p->next) {
printf("%s", p->data);
if(p->next) printf(", ");
}

@@ -282,23 +307,24 @@ void record_print_template(const struct record *r, int dup) {

}
printf("\n\n");
list_free(dummy.next, NULL);
list_free(&dummy, NULL);
}
int scmp(const void *a, const void *b) {
return strcmp((const char *)a, (const char *)b);
}
struct list *record_dupes(struct list *l) {
struct list *s = NULL, *d = NULL;
list_t *record_dupes(list_t *l) {
list_t *d = list_new(NULL);
list_t s = { 0 };
for(struct list *p = records; p; p = p->next) {
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)) s = list_new(r->name, s);
else if(!list_find(d, r->name, scmp)) d = list_new(r->name, d);
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);
list_free(&s, NULL);
return d;
}