Browse Source

proprocessor include, define and ifndef

Kajetan Johannes Hammerle 2 years ago
parent
commit
512dc5d2c7
6 changed files with 285 additions and 19 deletions
  1. 1 0
      tests/another
  2. 16 0
      tests/other
  3. 6 1
      tests/test
  4. 253 16
      tokenizer/FileReader.c
  5. 3 1
      tokenizer/FileReader.h
  6. 6 1
      tokenizer/Tokenizer.c

+ 1 - 0
tests/another

@@ -0,0 +1 @@
+// another

+ 16 - 0
tests/other

@@ -0,0 +1,16 @@
+#ifndef OTHER
+#define OTHER
+
+#include "another"
+
+void wusi(int a) {
+}
+
+#ifndef WUSI
+
+#ifndef WUSI
+#endif
+
+#endif
+
+#endif

+ 6 - 1
tests/test

@@ -1,4 +1,9 @@
+#include "other"
+#include "other"
+#include "other"
+#include "other"
+
 void main() {
     int a = 5;
-    test(a);
+    wusi(a);
 }

+ 253 - 16
tokenizer/FileReader.c

@@ -5,16 +5,50 @@
 
 #include "tokenizer/FileReader.h"
 
-static void frFullRead(FILE* file, const char* path, FileReader* fr, Error* e) {
+typedef char String[64];
+#define STRING_LENGTH ((int)sizeof(String))
+
+#define END_IF "#endif"
+#define END_IF_LENGTH ((int)sizeof(END_IF) - 1)
+
+static Error* error;
+
+typedef struct {
+    String name;
+} Define;
+
+#define DEFINES 256
+static Define defines[DEFINES];
+static int defineIndex = 0;
+
+static bool frHasError() {
+    return error->message[0] != '\0';
+}
+
+static bool frStringCompare(const String a, const char* b) {
+    return strcmp(a, b) == 0;
+}
+
+static void frInitFileReader(FileReader* fr, const char* path) {
+    fr->data = NULL;
+    fr->path = path;
+    fr->length = 0;
+    fr->readIndex = 0;
+    fr->line = 1;
+}
+
+static void frSystemError(const char* msg, FileReader* fr) {
+    eInitError(error, fr->line, "%s '%s': %s", msg, fr->path, strerror(errno));
+}
+
+static void frFullRead(FILE* file, FileReader* fr) {
     if(fseek(file, 0, SEEK_END)) {
-        eInitError(e, 1, "cannot seek end of file '%s': %s", path,
-                   strerror(errno));
+        frSystemError("cannot seek end of file", fr);
         return;
     }
     long length = ftell(file);
     if(length < 0) {
-        eInitError(e, 1, "cannot tell end of file '%s': %s", path,
-                   strerror(errno));
+        frSystemError("cannot tell end of file", fr);
         return;
     }
     rewind(file);
@@ -22,31 +56,234 @@ static void frFullRead(FILE* file, const char* path, FileReader* fr, Error* e) {
     fr->length = length;
     int readValues = fread(fr->data, 1, length, file);
     if(readValues != length) {
-        eInitError(e, 1, "cannot read file '%s': %s", path, strerror(errno));
+        frSystemError("cannot read file", fr);
         fr->data[0] = '\0';
+        fr->length = 0;
         return;
     }
     fr->data[length] = '\0';
-    eInitSuccess(e);
 }
 
-void frInit(const char* path, FileReader* fr, Error* e) {
-    fr->data = NULL;
-    fr->length = 0;
-    fr->readIndex = 0;
-
-    FILE* file = fopen(path, "r");
+static void frFullReadFile(FileReader* fr) {
+    FILE* file = fopen(fr->path, "r");
     if(file == NULL) {
-        eInitError(e, 1, "cannot open file '%s': %s", path, strerror(errno));
+        frSystemError("cannot open file", fr);
+        return;
+    }
+    frFullRead(file, fr);
+}
+
+static int frReadUntil(int from, char end, String s, FileReader* fr) {
+    int index = 0;
+    while(from < fr->length && index < STRING_LENGTH - 1 &&
+          fr->data[from] != end) {
+        s[index++] = fr->data[from];
+        from++;
+    }
+    s[index] = '\0';
+    return index;
+}
+
+static int frReadUntilOrLine(int from, char end, String s, FileReader* fr) {
+    int index = 0;
+    while(from < fr->length && index < STRING_LENGTH - 1 &&
+          fr->data[from] != end && fr->data[from] != '\n') {
+        s[index++] = fr->data[from];
+        from++;
+    }
+    s[index] = '\0';
+    return index;
+}
+
+static void frMakePath(String fullPath, const char* parent, String name) {
+    int i = 0;
+    int lastSlash = 0;
+    while(i < STRING_LENGTH - 1 && parent[i] != '\0') {
+        fullPath[i] = parent[i];
+        lastSlash = parent[i] == '/' ? i : lastSlash;
+        i++;
+    }
+    i = lastSlash + 1;
+    int nameI = 0;
+    while(i < STRING_LENGTH - 1 && name[nameI] != '\0') {
+        fullPath[i] = name[nameI];
+        i++;
+        nameI++;
+    }
+    fullPath[i] = '\0';
+}
+
+static void frReplace(int from, int to, FileReader* fr,
+                      FileReader* replacement) {
+    int replaceLength = replacement->length - 1;
+    replaceLength = replaceLength < 0 ? 0 : replaceLength;
+    int newLength = fr->length + replaceLength - (to - from);
+    unsigned char* newData = malloc(newLength + 1);
+    memcpy(newData, fr->data, from);
+    memcpy(newData + from, replacement->data, replaceLength);
+    memcpy(newData + from + replaceLength, fr->data + to, fr->length - to);
+    newData[newLength] = '\0';
+
+    free(fr->data);
+    fr->data = newData;
+    fr->length = newLength;
+}
+
+static int skipSpaces(int index, FileReader* fr) {
+    while(index < fr->length && fr->data[index] == ' ') {
+        index++;
+    }
+    return index;
+}
+
+static void frHandleInclude(int from, int length, FileReader* fr) {
+    int index = from + length;
+    index = skipSpaces(index, fr);
+    if(index >= fr->length || fr->data[index] != '"') {
+        eInitError(error, fr->line, "expected '\"' and not '%c' in '%s'",
+                   fr->data[index], fr->path);
         return;
     }
-    frFullRead(file, path, fr, e);
-    fclose(file);
+    index++;
+    String s;
+    int pathLength = frReadUntil(index, '"', s, fr) + 1;
+
+    String fullPath;
+    frMakePath(fullPath, fr->path, s);
+
+    FileReader include;
+    frInitFileReader(&include, fullPath);
+    frFullReadFile(&include);
+    if(frHasError()) {
+        frDelete(&include);
+        return;
+    }
+    frReplace(from, index + pathLength, fr, &include);
+    frDelete(&include);
+}
+
+static int frReadWord(String s, int from, FileReader* fr) {
+    int index = skipSpaces(from, fr);
+    int wordLength = frReadUntilOrLine(index, ' ', s, fr);
+    return index + wordLength;
+}
+
+static bool frIsDefined(String s) {
+    for(int i = 0; i < defineIndex; i++) {
+        if(frStringCompare(defines[i].name, s)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool frIsMatching(int from, FileReader* fr, const char* s) {
+    int i = 0;
+    while(true) {
+        if(s[i] == '\0') {
+            return true;
+        } else if(from >= fr->length || fr->data[from] != s[i]) {
+            return false;
+        }
+        from++;
+        i++;
+    }
+}
+
+static int frFindEndIf(int from, FileReader* fr) {
+    int layer = 0;
+    for(int i = from; i < fr->length; i++) {
+        if(frIsMatching(i, fr, END_IF)) {
+            if(layer == 0) {
+                return i;
+            }
+            layer--;
+        } else if(frIsMatching(i, fr, "#ifndef")) {
+            layer++;
+        }
+    }
+    return -1;
+}
+
+static void frHandleIfNotDefined(int from, int length, FileReader* fr) {
+    String s;
+    int end = frReadWord(s, from + length, fr);
+    int endIf = frFindEndIf(end, fr);
+    if(endIf == -1) {
+        eInitError(error, fr->line, "cannot find #endif in '%s'", fr->path);
+        return;
+    }
+    if(frIsDefined(s)) {
+        memset(fr->data + from, ' ', endIf - from + END_IF_LENGTH);
+    } else {
+        memset(fr->data + from, ' ', end - from);
+        memset(fr->data + endIf, ' ', END_IF_LENGTH);
+    }
+}
+
+static void frHandleDefine(int from, int length, FileReader* fr) {
+    if(defineIndex >= DEFINES) {
+        eInitError(error, fr->line, "too much defines in '%s'", fr->path);
+        return;
+    }
+    String s;
+    int end = frReadWord(s, from + length, fr);
+    if(frIsDefined(s)) {
+        puts("already defined");
+        return;
+    }
+    strncpy(defines[defineIndex].name, s, STRING_LENGTH);
+    defineIndex++;
+    printf("defined '%s'\n", s);
+    memset(fr->data + from, ' ', end - from);
+}
+
+static void frParseInstruction(int from, FileReader* fr) {
+    String s;
+    int length = frReadUntil(from, ' ', s, fr);
+    if(frStringCompare(s, "#include")) {
+        frHandleInclude(from, length, fr);
+    } else if(frStringCompare(s, "#ifndef")) {
+        frHandleIfNotDefined(from, length, fr);
+    } else if(frStringCompare(s, "#define")) {
+        frHandleDefine(from, length, fr);
+    } else {
+        eInitError(error, fr->line, "unknown preprocessor token '%s' in '%s'",
+                   s, fr->path);
+    }
+}
+
+static void frSearchInstruction(FileReader* fr) {
+    if(fr->data == NULL) {
+        return;
+    }
+    for(int i = 0; i < fr->length && !frHasError(); i++) {
+        unsigned char c = fr->data[i];
+        if(c == '#') {
+            frParseInstruction(i, fr);
+            i--;
+        } else if(c == '\n') {
+            fr->line++;
+        }
+    }
+}
+
+void frInit(const char* path, FileReader* fr, Error* e) {
+    defineIndex = 0;
+    error = e;
+    eInitSuccess(error);
+    frInitFileReader(fr, path);
+    frFullReadFile(fr);
+    frSearchInstruction(fr);
+    if(fr->data != NULL) {
+        printf((char*)fr->data);
+    }
 }
 
 void frDelete(FileReader* fr) {
     free(fr->data);
     fr->data = NULL;
+    fr->path = NULL;
     fr->length = 0;
     fr->readIndex = 0;
 }

+ 3 - 1
tokenizer/FileReader.h

@@ -8,9 +8,11 @@ extern "C" {
 #include "Error.h"
 
 typedef struct FileReader {
+    unsigned char* data;
+    const char* path;
     int length;
     int readIndex;
-    unsigned char* data;
+    int line;
 } FileReader;
 
 void frInit(const char* path, FileReader* fr, Error* e);

+ 6 - 1
tokenizer/Tokenizer.c

@@ -1,3 +1,4 @@
+#include <ctype.h>
 #include <limits.h>
 #include <setjmp.h>
 #include <stdarg.h>
@@ -304,7 +305,11 @@ static void tParseToken(int c) {
         case '[': tAddToken(T_OPEN_SQUARE_BRACKET); return;
         case ']': tAddToken(T_CLOSE_SQUARE_BRACKET); return;
     }
-    tError("unknown character on line %d: %c", line, c);
+    if(isprint(c)) {
+        tError("unknown character on line %d: %c", line, c);
+    } else {
+        tError("unknown character on line %d: %d", line, (int)c);
+    }
 }
 
 static void tParseFile() {