/* gpasm preprocessor implementation
Copyright 2012 Borut Ražem
*/
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "evaluate.h"
#include "gpmsg.h"
#include "directive.h"
#include "gptypes.h"
#include "gpsymbol.h"
#include "preprocess.h"
#define DEBUG 0
#define DBG_printf (!DEBUG) ? (void)0 : (void)printf
#define BUF_REPLACE(buf, start, end, len, repl_buf, repl_len, buf_size) \
do { \
memmove(&(buf)[(start) + (repl_len)], &(buf)[(end)], (len) - (end)); \
memcpy(&(buf)[(start)], (repl_buf), (repl_len)); \
(len) += (repl_len) - ((end) - (start)); \
(end) = (start) + (repl_len); \
} \
while (0)
typedef bool (*substitute_func_t)(char *Buf, int Begin, int *End, int *N, int Max_size, int Level);
static pnode_t *param_list = NULL;
struct arg_list_s {
const char *str;
struct arg_list_s *next;
} *arg_list = NULL, *arg_list_tail = NULL;
/*------------------------------------------------------------------------------------------------*/
static const char *
_check_defines(char *Symbol, int Symlen, pnode_t **Param_list_p)
{
symbol_t *sym;
pnode_t *p;
pnode_t *p2;
const char *subst;
*Param_list_p = NULL;
/* If not quoted, check for #define substitution. */
sym = gp_sym_get_symbol_len(state.stDefines, Symbol, Symlen);
if (sym == NULL) {
return NULL;
}
p = (pnode_t *)gp_sym_get_symbol_annotation(sym);
if (p == NULL) {
return "";
}
assert(PnIsList(p));
p2 = PnListHead(p);
assert(PnIsString(p2));
subst = PnString(p2);
*Param_list_p = PnListTail(p);
if (subst == NULL) {
return "";
}
/* check for a bad subsitution */
return (int)strlen(subst) == Symlen && !strncmp(Symbol, subst, Symlen) ? NULL : subst;
}
/*------------------------------------------------------------------------------------------------*/
static int _is_first_iden(int Ch)
{
return (!isascii(Ch) || (Ch == '_') || (Ch == '.') || (Ch == '?') || (Ch == '@') || (Ch == '#') || isalpha(Ch));
}
/*------------------------------------------------------------------------------------------------*/
static int _is_iden(int Ch) {
return (_is_first_iden(Ch) || isdigit(Ch));
}
/*------------------------------------------------------------------------------------------------*/
static _inline void _skip_spaces(const char *Buf, int *Num) {
while (isspace((unsigned char)Buf[*Num])) {
++(*Num);
}
}
/*------------------------------------------------------------------------------------------------*/
static void _add_arg(const char *Str) {
arg_list_s*pnew = new arg_list_s;
pnew->str = Str;
pnew->next = NULL;
if (!arg_list) arg_list = pnew;
else arg_list_tail->next = pnew;
arg_list_tail = pnew;
}
/*------------------------------------------------------------------------------------------------*/
static void _free_list(struct arg_list_s *List) {
if (List) {
if (List->next) {
_free_list(List->next);
free(List);
}
}
}
/*------------------------------------------------------------------------------------------------*/
static void
_free_arg_list(void)
{
_free_list(arg_list);
arg_list = NULL;
arg_list_tail = NULL;
}
/*------------------------------------------------------------------------------------------------*/
static bool
_substitute_define_param(char *Buf, int Begin, int *End, int *Num, int Max_size, int Level)
{
struct arg_list_s *argp;
pnode_t *parp;
int mlen;
int len;
argp = arg_list;
parp = param_list;
mlen = *End - Begin;
assert(mlen > 0);
/* find argument */
while (parp) {
assert(PnIsSymbol(PnListHead(parp)));
if (strncmp(&Buf[Begin], PnSymbol(PnListHead(parp)), mlen) == 0) {
/* substitute */
len = strlen(argp->str);
DBG_printf("@@@substituting parameter %*.*s with %s\n", mlen, mlen, &Buf[Begin], argp->str);
if ((*Num + len - mlen) >= Max_size) {
gpmsg(GPE_INTERNAL, NULL, "Flex buffer too small.");
return false;
}
BUF_REPLACE(Buf, Begin, *End, *Num, argp->str, len, Max_size);
return true;
}
parp = PnListTail(parp);
argp = argp->next;
}
return false; /* no substitution */
}
/*------------------------------------------------------------------------------------------------*/
static bool _preprocess(char *Buf, int Begin, int *End, int *Num, int Max_size, substitute_func_t Substitute, int Level);
static bool
_substitute_define(char *Buf, int Begin, int *End, int *Num, int Max_size, int Level)
{
int mlen;
const char *sub;
int size;
int n_params;
bool bracket;
int n_args;
int start1;
int end1;
int _state;
bool prev_esc;
int brackdepth;
int len;
mlen = *End - Begin;
if (mlen <= 0) {
/* nothing to substitute */
return false;
}
if ((sub = _check_defines(&Buf[Begin], mlen, ¶m_list))) {
n_params = eval_list_length(param_list);
DBG_printf("define %*.*s has %d parameters\n", mlen, mlen, &Buf[Begin], n_params);
if (n_params != 0) {
/* has parameters: collect arguments */
bracket = false;
n_args = 0;
_skip_spaces(Buf, End);
if (Buf[*End] == '(') {
++(*End);
bracket = true;
}
for (; ; ) {
_state = 0;
prev_esc = false;
brackdepth = 0;
_skip_spaces(Buf, End);
start1 = *End;
while ((*End < *Num) &&
((_state != 0) ||
(brackdepth != 0) ||
((Buf[*End] != ',') &&
(Buf[*End] != ';') &&
((bracket && (Buf[*End] != ')')) ||
(!bracket && (Buf[*End] != '\n')))))) {
switch (Buf[*End]) {
case '(': {
if (_state == 0) {
++brackdepth;
}
break;
}
case ')': {
if (_state == 0) {
--brackdepth;
}
break;
}
case '\\':
prev_esc = (_state != 0) ? !prev_esc : false;
break;
case '"':
case '\'':
if (!prev_esc) {
_state = (_state == 0) ? Buf[*End] : ((_state == Buf[*End]) ? 0 : _state);
}
default:
prev_esc = false;
break;
}
++(*End);
}
/* right trim */
end1 = *End - 1;
while ((end1 >= 0) && isspace((unsigned char)Buf[end1])) {
--end1;
}
++end1;
_add_arg(GP_Strndup(&Buf[start1], end1 - start1));
++n_args;
if (*End < *Num) {
if ((bracket && (Buf[*End] == ')')) || (!bracket && ((Buf[*End] == '\n') || (Buf[*End] == ';')))) {
if (Buf[*End] == ';') {
/* skip to the trailing newline */
*End = (Buf[*Num - 1] == '\n') ? (*Num - 1) : *Num;
}
else {
/* don't eat newline! */
if (Buf[*End] != '\n') {
++(*End);
}
}
if (n_args == n_params) {
len = strlen(sub);
/* substitute define parameters */
if ((*Num + len - mlen) >= Max_size) {
gpmsg(GPE_INTERNAL, NULL, "Flex buffer too small.");
return false;
}
else {
DBG_printf("@1@substituting define parameter %*.*s ", mlen, mlen, &Buf[Begin]);
BUF_REPLACE(Buf, Begin, *End, *Num, sub, len, Max_size);
/* recurse preprocess with increased level */
_preprocess(Buf, Begin, End, Num, Max_size, &_substitute_define_param, Level + 1);
_free_arg_list();
/* substitute defines */
/* recurse preprocess with increased level */
_preprocess(Buf, Begin, End, Num, Max_size, &_substitute_define, Level + 1);
size = *End - Begin;
DBG_printf("with %*.*s\n", size, size, &Buf[Begin]);
return true;
}
}
else {
/* error n_args != n_params: no substitution */
_free_arg_list();
return false;
}
}
else if (Buf[*End] != ',') {
/* error unknown delimiter: no substitution */
_free_arg_list();
return false;
}
++(*End);
}
if (*End >= *Num) {
/* error no ending bracket or newline: no substitution */
_free_arg_list();
return false;
}
} /* for each argument */
} /* if has parameters */
else {
len = strlen(sub);
/* substitute define */
if ((*Num + len - mlen) >= Max_size) {
gpmsg(GPE_INTERNAL, NULL, "Flex buffer too small.");
return false;
}
DBG_printf("@2@substituting define %*.*s ", mlen, mlen, &Buf[Begin]);
BUF_REPLACE(Buf, Begin, *End, *Num, sub, len, Bax_size);
/* recurse preprocess with increased level */
_preprocess(Buf, Begin, End, Num, Max_size, &_substitute_define, Level + 1);
size = *End - Begin;
DBG_printf("with %*.*s\n", size, size, &Buf[Begin]);
}
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
static bool
_no_process_iden(const char *Iden, int Len)
{
static const char * const iden_tbl[] = {
"#define",
"#ifdef",
"#ifndef",
"#elifdef",
"#elifndef",
"#undefine",
"define",
"ifdef",
"ifndef",
"elifdef",
"elifndef",
};
unsigned i;
for (i = 0; i < ARRAY_SIZE(iden_tbl); ++i) {
if ((int)strlen(iden_tbl[i]) == Len && !strncasecmp(Iden, iden_tbl[i], Len)) return true;
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
static bool _preprocess(char *Buf, int Begin, int *End, int *Num, int Max_size, substitute_func_t Substitute, int Level) {
int start;
int _state; /* '"': in double quotes; '\'': in single quotes; ';': in comment */
bool prev_esc; /* true: prev char was escape */
int in_hv; /* in #v */
bool number_start; /* true: possible start of a x'nnn' formatted number */
bool substituted; /* if there was a substitution in the preprocess run */
int i;
int c;
int size;
int end1;
int prev_n;
if (Level >= PREPROC_MAX_DEPTH) {
gpmsg(GPE_STRCPLX, NULL);
return false;
}
start = -1;
_state = 0;
in_hv = 0;
prev_esc = false;
number_start = false;
substituted = false;
size = *End - Begin;
DBG_printf("---Preprocessing %*.*s\n", size, size, &Buf[Begin]);
for (i = Begin; i < *End; ++i) {
c = Buf[i];
if (_state == 0) {
if (c == '#') {
in_hv = '#';
}
else if ((in_hv == '#') && ((c == 'v') || (c == 'V'))) {
in_hv = 'v';
}
else if ((in_hv == 'v') && (c == '(')) {
if (start != -1) {
if (start < (i - 2)) {
/* preprocess the identifier before #v */
end1 = i - 2;
prev_n = *Num;
size = end1 - start;
DBG_printf("@1@Preprocessing identifier: %*.*s\n", size, size, &Buf[start]);
substituted |= (*Substitute)(Buf, start, &end1, Num, Max_size, Level);
*End += *Num - prev_n;
i = end1 + 2;
}
start = -1;
}
in_hv = '(';
}
else {
in_hv = 0;
}
if ((start == -1) && _is_first_iden(c)) {
switch (c) {
case 'a': case 'A':
case 'b': case 'B':
case 'd': case 'D':
case 'h': case 'H':
case 'o': case 'O':
number_start = true;
break;
default:
number_start = false;
break;
}
start = i;
}
else {
if ((start != -1) && !_is_iden(c)) {
if ((Level == 0) && _no_process_iden(&Buf[start], i - start)) {
start = -1;
break;
}
if ((c != '\'') || !number_start) {
prev_n = *Num;
size = i - start;
DBG_printf("@2@Preprocessing identifier: %*.*s\n", size, size, &Buf[start]);
substituted |= (*Substitute)(Buf, start, &i, Num, Max_size, Level);
*End += *Num - prev_n;
}
start = -1;
}
number_start = false;
}
}
switch (c) {
case '\\':
prev_esc = ((_state == '"') || (_state == '\'')) ? (!prev_esc) : false;
break;
case ';': {
if (_state == 0) {
_state = c;
}
prev_esc = false;
break;
}
case '"':
case '\'':
if (!prev_esc && (_state != ';')) {
_state = (_state == 0) ? c : ((_state == c) ? 0 : _state);
}
default:
prev_esc = false;
break;
}
}
if (start != -1) {
prev_n = *Num;
size = i - start;
DBG_printf("@3@Preprocessing identifier: %*.*s\n", size, size, &Buf[start]);
substituted |= (*Substitute)(Buf, start, &i, Num, Max_size, Level);
*End += *Num - prev_n;
}
size = *End - Begin;
DBG_printf("+++Preprocessed %*.*s; substituted = %d\n", size, size, &Buf[Begin], substituted);
return substituted;
}
/*------------------------------------------------------------------------------------------------*/
static bool
_preprocess_hv(char *Buf, int Begin, int *End, int *Num, int Max_size)
{
char res_buf[11];
bool substituted;
int size;
int res_len;
int prev_n;
size = *End - Begin;
substituted = false;
DBG_printf("---preprocess_hv: %*.*s\n", size, size, &Buf[Begin]);
while (Begin < *End) {
size = *End - Begin;
DBG_printf("***Parsing chunk: %*.*s\n", size, size, &Buf[Begin]);
if (ppparse_chunk(Buf, Begin, *End)) {
substituted = true;
DBG_printf("col_begin = %d; col_end = %d; result = %d\n", ppcol_begin, ppcol_end, ppresult);
res_len = snprintf(res_buf, sizeof(res_buf), "%d", ppresult);
if ((*Num + res_len - (ppcol_end - ppcol_begin)) >= Max_size) {
gpmsg(GPE_INTERNAL, NULL, "Flex buffer too small.");
return false;
}
prev_n = *Num;
DBG_printf ("@@@Subtituting %*.*s ", ppcol_end - ppcol_begin, ppcol_end - ppcol_begin, &Buf[ppcol_begin]);
BUF_REPLACE(Buf, ppcol_begin, ppcol_end, *Num, res_buf, res_len, Max_size);
*End += *Num - prev_n;
DBG_printf("with %*.*s\n", res_len, res_len, &Buf[ppcol_begin]);
Begin = ppcol_end;
DBG_printf("buf = %*.*s\n", *Num, *Num, Buf);
}
else {
Begin = ppcol_end;
}
}
DBG_printf("+++preprocess_hv: %*.*s\n", *Num, *Num, Buf);
return substituted;
}
/*------------------------------------------------------------------------------------------------*/
static void
_preprocess_hv_params(char *Buf, int Begin, int *End, int *Num, int Max_size)
{
int start;
int _state; /* '"': in double quotes; '\'': in single quotes; ';': in comment; '(' in #v argument */
bool prev_esc; /* true: prev char was escape */
int in_hv; /* in #v */
int hv_parenth; /* #v parenthesis nesting depth */
int i;
int c;
int prev_n;
start = -1;
_state = 0;
prev_esc = false;
in_hv = 0;
hv_parenth = 0;
DBG_printf("---preprocess_hv_params: %*.*s\n", *End, *End, Buf);
for (i = Begin; i < *End; ++i) {
c = Buf[i];
if (_state == '(') {
if (in_hv == '(') {
start = i;
in_hv = 0;
}
if (c == '(') {
++hv_parenth;
}
else if (c == ')') {
if (--hv_parenth <= 0) {
prev_n = *Num;
_preprocess_hv_params(Buf, start, &i, Num, Max_size);
_preprocess(Buf, start, &i, Num, Max_size, &_substitute_define, 0);
_preprocess_hv(Buf, start, &i, Num, Max_size);
*End += *Num - prev_n;
start = -1;
_state = 0;
}
}
}
else if (_state == 0) {
if (c == '#') {
in_hv = '#';
}
else if ((in_hv == '#') && ((c == 'v') || (c == 'V'))) {
in_hv = 'v';
}
else if ((in_hv == 'v') && (c == '(')) {
in_hv = '(';
++hv_parenth;
_state = '(';
}
else {
in_hv = 0;
}
}
if (_state == 0) {
if ((start == -1) && _is_first_iden(c)) {
start = i;
}
}
switch (c) {
case '\\':
prev_esc = ((_state == '"') || (_state == '\'')) ? (!prev_esc) : false;
break;
case ';': {
if (_state == 0) {
_state = c;
}
prev_esc = false;
break;
}
case '"':
case '\'':
if (!prev_esc && (_state != ';')) {
_state = (_state == 0) ? c : ((_state == c) ? 0 : _state);
}
default:
prev_esc = false;
break;
}
}
DBG_printf("+++preprocess_hv_params: %*.*s\n", *End, *End, Buf);
}
/*------------------------------------------------------------------------------------------------*/
static const char *
_check_macro_params(char *Symbol, int Symlen)
{
const symbol_t *sym;
const pnode_t *p;
const pnode_t *p2;
const char *subst;
sym = gp_sym_get_symbol_len(state.stMacroParams, Symbol, Symlen);
if (sym == NULL) {
return NULL;
}
p = (const pnode_t *)gp_sym_get_symbol_annotation(sym);
if (p == NULL) {
return "";
}
assert(PnIsList(p));
assert(PnListTail(p) == NULL);
p2 = PnListHead(p);
assert(PnIsString(p2));
subst = PnString(p2);
if (subst == NULL) {
return "";
}
/* check for a bad subsitution */
return (int)strlen(subst) == Symlen && !strncmp(Symbol, subst, Symlen) ? NULL : subst;
}
/*------------------------------------------------------------------------------------------------*/
static bool
_substitute_macro_param(char *Buf, int Begin, int *End, int *Num, int Max_size, int Level)
{
int mlen;
int len;
const char *sub;
mlen = *End - Begin;
if (mlen <= 0) {
/* nothing to substitute */
return false;
}
if ((sub = _check_macro_params(&Buf[Begin], mlen))) {
len = strlen(sub);
if ((*Num + len - mlen) >= Max_size) {
gpmsg(GPE_INTERNAL, NULL, "Flex buffer too small.");
return false;
}
DBG_printf("@@@substituting macro parameter %*.*s with %*.*s\n", mlen, mlen, &Buf[Begin], len, len, sub);
BUF_REPLACE(Buf, Begin, *End, *Num, sub, len, Max_size);
return true;
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
static void _set_source_line(const char *Line, int Len, src_line_t *Src_line) {
if (Src_line->line == 0) {
Src_line->size = 128;
Src_line->line = new char[Src_line->size];
}
if (Len>0 && Line[Len - 1]=='\n') --Len; /* ignore trailing newline */
if ((int)Src_line->size <= Len) {
do {
Src_line->size *= 2;
}
while ((int)Src_line->size <= Len);
Src_line->line = (char*)GP_Realloc(Src_line->line, Src_line->size);
}
if (Len > 0) {
memcpy(Src_line->line, Line, Len);
Src_line->line[Len] = '\0';
}
else {
Src_line->line[0] = '\0';
}
}
/*------------------------------------------------------------------------------------------------*/
static _inline bool _in_macro_expansion(void)
{
const source_context_t *p;
for (p = state.src_list.last; p != NULL; p = p->prev) {
if (p->type == SRC_MACRO) {
return true;
}
}
return false;
}
/*------------------------------------------------------------------------------------------------*/
void
preprocess_line(char *Buf, int *Num, int Max_size)
{
bool res;
int end;
bool macro_expansion;
end = *Num;
if (IN_MACRO_WHILE_DEFINITION) {
/* don't preprocess source line if in macro definition */
_set_source_line(Buf, *Num, &state.src_list.last->curr_src_line);
}
else {
macro_expansion = _in_macro_expansion();
if (macro_expansion) {
/* preprocess macro parameters */
_preprocess(Buf, 0, &end, Num, Max_size, &_substitute_macro_param, 1);
}
/* preprocess #v parameters */
_preprocess_hv_params(Buf, 0, &end, Num, Max_size);
_preprocess_hv(Buf, 0, &end, Num, Max_size);
if (!macro_expansion) {
/* set only #v processed source line if not in macro expansion */
_set_source_line(Buf, *Num, &state.src_list.last->curr_src_line);
}
/* preprocess line */
do {
res = _preprocess(Buf, 0, &end, Num, Max_size, &_substitute_define, 0);
res |= _preprocess_hv(Buf, 0, &end, Num, Max_size);
}
while (res);
if (macro_expansion) {
/* set processed source line if in macro expansion */
_set_source_line(Buf, *Num, &state.src_list.last->curr_src_line);
}
}
if (state.preproc.f) {
/* set current preprocessed source line if preprocessed asm file emission enabled */
_set_source_line(Buf, *Num, &state.preproc.curr_src_line);
}
}
Detected encoding: UTF-8 | 0
|