/* cpp-like preprocessor. Implemements #define, #if, #ifdef, #else, #endif. Does *not* do any macro substitution. #define takes a simple integer, #if takes an integer expression. The integer expression correctly applies operator precedence rules. TO DO: #ifdef, #include "filename", and nested #if structures. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #ifndef FALSE #define TRUE (0==0) #define FALSE (0!=0) #endif static int tpp_if = FALSE; // are we in an if section? static int tpp_else = FALSE; // are we in an else? static int tpp_cond = FALSE; // was the last #if test true? static int tpp_val = 0; // last expr value static int lineno = 1; static char *token[128]; static int value[128]; static int nextfreetok = 0; static void tpp_set(FILE *source) { // read a var name and a value then \n // eg #define test 42 char tok[80]; char *s = tok; int i, c, rc; for (;;) { c = fgetc(source); if (!isalpha(c)) break; *s++ = c; } *s = '\0'; token[nextfreetok] = strdup(tok); for (i = 0; i <= nextfreetok; i++) { if (strcmp(tok, token[i]) == 0) { if (i == nextfreetok) nextfreetok++; else fprintf(stderr, "error: redefinition of %s\n", tok); break; } } rc = fscanf(source, "%d\n", &value[i]); if (rc != 1) { fprintf(stderr, "error: malformed #define %s\n", tok); exit(1); } } static void skipspace(FILE *source) { int c; for (;;) { c = fgetc(source); if (c != ' ' && c != '\t') break; } ungetc(c, source); // pascal-style lookahead... } static void tpp_test(FILE *source) { int rc, i, c, top = 0, stackp = 0; // top is inclusive int nest[100]; // nest[0] is never used. int input_stream[100]; // operand stack. nest[top] points to first operand after last '(' // stackp is exclusive and points to next free entry void insert_opener(void) { top += 1; nest[top] = stackp; } void insert_closer(void) { if (nest[top] == 0 && stackp == 0) { if (top <= 0) {fprintf(stderr, "internal error: top <= 0 on ')'\n"); exit(1);} else top -= 1; } else if (stackp-3 == nest[top]) { // replace a op b in input_stream with the result of the calculation. switch (input_stream[stackp-2]) { case '+': input_stream[stackp-3] = input_stream[stackp-3] + input_stream[stackp-1]; break; case '-': input_stream[stackp-3] = input_stream[stackp-3] - input_stream[stackp-1]; break; case '*': input_stream[stackp-3] = input_stream[stackp-3] * input_stream[stackp-1]; break; case '/': input_stream[stackp-3] = input_stream[stackp-3] / input_stream[stackp-1]; break; case '<': input_stream[stackp-3] = input_stream[stackp-3] < input_stream[stackp-1]; break; case '{': input_stream[stackp-3] = input_stream[stackp-3] <= input_stream[stackp-1]; break; case '>': input_stream[stackp-3] = input_stream[stackp-3] > input_stream[stackp-1]; break; case '}': input_stream[stackp-3] = input_stream[stackp-3] >= input_stream[stackp-1]; break; case '=': input_stream[stackp-3] = input_stream[stackp-3] == input_stream[stackp-1]; break; case '#': input_stream[stackp-3] = input_stream[stackp-3] != input_stream[stackp-1]; break; case '&': input_stream[stackp-3] = input_stream[stackp-3] && input_stream[stackp-1]; break; case '|': input_stream[stackp-3] = input_stream[stackp-3] || input_stream[stackp-1]; break; } stackp = nest[top]+1; top -= 1; } else if (stackp-1 == nest[top]) { // replace (a) with a (actually just tweak nest) // ASSERT: stackp == nest[top]; // replace input_stream[stackp] with contents of bracketed expr... top -= 1; } else {fprintf(stderr, "internal error: stackp = %d, nest[%d] = %d\n", stackp, top, nest[top]); exit(1);} if (top < 0) {fprintf(stderr, "error: spurious ')' in #if expression\n"); exit(1);} tpp_cond = ((tpp_val = input_stream[stackp-1]) != 0); } void insert(char *s) { int c; while ((c = *s++) != '\0') { if (c == '(') insert_opener(); else if (c == ')') insert_closer(); else input_stream[stackp++] = c; // operator... } } // read expression, set tpp_cond = (expr) != 0 tpp_cond = TRUE; nest[0] = 0; insert("(((((("); for (;;) { skipspace(source); c = fgetc(source); if (c == '\n') { // either evaluate, or give a syntax error insert("))))))"); if (top != 0) {fprintf(stderr, "error: unbalanced parentheses\n"); exit(1);} break; } else if (isdigit(c)) { ungetc(c, source); rc = fscanf(source, "%d", &i); // assert rc = 1 input_stream[stackp++] = i; } else if (isalpha(c)) { // token char tok[80]; char *s = tok; int i, rc; for (;;) { if (!isalpha(c)) break; *s++ = c; c = fgetc(source); } *s = '\0'; ungetc(c, source); token[nextfreetok] = strdup(tok); for (i = 0; i <= nextfreetok; i++) { if (strcmp(tok, token[i]) == 0) { if (i == nextfreetok) { fprintf(stderr, "error: %s not set\n", tok); exit(1); } break; } } input_stream[stackp++] = value[i]; } else { int c2 = fgetc(source); ungetc(c2, source); if (c == '(') { insert_opener(); } else if (c == ')') { insert_closer(); } else if (c == '*') { insert(")*("); } else if (c == '/') { insert(")/("); } else if (c == '+') { insert("))+(("); } else if (c == '-') { insert("))-(("); } else if (c == '<') { if (c2 == '=') {insert("))){((("); (void)fgetc(source);} else insert(")))<((("); } else if (c == '>') { if (c2 == '=') {insert(")))}((("); (void)fgetc(source);} else insert(")))>((("); } else if (c == '=' && c2 == '=') { insert(")))=((("); (void)fgetc(source); } else if (c == '!' && c2 == '=') { insert(")))#((("); (void)fgetc(source); } else if (c == '&' && c2 == '&') { insert("))))&(((("); (void)fgetc(source); } else if (c == '|' && c2 == '|') { insert(")))))|((((("); (void)fgetc(source); } else { fprintf(stderr, "error: invalid symbol '%c' in #if expression\n", c); exit(1); } } } } int main(int argc, char **argv) { FILE *source = fopen(argv[1], "r"); if (argc != 2) {fprintf(stderr, "syntax: %s file\n", argv[0]); exit(1);} if (source == NULL) {fprintf(stderr, "%s: %s\n", argv[0], strerror(errno)); exit(1);} for (;;) { int c = fgetc(source); if (c == EOF) break; else if (c == '#') {// No leading spaces or tabs allowed // we have a pre-processor line char tok[80]; char *s = tok; for (;;) { c = fgetc(source); if (!isalpha(c)) break; *s++ = c; } *s = '\0'; if (strcmp(tok, "define") == 0) { tpp_set(source); } else if (strcmp(tok, "if") == 0) { if (tpp_if || tpp_else) {fprintf(stderr, "Nested #if at line ?\n", lineno); exit(1);} // first hack does not allow nested constructs. Add later with a stack. Here and for ifdef tpp_test(source); tpp_if = TRUE; tpp_else = FALSE; } else if (strcmp(tok, "ifdef") == 0) { if (tpp_if || tpp_else) {fprintf(stderr, "Nested #if at line ?\n", lineno); exit(1);} // tpp_test(source); // not yet implemented ************************************************************************ tpp_cond = FALSE; // defaulting to 'not defined' for now tpp_if = TRUE; tpp_else = FALSE; } else if (strcmp(tok, "debug") == 0) { tpp_test(source); fprintf(stderr, "debug at line %d: %d\n", lineno, tpp_val); } else if (strcmp(tok, "else") == 0) { if (!tpp_if) {fprintf(stderr, "Bad #else at line ?\n", lineno); exit(1);} tpp_if = FALSE; tpp_else = TRUE; } else if (strcmp(tok, "endif") == 0) { tpp_if = FALSE; tpp_else = FALSE; } else { fprintf(stderr, "Invalid pre-processor line:\n"); fprintf(stderr, "#%s", tok); if (c != '\n') for (;;) { fputc(c, stderr); c = fgetc(source); if (c == '\n') break; } fputc(c, stderr); } } else { for (;;) { if ((tpp_if && tpp_cond) || (tpp_else && !tpp_cond) || !(tpp_if || tpp_else)) fputc(c, stdout); if (c == '\n') break; c = fgetc(source); } } } exit(0); return(0); }