Skip to content

Commit

Permalink
initial support for conditional directives (only #ifdef/#ifndef/#else…
Browse files Browse the repository at this point in the history
…/#endif for now)
  • Loading branch information
Plombo committed Dec 8, 2010
1 parent f5a3fa0 commit 0f77750
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 10 deletions.
2 changes: 2 additions & 0 deletions pp_lexer.c
Expand Up @@ -378,6 +378,8 @@ HRESULT pp_lexer_GetTokenIdentifier(pp_lexer* plexer, pp_token* theNextToken)
MAKETOKEN( PP_TOKEN_INCLUDE );}
else if (!strcmp( plexer->theTokenSource, "define")){
MAKETOKEN( PP_TOKEN_DEFINE );}
else if (!strcmp( plexer->theTokenSource, "undef")){
MAKETOKEN( PP_TOKEN_UNDEF );}
else if (!strcmp( plexer->theTokenSource, "pragma")){
MAKETOKEN( PP_TOKEN_PRAGMA );}
else if (!strcmp( plexer->theTokenSource, "ifdef")){
Expand Down
4 changes: 2 additions & 2 deletions pp_lexer.h
Expand Up @@ -50,8 +50,8 @@ typedef enum PP_TOKEN_TYPE {
PP_TOKEN_DIV, PP_TOKEN_MOD, PP_TOKEN_LT, PP_TOKEN_GT, PP_TOKEN_XOR, PP_TOKEN_BITWISE_OR,
PP_TOKEN_ERROR, PP_TOKEN_COMMENT_SLASH, PP_TOKEN_COMMENT_STAR_BEGIN,
PP_TOKEN_COMMENT_STAR_END, PP_TOKEN_NEWLINE, PP_TOKEN_WHITESPACE, PP_TOKEN_DIRECTIVE,
PP_TOKEN_INCLUDE, PP_TOKEN_DEFINE, PP_TOKEN_PRAGMA, PP_TOKEN_ELIF, PP_TOKEN_IFDEF,
PP_TOKEN_IFNDEF, PP_TOKEN_ENDIF, PP_TOKEN_EOF, PP_EPSILON, PP_END_OF_TOKENS
PP_TOKEN_INCLUDE, PP_TOKEN_DEFINE, PP_TOKEN_UNDEF, PP_TOKEN_PRAGMA, PP_TOKEN_ELIF,
PP_TOKEN_IFDEF, PP_TOKEN_IFNDEF, PP_TOKEN_ENDIF, PP_TOKEN_EOF, PP_EPSILON, PP_END_OF_TOKENS
}PP_TOKEN_TYPE;

/******************************************************************************
Expand Down
122 changes: 117 additions & 5 deletions pp_parser.c
Expand Up @@ -21,12 +21,12 @@

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <errno.h>
#include "List.h"
#include "pp_parser.h"
#include "borendian.h"
#include "openbor.h"

#define DEFAULT_TOKEN_BUFFER_SIZE (16 * 1024)
#define TOKEN_BUFFER_SIZE_INCREMENT (16 * 1024)
Expand All @@ -36,16 +36,18 @@
#undef printf
#define tracemalloc(name, size) malloc(size)
#define tracecalloc(name, size) calloc(1, size)
#define tracerealloc(ptr, size) realloc(ptr, size)
#define tracerealloc(ptr, size, os) realloc(ptr, size)
#define tracefree(ptr) free(ptr)
#define openpackfile(fname, pname) ((int)fopen(fname, "rb"))
#define readpackfile(hnd, buf, len) fread(buf, 1, len, (FILE*)hnd)
#define seekpackfile(hnd, loc, md) fseek((FILE*)hnd, loc, md)
#define tellpackfile(hnd) ftell((FILE*)hnd)
#define closepackfile(hnd) fclose((FILE*)hnd)
#define shutdown(ret, msg, args...) { fprintf(stderr, msg, ##args); exit(ret); }
#else // otherwise, we can use OpenBOR functionality like tracemalloc and writeToLogFile
#include "tracemalloc.h"
#include "openbor.h"
#include "globals.h"
#include "tracemalloc.h"
#include "packfile.h"
#define tellpackfile(hnd) seekpackfile(hnd, 0, SEEK_CUR)
#endif
Expand All @@ -66,6 +68,27 @@ char* tokens = NULL;
static int token_bufsize = 0;
static int tokens_length = 0;

/**
* Stack of conditional directives. The preprocessor can handle up to 16 nested
* conditionals. The stack is implemented efficiently as a 32-bit value.
*/
union {
int all;
struct {
unsigned others:30;
unsigned top:2;
};
} conditionals;

int num_conditionals = 0;

enum conditional_state {
cs_none = 0,
cs_true = 1,
cs_false = 2,
cs_done = 3
};

/**
* Emits a token to the token buffer, enlarging the token buffer if necessary.
* (Too bad strlcat() isn't part of the C standard library, or even in glibc.)
Expand Down Expand Up @@ -144,6 +167,18 @@ void pp_parser_reset()
}
}

void pp_error(pp_parser* self, char* format, ...)
{
char buf[1024] = {""};
va_list arglist;

sprintf(buf, "Preprocessor error: %s: ", self->filename);
va_start(arglist, format);
vsprintf(buf, format, arglist);
va_end(arglist);
shutdown(1, buf);
}

/**
* Preprocesses the entire source file.
* @return S_OK on success, E_FAIL on failure
Expand Down Expand Up @@ -255,10 +290,10 @@ HRESULT pp_parser_parse_directive(pp_parser* self) {

// Parse macro name and contents
strcpy(name, token.theSource);
skip_whitespace();
contents[0] = '\0';
while(1)
{
pp_lexer_GetNextToken(&self->lexer, &token);
if((token.theType == PP_TOKEN_NEWLINE) || (token.theType == PP_TOKEN_EOF)) { emit(token); break; }
else if(strcmp(token.theSource, "\\") == 0) pp_lexer_GetNextToken(&self->lexer, &token); // allows escaping line breaks with "\"

Expand All @@ -269,13 +304,23 @@ HRESULT pp_parser_parse_directive(pp_parser* self) {
return E_FAIL;
}
else strcat(contents, token.theSource);
pp_lexer_GetNextToken(&self->lexer, &token);
}

// Add macro to list
List_InsertAfter(&macros, contents, name);

break;
}
case PP_TOKEN_UNDEF:
pp_error(self, "#undef is not implemented yet");
break;
case PP_TOKEN_IF:
case PP_TOKEN_IFDEF:
case PP_TOKEN_IFNDEF:
case PP_TOKEN_ELIF:
case PP_TOKEN_ELSE:
case PP_TOKEN_ENDIF:
pp_parser_conditional(self, token.theType);
default:
printf("Preprocessor error: %s: unknown directive '%s'\n", self->filename, token.theSource);
return E_FAIL;
Expand All @@ -298,7 +343,11 @@ HRESULT pp_parser_include(pp_parser* self, char* filename)

// Open the file
handle = openpackfile(filename, packfile);
#ifdef PP_TEST
if(!handle)
#else
if(handle < 0)
#endif
{
printf("Preprocessor error: unable to open file '%s'\n", filename);
return E_FAIL;
Expand Down Expand Up @@ -333,6 +382,69 @@ HRESULT pp_parser_include(pp_parser* self, char* filename)
return S_OK;
}

/**
* Handles conditional directives.
* @param directive the type of conditional directive
*/
HRESULT pp_parser_conditional(pp_parser* self, PP_TOKEN_TYPE directive)
{
switch(directive)
{
case PP_TOKEN_IF:
case PP_TOKEN_IFDEF:
case PP_TOKEN_IFNDEF:
if(num_conditionals++ > 16) pp_error(self, "too many levels of nested conditional directives");
conditionals.all <<= 2; // push a new conditional state onto the stack
conditionals.top = pp_parser_eval_conditional(self, directive) ? cs_true : cs_false;
break;
case PP_TOKEN_ELIF:
if(conditionals.top == cs_done || conditionals.top == cs_true)
conditionals.top = cs_done;
else
conditionals.top = pp_parser_eval_conditional(self, directive) ? cs_true : cs_false;
break;
case PP_TOKEN_ELSE:
if(conditionals.top == cs_none) pp_error(self, "stray #else");
conditionals.top = conditionals.top == cs_false ? cs_true : cs_false;
break;
case PP_TOKEN_ENDIF:
if(conditionals.top == cs_none || num_conditionals-- < 0) pp_error(self, "stray #endif");
conditionals.all >>= 2; // pop a conditional state from the stack
break;
default:
pp_error(self, "unknown conditional directive type (ID=%d)", directive);
}

return S_OK;
}

bool pp_parser_eval_conditional(pp_parser* self, PP_TOKEN_TYPE directive)
{
pp_token token;

// all directives can have whitespace between the directive and the contents
skip_whitespace();

switch(directive)
{
case PP_TOKEN_IFDEF:
pp_lexer_GetNextToken(&self->lexer, &token); // FIXME: this and should others should check for E_FAIL
return List_FindByName(&macros, token.theSource);
case PP_TOKEN_IFNDEF:
pp_lexer_GetNextToken(&self->lexer, &token);
return !List_FindByName(&macros, token.theSource);
case PP_TOKEN_IF:
pp_error(self, "#if directive not yet supported");
break;
case PP_TOKEN_ELIF:
pp_error(self, "#elif directive not yet supported");
break;
default: pp_error(self, "internal error: evaluating an unknown conditional type");
}

return false;
}

/**
* Expands a macro.
* Pre: the macro is defined
Expand Down
7 changes: 4 additions & 3 deletions pp_parser.h
Expand Up @@ -34,17 +34,18 @@ typedef struct pp_parser {
bool newline;
} pp_parser;

// FIXME: nothing outside of pp_parser has any business accessing the token buffer
extern char* tokens;

//Constructor
void pp_parser_init(pp_parser* self, Script* script, char* filename, char* sourceCode);
void pp_parser_reset();
void pp_error(pp_parser* self, char* format, ...);
HRESULT pp_parser_parse(pp_parser* self);
HRESULT pp_parser_parse_directive(pp_parser* self);
HRESULT pp_parser_include(pp_parser* self, char* filename);
HRESULT pp_parser_conditional(pp_parser* self, PP_TOKEN_TYPE directive);
bool pp_parser_eval_conditional(pp_parser* self, PP_TOKEN_TYPE directive);
void pp_parser_insert_macro(pp_parser* self, char* name);

#endif



0 comments on commit 0f77750

Please sign in to comment.