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 | 58f5b24c1c58f2fa5a2c06d758735150261483df | 
| parent | 73bcb13900667e4b56264b6d38eeaab42434aa2d | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Mon, 26 Feb 2024 22:42:53 +0000 | 
Proof of concept generator in Bison and Flex
Since all of the logic is duplicated I thought I should find a way to
automate the code generation. Here I present an experimental version of
the library generator, that's yet to be included in the building
process, where I experiment with Bison and Flex, by parsing a
configuration file and generating template and function code
automatically.
Documentations will be added after intervace and tool have been refined.
| M | CMakeLists.txt | | | ++++++ -- | 
| M | src/CMakeLists.txt | | | +++++++++++++++++++++++++ | 
| A | src/generator.h | | | ++++++++++++++++++++++++++++++++ | 
| A | src/lexer.l | | | +++++++++++++++++++++ | 
| A | src/parser.y | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | src/rules | | | +++++++++++++++++++++++++++++ | 
6 files changed, 316 insertions(+), 2 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -3,13 +3,17 @@ 
          set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
        
        
          project(
              Alec
          	VERSION 0.0.8
          	VERSION 0.0.9
              DESCRIPTION "Abstraction Layer for Escape Codes"
          	HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git
              LANGUAGES CXX
              LANGUAGES C CXX
          )
          enable_testing()
          set(CMAKE_C_STANDARD 11)
          set(CMAKE_C_STANDARD_REQUIRED ON)
          set(CMAKE_C_EXTENSIONS YES)
          set(CMAKE_CXX_STANDARD 23)
          set(CMAKE_CXX_STANDARD_REQUIRED ON)
          set(CMAKE_CXX_EXTENSIONS OFF)
        
        diff --git a/ src/CMakeLists.txt b/ src/CMakeLists.txt
