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:
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;
}