alec

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

commit 9aecfcedea481a45b5bef86c2c41d64733d9bcd5
parent 0cdc598bd65b3fcccb48a9537586eefa9dd577f2
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri,  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 | 2+-
MREADME.md | 11+++++------
Msrc/generator.h | 37++++++++++++++++++++++++-------------
Msrc/lexer.l | 2+-
Msrc/parser.y | 186+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
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; }