alec

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

commit 9dc03661ab1baccce726bd37b704d7ae34a4caa5
parent 9dbfc0ddfc5fa3f502132b72ae52a48d4af8ee97
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Tue, 27 Feb 2024 20:58:40 +0000

Working generator and full configuration

* Detect duplicate names and forward declare general template
* Make explicit specialization for general templates
* Detect string literal and call appropriate escape function
* Functions without parameters return templated value
* Full working configuration with comments
* | can be used to detonate a empty line for better readability
  when there are no parameters or constraints
* Memory leak free
* Minor consistency fixups

Diffstat:
MCMakeLists.txt | 2+-
Msrc/generator.h | 14++++++++++++--
Msrc/lexer.l | 9+++++----
Msrc/parser.y | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/rules | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
5 files changed, 279 insertions(+), 64 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Alec - VERSION 0.0.9 + VERSION 0.0.10 DESCRIPTION "Abstraction Layer for Escape Codes" HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git LANGUAGES C CXX diff --git a/src/generator.h b/src/generator.h @@ -20,13 +20,23 @@ struct record { struct list *recipe; }; +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, void (*free_data)(void *)); +void list_free(struct list *l, free_f free_data); + +int list_find(struct list *l, void *data, cmp_f cmp); struct record *record_new(char *name, struct list *args, struct list *rules, struct list *recipe); void record_free(void *rp); +struct list *record_dupes(struct list *l); + +void record_print_template(const struct record *r, int dup); void record_print_function(const struct record *r); -void record_print_template(const struct record *r); +void record_print_dupes(const struct list *l); #endif diff --git a/src/lexer.l b/src/lexer.l @@ -10,13 +10,14 @@ LINE_END (\n|\r|\r\n) %% -{LINE_END} { return EOL; } -^[\t ]*{LINE_END} { return EOL; } -^[\t ]* +{LINE_END} { return EOL; } +^[\t ]*{LINE_END} { return EOL; } +^[\t ]*\|*[\t ]*{LINE_END} { return EOL; } ^[\t ]*"//".* { yylval.n = strdup(yytext); return COMMENT; } -, { return COMMA; } +, { return COMMA; } + [^,\n]* { char *p = yytext + strlen(yytext) - 1; while(isspace(*p)) *p-- = '\0'; diff --git a/src/parser.y b/src/parser.y @@ -73,15 +73,24 @@ int main(const int argc, char *argv[]) { fclose(f); } + struct list *dupes = record_dupes(records); + record_print_dupes(dupes); + + printf("\n/* Template compile-time variables */\n"); for(struct list *p = records; p; p = p->next) { - record_print_function((const struct record *)p->data); + 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"); for(struct list *p = records; p; p = p->next) { - record_print_template((const struct record *)p->data); + const struct record * r = (const struct record *)p->data; + record_print_function(r); } list_free(records, record_free); + list_free(dupes, 0); + yylex_destroy(); } @@ -138,13 +147,29 @@ void record_free(void *rp) { 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; + } + 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_function(const struct record *r) { + struct list dummy = { 0 }, *c = &dummy; + if(!r->recipe) { // comment - printf("%s\n", r->name); + printf("%s\n\n", r->name); return; } - 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); @@ -155,7 +180,7 @@ void record_print_function(const struct record *r) { if(r->rules) { for(struct list *p = dummy.next; p; p = p->next) { - printf("\t assert("); + printf("\tassert("); for(struct list *q = r->rules; q; q = q->next) { printf("%s(%s)", q->data, p->data); if(q->next) printf(" && "); @@ -164,37 +189,44 @@ void record_print_function(const struct record *r) { } } - 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"); + if(r->args) { + printf("\treturn details::helper::make("); + for(struct list *p = r->recipe; p; p = p->next) { + printf("%s", p->data); + if(p->next) printf(", "); + } + printf(")"); + } else { + printf("\treturn %s_v", r->name); + } + printf(";\n}\n\n"); list_free(dummy.next, NULL); } -void record_print_template(const struct record *r) { +void record_print_template(const struct record *r, int dup) { + struct list dummy = { 0 }, *c = &dummy; + if(!r->recipe) { // comment - printf("%s\n", r->name); + printf("%s\n\n", r->name); return; } - 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->args) { + 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); + printf("%s_v<%s>", q->data, p->data); if(q->next) printf(" && "); } if(p->next) printf(" && "); @@ -202,16 +234,49 @@ void record_print_template(const struct record *r) { 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"); + printf("static constexpr auto %s_v", r->name); + if(dup) { + printf("<"); + for(struct list *p = dummy.next; 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); + } else { + printf("\n\t = details::escape<"); + for(struct list *p = r->recipe; p; p = p->next) { + printf("%s", p->data); + if(p->next) printf(", "); + } + printf(">;"); + } + printf("\n\n"); list_free(dummy.next, 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; + + for(struct list *p = records; 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); + } + + list_free(s, NULL); + return d; +} + void yyerror(char *s, ...) { va_list ap; va_start(ap, s); diff --git a/src/rules b/src/rules @@ -1,44 +1,183 @@ - -// hello +// Move cursor up/down/frwd/back -foreground -int idx -limit_256 -32, ';', 5, ';', idx, 'm' - + cursor_up + int n + limit_pos + n, 'A' + + cursor_down + int n + limit_pos + n, 'B' + + cursor_frwd + int n + limit_pos + n, 'C' + + cursor_back + int n + limit_pos + n, 'D' + +// Move cursor to the next/prev line + + cursor_line_next + int n + limit_pos + n, 'E' + + cursor_line_prev + int n + limit_pos + n, 'F' + +// Set cursor to specific column + + cursor_column + int n + limit_pos + n, 'G' + +// Erase functions + + erase_display + MOTION m + | + (int)m, 'J' + + erase_line + MOTION m + | + (int)m, 'K' + +// Scroll up/down + + scroll_up + int n + limit_pos + n, 'S' + + scroll_down + int n + limit_pos + n, 'T' + +// Set cursor to a specific position + + cursor_position + int n, int m + limit_pos + n, ';', m, 'H' + +// color + +// palet colors + + foreground + COLOR color + | + (int)color + 30, 'm' background + COLOR color + | + (int)color + 40, 'm' + +// 256-color palette + + foreground int idx limit_256 - 42, ';', 5, ';', idx, 'm' - + 38, ';', 5, ';', idx, 'm' + + background + int idx + limit_256 + 48, ';', 5, ';', idx, 'm' + +// RGB colors + foreground int R, int G, int B limit_256 38, ';', 5, ';', R, ';', G, ';', B, 'm' - - // world - // world + background + int R, int G, int B + limit_256 + 48, ';', 5, ';', R, ';', G, ';', B, 'm' + +// Set/reset text decorators + + decor_set + DECOR decor + | + (int)decor, 'm' + + decor_reset + DECOR decor + | + (int)decor + 20, 'm' + +// Save/load cursor position; + + cursor_save + | + | + 's' + + cursor_load + | + | + 'u' + +// Set screen modes + + screen_mode_set + int n + limit_pos + '=', n, 'h' + + screen_mode_reset + int n + limit_pos + '=', n, 'l' + +// Private screen modes supported by most terminals + +// Save/load screen + + screen_show + | + | + "?47h" + + screen_hide + | + | + "?47l" -background -int R, int G, int B -limit_256 -38 , ';', 5, ';', R, ';', G, ';', B, 'm' -// comment -foreground -COLOR color - -(int)color + 30, 'm' +// Show/hide cursor + cursor_show + | + | + "?25h" -// hello there -background -COLOR color + cursor_hide + | + | + "?25l" -(int)color + 40, 'm' +// Show/hide alternate buffer + abuf_show + | + | + "?1049h" - - - + abuf_hide + | + | + "?1049l"