@@ -1,3 +1,28 @@
set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}")
          find_package(FLEX)
          find_package(BISON)
          set(LEXER_OUT "${PARSER_DIR}/lexer.c")
          set(PARSER_OUT "${PARSER_DIR}/parser.c")
          if(CMAKE_BUILD_TYPE STREQUAL "Debug")
          	set(FLAGS "--debug")
          endif()
          FLEX_TARGET(LEXER lexer.l "${LEXER_OUT}" DEFINES_FILE "${PARSER_DIR}/scanner.h" COMPILE_FLAGS "${FLAGS}")
          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}")
          target_include_directories(generator PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
          set_target_properties(generator PROPERTIES
              VERSION ${PROJECT_VERSION}
              SOVERSION ${PROJECT_VERSION_MAJOR}
              RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
          )
          add_library(alec INTERFACE)
          target_include_directories(alec INTERFACE .)
          diff --git a/ src/generator.h b/ src/generator.h
@@ -0,0 +1,32 @@
#ifndef ALEC_PARSER_H
          #define ALEC_PARSER_H
          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          extern int yylineno;
          void yyerror(char *s, ...);
          struct list {
              char *data;
              struct list *next;
          };
          struct record {
              char *name;
              struct list *args;
              struct list *rules;
              struct list *recipe;
          };
          struct list *list_new(char *data, struct list *list);
          void list_free(struct list *l, void (*free_data)(void *));
          struct record *record_new(char *name, struct list *args, struct list *rules, struct list *recipe);
          void record_free(void *rp);
          void record_print_function(const struct record *r);
          void record_print_template(const struct record *r);
          #endif
        
        diff --git a/ src/lexer.l b/ src/lexer.l
@@ -0,0 +1,21 @@
%option noyywrap nodefault yylineno
          %{
              #include "generator.h"
              #include "parser.h"
              #include <ctype.h>
          %}
          LINE_END (\n|\r|\r\n)
          %%
          {LINE_END}       { return EOL; }
          ,                { return COMMA; }
          [^,\n]*          {
          	while(*yytext && isspace(*yytext)) yytext++;
          	yylval.n = strdup(yytext); return LITERAL;
          }
          %%
          diff --git a/ src/parser.y b/ src/parser.y
@@ -0,0 +1,203 @@
%{
              #include "generator.h"
              #include <stdarg.h>
          	int yylex(void);
          	int yyparse(void);
          	void yyrestart(FILE *);
          	int yylex_destroy(void);
          	struct list *records;
          %}
          %union {
              struct record *r;
              struct list *l;
              char *n;
          }
          %token <n> LITERAL
          %token EOL COMMA
          %type <r> record
          %type <l> list
          %type <n> name
          %start document
          %%
          document: record              { records = list_new((char *)$1, records); }
                  | record EOL document { records = list_new((char *)$1, records); }
          record: name list list list { $$ = record_new($1, $2, $3, $4); }
          name: LITERAL EOL   { $$ = $1; }
          list: EOL                { $$ = NULL; }
              | LITERAL EOL        { $$ = list_new($1, NULL); }
              | LITERAL COMMA list { $$ = list_new($1, $3); }
          %%
          #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);
              }
          	for(struct list *p = records; p; p = p->next) {
          		record_print_function((const struct record *)p->data);
          	}
          	for(struct list *p = records; p; p = p->next) {
          		record_print_template((const struct record *)p->data);
          	}
          	list_free(records, record_free);
          	yylex_destroy();
          }
          struct list *list_new(char *data, struct list *list) {
              struct list *l = malloc(sizeof(struct list));
              if(!l) {
                  yyerror("out of space");
                  exit(1);
              }
              *l = (struct list) {
                  .data = data,
                  .next = list,
              };
              return l;
          }
          void list_free(struct list *l, void (*free_data)(void *)) {
          	struct list *t;
          	while(l) {
          		t = l;
          		l = l->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){
          	struct record* rec = malloc(sizeof(struct record));
          	if(!rec) {
          		yyerror("out of space");
          		exit(1);
          	}
          	*rec = (struct record) {
          		.name = name,
          		.args = args,
          		.rules = rules,
          		.recipe = recipe,
          	};
          	return rec;
          }
          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);
          	free(r->name);
          	free(r);
          }
          void record_print_function(const struct record *r) {
          	struct list dummy, *c = &dummy;
          	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(", ");
          	}
          	printf(") {\n");
          	if(r->rules) {
          		for(struct list *p = dummy.next; p; p = p->next) {
          			printf("\t assert(");
          			for(struct list *q = r->rules; q; q = q->next) {
          				printf("%s(%s)", q->data, p->data);
          				if(q->next) printf(" && ");
          			}
          			printf(");\n");
          		}
          	}
          	printf("\treturn details::helper::make(");
          	for(struct list *p = r->recipe; p; p = p->next) {
          		printf("%s", p->data);
          		if(p->next) printf(", ");
          	}
          	printf(");\n}\n\n");
          	list_free(dummy.next, NULL);
          }
          void record_print_template(const struct record *r) {
          	struct list dummy, *c = &dummy;
          	printf("template <");
          	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(", ");
          	}
          	printf(">\n");
          	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) {
          				printf("%s_v(%s)", q->data, p->data);
          				if(q->next) printf(" && ");
          			}
          			if(p->next) printf(" && ");
          		}
          		printf("\n");
          	}
          	printf("static constexpr auto %s_v\n\t = details::escape<", r->name);
          	for(struct list *p = r->recipe; p; p = p->next) {
          		printf("%s", p->data);
          		if(p->next) printf(", ");
          	}
          	printf(">;\n\n");
          	list_free(dummy.next, NULL);
          }
          void yyerror(char *s, ...) {
            va_list ap;
            va_start(ap, s);
            fprintf(stderr, "%d: error: ", yylineno);
            vfprintf(stderr, s, ap);
            fprintf(stderr, "\n");
          }
          diff --git a/ src/rules b/ src/rules
@@ -0,0 +1,29 @@
foreground
          int idx
          limit_256
          32, ';', 5, ';', idx, 'm'
          background
          int idx
          limit_256
          42, ';', 5, ';', idx, 'm'
          foreground
          int R, int G, int B
          limit_256
          38, ';', 5, ';', R, ';', G, ';', B, 'm'
          background
          int R, int G, int B
          limit_256
          38, ';', 5, ';', R, ';', G, ';', B, 'm'
          foreground
          COLOR color
          (int)color + 30, 'm'
          background
          COLOR color
          (int)color + 40, 'm'