mirror of
https://github.com/sigmasternchen/macrofuck
synced 2025-03-15 07:08:56 +00:00
feat: First draft of preprocessor macros
This commit is contained in:
parent
6cce82a1ea
commit
f7adfd62b3
7 changed files with 240 additions and 25 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
5
compiler/test/cases/025b-preproc-macro.in
Normal file
5
compiler/test/cases/025b-preproc-macro.in
Normal file
|
@ -0,0 +1,5 @@
|
|||
#macro greet(name) {
|
||||
print("Hello, ", $name, "!\n")
|
||||
}
|
||||
|
||||
greet$("World");
|
1
compiler/test/cases/025b-preproc-macro.out
Normal file
1
compiler/test/cases/025b-preproc-macro.out
Normal file
|
@ -0,0 +1 @@
|
|||
[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++>[-]++++++++++<<<<<<<<<<<<<.>.>.>.>.>.>.>.>.>.>.>.>.>.
|
Loading…
Reference in a new issue