]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - lex.c
Fixes to README
[xonotic/gmqcc.git] / lex.c
diff --git a/lex.c b/lex.c
index 9d15301d8d0070da7aec062df4af6e2b5864f83b..5673701157a7aaf1b47fd043d821054dcaa0e488 100644 (file)
--- a/lex.c
+++ b/lex.c
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
 #include "gmqcc.h"
 
+/*
+ * Keywords are multichar, punctuation lexing is a bit more complicated
+ * than keyword lexing.
+ */
 static const char *const lex_keywords[] = {
        "do",    "else",     "if",     "while",
        "break", "continue", "return", "goto",
-       "for",
-       
-       /* types */
-       "int",
-       "bool",
-       "void",
-       "string",
-       "float",
-       "vector",
-       "entity",
+       "for",   "typedef"
 };
 
 struct lex_file *lex_open(FILE *fp) {
        struct lex_file *lex = mem_a(sizeof(struct lex_file));
-       if (lex) {
-               lex->file = fp;
-               fseek(lex->file, 0, SEEK_END);
-               lex->length = ftell(lex->file);
-               lex->size   = lex->length; /* copy, this is never changed */
-               fseek(lex->file, 0, SEEK_SET);
-               lex->last = 0;
+       if (!lex || !fp)
+               return NULL;
                
-               memset(lex->peek, 0, sizeof(lex->peek));
-       }
+       lex->file = fp;
+       fseek(lex->file, 0, SEEK_END);
+       lex->length = ftell(lex->file);
+       lex->size   = lex->length; /* copy, this is never changed */
+       fseek(lex->file, 0, SEEK_SET);
+       lex->last = 0;
+       lex->line = 0;
+       
+       memset(lex->peek, 0, sizeof(lex->peek));
        return lex;
 }
 
