feat: First draft of preprocessor macros

This commit is contained in:
overflowerror 2024-05-18 00:00:36 +02:00
parent 6cce82a1ea
commit f7adfd62b3
7 changed files with 240 additions and 25 deletions

View file

@ -8,6 +8,13 @@ strbuf_t strbuf_new(void) {
return buffer;
}
strbuf_t strbuf_from(const char* str) {
strbuf_t buffer = strbuf_new();
strbuf_append(buffer, str);
return buffer;
}
strbuf_t _strbuf_replace(strbuf_t buffer, char* needle, char* replace) {
size_t offset = 0;

View file

@ -34,6 +34,7 @@ typedef char* strbuf_t;
}
strbuf_t strbuf_new(void);
strbuf_t strbuf_from(const char*);
strbuf_t _strbuf_replace(strbuf_t, char*, char*);
#define strbuf_free(b) list_free(b)

View file

@ -17,6 +17,7 @@ char "'"([^\\]|[\\]n|[\\]t|[\\]{2})"'"
id [a-zA-Z_][a-zA-Z0-9_]*
include "#include"
macro "#macro"
%option noyywrap
%option nodefault
@ -26,7 +27,9 @@ include "#include"
%x COMMENT
%x STRING
%x MACRO
%x MACRO_DEF
%x MACRO_VAL
%x MACRO_ARGS
%{
@ -45,6 +48,11 @@ extern void push_file_to_yy_stack(FILE* file) {
yypush_buffer_state(state);
}
extern void push_str_to_yy_stack(const char* str) {
yypush_buffer_state(YY_CURRENT_BUFFER);
yy_scan_string(str);
}
char char_escape(const char* yytext) {
if (yytext[1] == '\\') {
if (yytext[2] == '\\') return '\\';
@ -56,14 +64,15 @@ char char_escape(const char* yytext) {
}
}
strbuf_t strbuf = NULL;
int nested_braces = 0;
%}
%%
%{
strbuf_t strbuf = NULL;
%}
<INITIAL>"/*" { BEGIN(COMMENT); }
@ -75,6 +84,33 @@ strbuf_t strbuf = NULL;
<INITIAL>"//"[^\n]* { /* single line comment */ }
<INITIAL>{include} { return PRE_INCLUDE; }
<INITIAL>{macro} { BEGIN MACRO_DEF; return PRE_MACRO; }
<MACRO_DEF>{id} { yylval.id = strdup(yytext); return ID; }
<MACRO_DEF>"(" { return OPENING_BRACKETS; }
<MACRO_DEF>")" { return CLOSING_BRACKETS; }
<MACRO_DEF>"," { return COMMA; }
<MACRO_DEF>{whitespace}+ { }
<MACRO_DEF>"{" { BEGIN MACRO_VAL; nested_braces = 0; strbuf_clear(strbuf); }
<MACRO_DEF>. { lex_panic("unknown token: %s\n", yytext); }
<MACRO_VAL>"{" { nested_braces++; strbuf_append_c(strbuf, yytext[0]); }
<MACRO_VAL>"}" {
if (nested_braces-- == 0) {
BEGIN INITIAL;
yylval.str = strdup(strbuf);
return MACRO_CONTENT;
} else {
strbuf_append_c(strbuf, yytext[0]);
}
}
<MACRO_VAL>{whitespace}+ { strbuf_append(strbuf, yytext); }
<MACRO_VAL>. { strbuf_append_c(strbuf, yytext[0]); }
<INITIAL>{id}"$(" { strbuf_clear(strbuf); yylval.id = strdup(yytext); BEGIN MACRO_ARGS; return MACRO_CALL; }
<MACRO_ARGS>[^\\)]* { strbuf_append(strbuf, yytext); }
<MACRO_ARGS>"\\)" { strbuf_append_c(strbuf, yytext[1]); }
<MACRO_ARGS>"\\(" { strbuf_append_c(strbuf, yytext[1]); }
<MACRO_ARGS>"\\" { strbuf_append(strbuf, yytext); }
<MACRO_ARGS>")" { yylval.str = strdup(strbuf); BEGIN INITIAL; return MACRO_CONTENT; }
<INITIAL>";" { return SEMICOLON; }
@ -122,13 +158,6 @@ strbuf_t strbuf = NULL;
<INITIAL>"]" { return CLOSING_SQ_BRACKETS; }
<INITIAL>"," { return COMMA; }
<INITIAL>"$(" { BEGIN MACRO; strbuf_clear(strbuf); }
<MACRO>[^\\)]* { strbuf_append(strbuf, yytext); }
<MACRO>"\\)" { strbuf_append_c(strbuf, yytext[1]); }
<MACRO>"\\(" { strbuf_append_c(strbuf, yytext[1]); }
<MACRO>"\\" { strbuf_append(strbuf, yytext); }
<MACRO>")" { yylval.str = strdup(strbuf); BEGIN INITIAL; return MACRO_CONTENT; }
<INITIAL>{hex} { yylval.number = strtol(yytext + 2, NULL, 16); return NUM; }
<INITIAL>{dec} { yylval.number = strtol(yytext, NULL, 10); return NUM; }
<INITIAL>{oct} { yylval.number = strtol(yytext + 1, NULL, 8); return NUM; }

