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

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:
M CMakeLists.txt | + -
M src/generator.h | ++++++++++++ --
M src/lexer.l | +++++ ----
M src/parser.y | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --------------------
M src/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"