-int lex_close(struct lex_file *file) {
-       int ret = -1;
-       if (file) {
-               ret = fclose(file->file);
-               mem_d(file);
-       }
-       return ret;
+void lex_close(struct lex_file *file) {
+       if (!file) return;
+       
+       fclose(file->file); /* may already be closed */
+       mem_d (file);
 }
 
 static void lex_addch(int ch, struct lex_file *file) {
@@ -144,10 +134,21 @@ static int lex_digraph(struct lex_file *file, int first) {
 
 static int lex_getch(struct lex_file *file) {
        int ch = lex_inget(file);
-       if (ch == '?')
-               return lex_trigraph(file);
-       if (ch == '<' || ch == ':' || ch == '%')
-               return lex_digraph (file, ch);
+
+       static int str = 0;
+       switch (ch) {
+               case '?' :
+                       return lex_trigraph(file);
+               case '<' :
+               case ':' :
+               case '%' :
+               case '"' : str = !str; if (str) { file->line ++; }
+                       return lex_digraph(file, ch);
+                       
+               case '\n':
+                       if (!str)
+                               file->line++;
+       }
                
        return ch;
 }
@@ -156,14 +157,13 @@ static int lex_get(struct lex_file *file) {
        int ch;
        if (!isspace(ch = lex_getch(file)))
                return ch;
-       
+               
        /* skip over all spaces */
        while (isspace(ch) && ch != '\n')
                ch = lex_getch(file);
                
        if (ch == '\n')
                return ch;
-               
        lex_unget(ch, file);
        return ' ';
 }
@@ -244,7 +244,7 @@ static int lex_skipcmt(struct lex_file *file) {
                lex_addch(ch, file);
                while ((ch = lex_getch(file)) != '*') {
                        if (ch == EOF)
-                               return error(ERROR_LEX, "malformatted comment", " ");
+                               return error(file, ERROR_LEX, "malformatted comment");
                        else
                                lex_addch(ch, file);
                }
@@ -265,7 +265,8 @@ static int lex_getsource(struct lex_file *file) {
                case '\'': return lex_skipchr(file);
                case '"':  return lex_skipstr(file);
                case '/':  return lex_skipcmt(file);
-               default:   return ch;
+               default:
+                       return ch;
        }
 }
 
@@ -276,7 +277,14 @@ int lex_token(struct lex_file *file) {
        /* valid identifier */
        if (ch > 0 && (ch == '_' || isalpha(ch))) {
                lex_clear(file);
-               while (ch > 0 && (isalpha(ch) || ch == '_')) {
+               
+               /*
+                * Yes this is dirty, but there is no other _sane_ easy
+                * way to do it, this is what I call defensive programming
+                * if something breaks, add more defense :-)
+                */
+               while (ch >   0   && ch != ' ' && ch != '(' &&
+                      ch != '\n' && ch != ';' && ch != ')') {
                        lex_addch(ch, file);
                        ch = lex_getsource(file);
                }
@@ -288,6 +296,28 @@ int lex_token(struct lex_file *file) {
                        if (!strncmp(file->lastok, lex_keywords[it], sizeof(lex_keywords[it])))
                                return it;
                                
+               /* try a type? */
+               #define TEST_TYPE(X)                                 \
+                   do {                                             \
+                       if (!strncmp(X, "float",  sizeof("float")))  \
+                           return TOKEN_FLOAT;                      \
+                       if (!strncmp(X, "vector", sizeof("vector"))) \
+                           return TOKEN_VECTOR;                     \
+                       if (!strncmp(X, "string", sizeof("string"))) \
+                           return TOKEN_STRING;                     \
+                       if (!strncmp(X, "entity", sizeof("entity"))) \
+                           return TOKEN_ENTITY;                     \
+                       if (!strncmp(X, "void"  , sizeof("void")))   \
+                           return TOKEN_VOID;                       \
+                   } while(0)
+               
+               TEST_TYPE(file->lastok);
+               
+               /* try the hashtable for typedefs? */
+               if (typedef_find(file->lastok))
+                       TEST_TYPE(typedef_find(file->lastok)->name);
+                       
+               #undef TEST_TYPE
                return LEX_IDENT;
        }
        return ch;
@@ -303,52 +333,23 @@ void lex_reset(struct lex_file *file) {
        memset(file->lastok, 0, sizeof(file->lastok));
 }
 
-int lex_debug(struct lex_file *file) {
-       int list_do       = 0;
-       int list_else     = 0;
-       int list_if       = 0;
-       int list_while    = 0;
-       int list_break    = 0;
-       int list_continue = 0;
-       int list_return   = 0;
-       int list_goto     = 0;
-       int list_for      = 0;
-       int token         = 0;
-       printf("===========================\nTOKENS:   \n===========================\n");
-       while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0) {
-               if (token != -1) {
-                       switch (token) {
-                               case 0: list_do      ++; break;
-                               case 1: list_else    ++; break;
-                               case 2: list_if      ++; break;
-                               case 3: list_while   ++; break;
-                               case 4: list_break   ++; break;
-                               case 5: list_continue++; break;
-                               case 6: list_return  ++; break;
-                               case 7: list_goto    ++; break;
-                               case 8: list_for     ++; break;
-                       }
-               }
-               if (token >= 33 && token <= 126)
-                       putchar(token);
+/*
+ * Include a file into the lexer / parsing process:  This really
+ * should check if names are the same to prevent endless include
+ * recrusion.
+ */
+struct lex_file *lex_include(struct lex_file *lex, char *file) {
+       util_strrq(file);
+       if (strncmp(lex->name, file, strlen(lex->name)) == 0) {
+               error(lex, ERROR_LEX, "Source file cannot include itself\n");
+               exit (-1);
        }
-       printf("\n===========================\nBRANCHES \n===========================\n");
-       printf("\t if       % 8d\n", list_if);
-       printf("\t else     % 8d\n", list_else);
-       printf("===========================\nLOOPS      \n===========================\n");
-       printf("\t for      % 8d\n", list_for);
-       printf("\t while    % 8d\n", list_while);
-       printf("\t do       % 8d\n", list_do);
-       printf("===========================\nSTATEMENTS \n===========================\n");
-       printf("\t break    % 8d\n", list_break);
-       printf("\t continue % 8d\n", list_continue);
-       printf("\t return   % 8d\n", list_return);
-       printf("\t goto     % 8d\n", list_goto);
-       printf("===========================\nIDENTIFIERS\n===========================\n");
-       lex_reset(file);
-       while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0)
-               if (token == LEX_IDENT)
-                       printf("%s ", file->lastok);
-       lex_reset(file);
-       return 1;
+       
+       FILE *fp = fopen(file, "r");
+       if  (!fp) {
+               error(lex, ERROR_LEX, "Include file `%s` doesn't exist\n", file);
+               exit (-1);
+       }
+       
+       return lex_open(fp);
 }