View file

@ -36,14 +36,13 @@ extern struct block* program;
%type <block> stats optelse block
%type <statement> stat definition assignment exprstat if while map
%type <expr> expr literal variable macroexpr builtincall noncalcexpr argumentlist arrayliteral
%type <expr> expr literal variable builtincall noncalcexpr argumentlist arrayliteral
%type <expr> calcexpr1 calcexpr2 calcexpr3 calcexpr4 calcexpr5 calcexpr6 calcexpr7 calcexpr8
%token <number> NUM
%token <ch> CHAR
%token <str> STR
%token <id> ID
%token <str> MACRO_CONTENT
%token SEMICOLON
%token ASSIGNMENT
@ -79,6 +78,9 @@ extern struct block* program;
%token IN
%token PRE_INCLUDE
%token PRE_MACRO
%token MACRO_CALL
%token MACRO_CONTENT
%start file
@ -280,7 +282,6 @@ expr: noncalcexpr
noncalcexpr: literal
| variable
| macroexpr
| builtincall
| OPENING_BRACKETS expr CLOSING_BRACKETS
{
@ -325,12 +326,6 @@ arrayliteral: OPENING_SQ_BRACKETS NUM CLOSING_SQ_BRACKETS
}
;
macroexpr: ID MACRO_CONTENT
{
$$ = macro_expression_new($1, $2);
}
;
%%
void yyerror(const char* s) {

View file

@ -1,10 +1,18 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <alloca.h>
#include "y.tab.h"
#include <error.h>
#include <list.h>
#include <dict.h>
#include <alloc.h>
#include <strbuf.h>
void push_file_to_yy_stack(FILE*);
void push_str_to_yy_stack(const char*);
int yylex(void);
extern char* yytext;
@ -15,7 +23,7 @@ static void handle_include(void) {
if (token != STR) {
fprintf(stderr, "preprocessor: include: file name expected, got: %s\n", yytext);
panic("processor syntax error");
panic("preprocessor syntax error");
}
const char* filename = yylval.str;
@ -27,15 +35,184 @@ static void handle_include(void) {
push_file_to_yy_stack(file);
}
struct macro {
char* name;
char** argument_list;
char* content;
};
static dict_t* macros = NULL;
static void handle_macro_definition(void) {
struct macro* macro = safe_malloc(sizeof(struct macro));
macro->argument_list = list_new(char*);
int token = yylex();
if (token != ID) {
fprintf(stderr, "preprocessor: macro: identifier expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
macro->name = yylval.id;
token = yylex();
if (token != OPENING_BRACKETS) {
fprintf(stderr, "preprocessor: macro: ( expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
bool is_first = true;
while (true) {
token = yylex();
if (token == CLOSING_BRACKETS) {
break;
}
if (!is_first) {
if (token != COMMA) {
fprintf(stderr, "preprocessor: macro: , expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
} else {
if (token != ID) {
fprintf(stderr, "preprocessor: macro: identifier expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
list_add(macro->argument_list, yylval.id);
}
}
token = yylex();
if (token != MACRO_CONTENT) {
fprintf(stderr, "preprocessor: macro: body expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
macro->content = yylval.str;
if (macros == NULL) {
macros = dict_new();
}
printf("new macro: %s\n ", macro->name);
for (size_t i = 0; i < list_size(macro->argument_list); i++) {
printf("%s", macro->argument_list[i]);
if (i == list_size(macro->argument_list) - 1) {
printf(", ");
}
}
printf("\n {%s}\n", macro->content);
dict_put(macros, macro->name, macro);
}
char** split_arguments(char* str) {
strbuf_t* argument_list = list_new(char*);
strbuf_t argument = strbuf_new();
while(*str) {
if (*str != ',') {
strbuf_append_c(argument, *str);
} else {
list_add(argument_list, strdup(argument));
strbuf_clear(argument);
}
str++;
}
if (strlen(argument) > 0) {
list_add(argument_list, strdup(argument));
}
strbuf_free(argument);
return argument_list;
}
void handle_macro_call(void) {
char* id = yylval.id;
id[strlen(id) - 2] = '\0';
int token = yylex();
if (token != MACRO_CONTENT) {
fprintf(stderr, "preprocessor: macro: arguments expected, got: %s\n", yytext);
panic("preprocessor syntax error");
}
char* args_string = yylval.str;
printf("call to %s: %s\n", id, args_string);
struct macro* macro = dict_get(macros, id);
if (macro == NULL) {
fprintf(stderr, "preprocessor: macro: not found: %s\n", id);
panic("preprocessor error");
}
strbuf_t* args = split_arguments(args_string);
size_t given_argc = list_size(args);
size_t expected_argc = list_size(macro->argument_list);
if (given_argc != expected_argc) {
fprintf(stderr, "preprocessor: macro: %s: argument count mismatch: %zu/%zu\n", id, given_argc, expected_argc);
panic("preprocessor error");
}
strbuf_t result = strbuf_from(macro->content);
for (size_t i = 0; i < expected_argc; i++) {
char* arg_id_with_prefix = alloca(strlen(macro->argument_list[i]) + 1 + 1);
arg_id_with_prefix[0] = '$';
arg_id_with_prefix[1] = '\0';
strcat(arg_id_with_prefix, macro->argument_list[i]);
printf("arg: %s -> %s\n", arg_id_with_prefix, args[i]);
strbuf_replace(result, arg_id_with_prefix, args[i]);
strbuf_free(args[i]);
}
list_free(args);
printf("result: %s\n", result);
push_str_to_yy_stack(result);
//strbuf_free(result);
}
extern int preproc_lex(void) {
int token = yylex();
switch(token) {
case PRE_INCLUDE:
handle_include();
token = yylex();
break;
//printf("pre: %d\n", token);
bool is_normal_token = false;
while (!is_normal_token) {
switch (token) {
case PRE_INCLUDE:
handle_include();
token = yylex();
break;
case PRE_MACRO:
handle_macro_definition();
token = yylex();
break;
case MACRO_CALL:
handle_macro_call();
token = yylex();
break;
default:
is_normal_token = true;
}
}
//printf("post: %d\n", token);
return token;
}

View file

@ -0,0 +1,5 @@
#macro greet(name) {
print("Hello, ", $name, "!\n")
}
greet$("World");

View file

@ -0,0 +1 @@
[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++>[-]++++++++++<<<<<<<<<<<<<.>.>.>.>.>.>.>.>.>.>.>.>.>.