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 | 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
| M | CMakeLists.txt | | | + - | 
| M | README.md | | | +++++ ------ | 
| M | src/generator.h | | | ++++++++++++++++++++++++ ------------- | 
| M | src/lexer.l | | | + - | 
| M | src/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;
          }