alec

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

commit9dc03661ab1baccce726bd37b704d7ae34a4caa5
parent9dbfc0ddfc5fa3f502132b72ae52a48d4af8ee97
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateTue, 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|+-
Msrc/generator.h|++++++++++++--
Msrc/lexer.l|+++++----
Msrc/parser.y|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/rules|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------

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"