]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - lexer.c
Update doc/specification.tex
[xonotic/gmqcc.git] / lexer.c
diff --git a/lexer.c b/lexer.c
index 3660190946f4b4bfd5b273a0e7a71395e78a17b2..42b21a1a6cd7de46bd45aa21b227cc412a4fbdec 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012
+ * Copyright (C) 2012, 2013
  *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
@@ -47,7 +47,10 @@ static const char *keywords_fg[] = {
     "switch", "case", "default",
     "struct", "union",
     "break", "continue",
-    "typedef"
+    "typedef",
+    "goto",
+
+    "__builtin_debug_printtype"
 };
 static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]);
 
@@ -71,20 +74,17 @@ void lexerror(lex_file *lex, const char *fmt, ...)
 
 bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
 {
+    bool    r;
+    lex_ctx ctx;
     va_list ap;
-    int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
 
-    if (opts_werror)
-        lvl = LVL_ERROR;
+    ctx.file = lex->name;
+    ctx.line = lex->sline;
 
     va_start(ap, fmt);
-    con_vprintmsg(lvl, lex->name, lex->sline, "warning", fmt, ap);
+    r = vcompile_warning(ctx, warntype, fmt, ap);
     va_end(ap);
-
-    return opts_werror;
+    return r;
 }
 
 
@@ -184,7 +184,7 @@ static void lex_token_new(lex_file *lex)
 lex_file* lex_open(const char *file)
 {
     lex_file *lex;
-    FILE *in = util_fopen(file, "rb");
+    FILE *in = file_open(file, "rb");
 
     if (!in) {
         lexerror(NULL, "open failed: '%s'\n", file);
@@ -193,7 +193,7 @@ lex_file* lex_open(const char *file)
 
     lex = (lex_file*)mem_a(sizeof(*lex));
     if (!lex) {
-        fclose(in);
+        file_close(in);
         lexerror(NULL, "out of memory\n");
         return NULL;
     }
@@ -258,7 +258,7 @@ void lex_close(lex_file *lex)
         vec_free(lex->modelname);
 
     if (lex->file)
-        fclose(lex->file);
+        file_close(lex->file);
 #if 0
     if (lex->tok)
         token_delete(lex->tok);
@@ -291,12 +291,16 @@ static int lex_try_trigraph(lex_file *lex, int old)
 {
     int c2, c3;
     c2 = lex_fgetc(lex);
+    if (!lex->push_line && c2 == '\n')
+        lex->line++;
     if (c2 != '?') {
         lex_ungetch(lex, c2);
         return old;
     }
 
     c3 = lex_fgetc(lex);
+    if (!lex->push_line && c3 == '\n')
+        lex->line++;
     switch (c3) {
         case '=': return '#';
         case '/': return '\\';
@@ -438,7 +442,7 @@ static bool lex_try_pragma(lex_file *lex)
         goto unroll;
     }
 
-    for (ch = lex_getch(lex); vec_size(param) < 32 && ch != ')' && ch != '\n'; ch = lex_getch(lex))
+    for (ch = lex_getch(lex); vec_size(param) < 1024 && ch != ')' && ch != '\n'; ch = lex_getch(lex))
         vec_push(param, ch);
     vec_push(param, 0);
 
@@ -485,23 +489,25 @@ unroll:
     if (command) {
         vec_pop(command);
         while (vec_size(command)) {
-            lex_ungetch(lex, vec_last(command));
+            lex_ungetch(lex, (unsigned char)vec_last(command));
             vec_pop(command);
         }
         vec_free(command);
+        lex_ungetch(lex, ' ');
     }
     if (command) {
         vec_pop(command);
         while (vec_size(command)) {
-            lex_ungetch(lex, vec_last(command));
+            lex_ungetch(lex, (unsigned char)vec_last(command));
             vec_pop(command);
         }
         vec_free(command);
+        lex_ungetch(lex, ' ');
     }
     if (pragma) {
         vec_pop(pragma);
         while (vec_size(pragma)) {
-            lex_ungetch(lex, vec_last(pragma));
+            lex_ungetch(lex, (unsigned char)vec_last(pragma));
             vec_pop(pragma);
         }
         vec_free(pragma);
@@ -544,10 +550,10 @@ printf(   "line one\n"
  *    here is to store the line of the first character after skipping
  *    the initial whitespace in lex->sline, this happens in lex_do.
  */
-static int lex_skipwhite(lex_file *lex)
+static int lex_skipwhite(lex_file *lex, bool hadwhite)
 {
     int ch = 0;
-    bool haswhite = false;
+    bool haswhite = hadwhite;
 
     do
     {
@@ -734,11 +740,18 @@ static bool lex_finish_frames(lex_file *lex)
         vec_shrinkto(lex->tok.value, 0);
         vec_push(lex->frames, m);
     } while (true);
+
+    return false;
 }
 
 static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
 {
+    uchar_t chr;
     int ch = 0;
+    int nextch;
+    bool hex;
+    char u8buf[8]; /* way more than enough */
+    int  u8len, uc;
 
     while (ch != EOF)
     {
@@ -775,7 +788,100 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
             case 't':  ch = '\t'; break;
             case 'f':  ch = '\f'; break;
             case 'v':  ch = '\v'; break;
+            case 'x':
+            case 'X':
+                /* same procedure as in fteqcc */
+                ch = 0;
+                nextch = lex_getch(lex);
+                if      (nextch >= '0' && nextch <= '9')
+                    ch += nextch - '0';
+                else if (nextch >= 'a' && nextch <= 'f')
+                    ch += nextch - 'a' + 10;
+                else if (nextch >= 'A' && nextch <= 'F')
+                    ch += nextch - 'A' + 10;
+                else {
+                    lexerror(lex, "bad character code");
+                    lex_ungetch(lex, nextch);
+                    return (lex->tok.ttype = TOKEN_ERROR);
+                }
+
+                ch *= 0x10;
+                nextch = lex_getch(lex);
+                if      (nextch >= '0' && nextch <= '9')
+                    ch += nextch - '0';
+                else if (nextch >= 'a' && nextch <= 'f')
+                    ch += nextch - 'a' + 10;
+                else if (nextch >= 'A' && nextch <= 'F')
+                    ch += nextch - 'A' + 10;
+                else {
+                    lexerror(lex, "bad character code");
+                    lex_ungetch(lex, nextch);
+                    return (lex->tok.ttype = TOKEN_ERROR);
+                }
+                break;
+
+            /* fteqcc support */
+            case '0': case '1': case '2': case '3':
+            case '4': case '5': case '6': case '7':
+            case '8': case '9':
+                ch = 18 + ch - '0';
+                break;
+            case '<':  ch = 29; break;
+            case '-':  ch = 30; break;
+            case '>':  ch = 31; break;
+            case '[':  ch = 16; break;
+            case ']':  ch = 17; break;
+            case '{':
+                chr = 0;
+                nextch = lex_getch(lex);
+                hex = (nextch == 'x');
+                if (!hex)
+                    lex_ungetch(lex, nextch);
+                for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) {
+                    if (!hex) {
+                        if (nextch >= '0' && nextch <= '9')
+                            chr = chr * 10 + nextch - '0';
+                        else {
+                            lexerror(lex, "bad character code");
+                            return (lex->tok.ttype = TOKEN_ERROR);
+                        }
+                    } else {
+                        if (nextch >= '0' && nextch <= '9')
+                            chr = chr * 0x10 + nextch - '0';
+                        else if (nextch >= 'a' && nextch <= 'f')
+                            chr = chr * 0x10 + nextch - 'a' + 10;
+                        else if (nextch >= 'A' && nextch <= 'F')
+                            chr = chr * 0x10 + nextch - 'A' + 10;
+                        else {
+                            lexerror(lex, "bad character code");
+                            return (lex->tok.ttype = TOKEN_ERROR);
+                        }
+                    }
+                    if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255))
+                    {
+                        lexerror(lex, "character code out of range");
+                        return (lex->tok.ttype = TOKEN_ERROR);
+                    }
+                }
+                if (OPTS_FLAG(UTF8) && chr >= 128) {
+                    u8len = u8_fromchar(chr, u8buf, sizeof(u8buf));
+                    if (!u8len)
+                        ch = 0;
+                    else {
+                        --u8len;
+                        for (uc = 0; uc < u8len; ++uc)
+                            lex_tokench(lex, u8buf[uc]);
+                        /* the last character will be inserted with the tokench() call
+                         * below the switch
+                         */
+                        ch = u8buf[uc];
+                    }
+                }
+                else
+                    ch = chr;
+                break;
             case '\n':  ch = '\n'; break;
+
             default:
                 lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
                 /* so we just add the character plus backslash no matter what it actually is */
@@ -799,7 +905,10 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
     int  ch = lastch;
 
     /* parse a number... */
-    lex->tok.ttype = TOKEN_INTCONST;
+    if (ch == '.')
+        lex->tok.ttype = TOKEN_FLOATCONST;
+    else
+        lex->tok.ttype = TOKEN_INTCONST;
 
     lex_tokench(lex, ch);
 
@@ -832,7 +941,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
         }
     }
     /* NOT else, '.' can come from above as well */
-    if (ch == '.' && !ishex)
+    if (lex->tok.ttype != TOKEN_FLOATCONST && ch == '.' && !ishex)
     {
         /* Allow floating comma in non-hex mode */
         lex->tok.ttype = TOKEN_FLOATCONST;
@@ -869,6 +978,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
 int lex_do(lex_file *lex)
 {
     int ch, nextch, thirdch;
+    bool hadwhite = false;
 
     lex_token_new(lex);
 #if 0
@@ -877,7 +987,8 @@ int lex_do(lex_file *lex)
 #endif
 
     while (true) {
-        ch = lex_skipwhite(lex);
+        ch = lex_skipwhite(lex, hadwhite);
+        hadwhite = true;
         if (!lex->flags.mergelines || ch != '\\')
             break;
         ch = lex_getch(lex);
@@ -908,7 +1019,7 @@ int lex_do(lex_file *lex)
     }
 
     /* modelgen / spiritgen commands */
-    if (ch == '$') {
+    if (ch == '$' && !lex->flags.preprocessing) {
         const char *v;
         size_t frame;
 
@@ -999,7 +1110,6 @@ int lex_do(lex_file *lex)
             if (rc < 0)
                 return (lex->tok.ttype = TOKEN_FATAL);
 
-            v = lex->tok.value;
             if (lex->modelname) {
                 frame_macro m;
                 m.value = lex->framevalue;
@@ -1054,6 +1164,15 @@ int lex_do(lex_file *lex)
     switch (ch)
     {
         case '[':
+            nextch = lex_getch(lex);
+            if (nextch == '[') {
+                lex_tokench(lex, ch);
+                lex_tokench(lex, nextch);
+                lex_endtoken(lex);
+                return (lex->tok.ttype = TOKEN_ATTRIBUTE_OPEN);
+            }
+            lex_ungetch(lex, nextch);
+            /* FALL THROUGH */
         case '(':
         case ':':
         case '?':
@@ -1063,11 +1182,23 @@ int lex_do(lex_file *lex)
                 return (lex->tok.ttype = ch);
             else
                 return (lex->tok.ttype = TOKEN_OPERATOR);
+
+        case ']':
+            if (lex->flags.noops) {
+                nextch = lex_getch(lex);
+                if (nextch == ']') {
+                    lex_tokench(lex, ch);
+                    lex_tokench(lex, nextch);
+                    lex_endtoken(lex);
+                    return (lex->tok.ttype = TOKEN_ATTRIBUTE_CLOSE);
+                }
+                lex_ungetch(lex, nextch);
+            }
+            /* FALL THROUGH */
         case ')':
         case ';':
         case '{':
         case '}':
-        case ']':
 
         case '#':
             lex_tokench(lex, ch);
@@ -1077,6 +1208,18 @@ int lex_do(lex_file *lex)
             break;
     }
 
+    if (ch == '.') {
+        nextch = lex_getch(lex);
+        /* digits starting with a dot */
+        if (isdigit(nextch)) {
+            lex_ungetch(lex, nextch);
+            lex->tok.ttype = lex_finish_digit(lex, ch);
+            lex_endtoken(lex);
+            return lex->tok.ttype;
+        }
+        lex_ungetch(lex, nextch);
+    }
+
     if (lex->flags.noops)
     {
         /* Detect characters early which are normally
@@ -1105,31 +1248,37 @@ int lex_do(lex_file *lex)
             default:
                 break;
         }
+    }
 
-        if (ch == '.')
-        {
-            lex_tokench(lex, ch);
-            /* peak ahead once */
-            nextch = lex_getch(lex);
-            if (nextch != '.') {
-                lex_ungetch(lex, nextch);
-                lex_endtoken(lex);
-                return (lex->tok.ttype = ch);
-            }
-            /* peak ahead again */
-            nextch = lex_getch(lex);
-            if (nextch != '.') {
-                lex_ungetch(lex, nextch);
-                lex_ungetch(lex, '.');
-                lex_endtoken(lex);
+    if (ch == '.')
+    {
+        lex_tokench(lex, ch);
+        /* peak ahead once */
+        nextch = lex_getch(lex);
+        if (nextch != '.') {
+            lex_ungetch(lex, nextch);
+            lex_endtoken(lex);
+            if (lex->flags.noops)
                 return (lex->tok.ttype = ch);
-            }
-            /* fill the token to be "..." */
-            lex_tokench(lex, ch);
-            lex_tokench(lex, ch);
+            else
+                return (lex->tok.ttype = TOKEN_OPERATOR);
+        }
+        /* peak ahead again */
+        nextch = lex_getch(lex);
+        if (nextch != '.') {
+            lex_ungetch(lex, nextch);
+            lex_ungetch(lex, '.');
             lex_endtoken(lex);
-            return (lex->tok.ttype = TOKEN_DOTS);
+            if (lex->flags.noops)
+                return (lex->tok.ttype = ch);
+            else
+                return (lex->tok.ttype = TOKEN_OPERATOR);
         }
+        /* fill the token to be "..." */
+        lex_tokench(lex, ch);
+        lex_tokench(lex, ch);
+        lex_endtoken(lex);
+        return (lex->tok.ttype = TOKEN_DOTS);
     }
 
     if (ch == ',' || ch == '.') {
@@ -1139,14 +1288,15 @@ int lex_do(lex_file *lex)
     }
 
     if (ch == '+' || ch == '-' || /* ++, --, +=, -=  and -> as well! */
-        ch == '>' || ch == '<' || /* <<, >>, <=, >= */
-        ch == '=' || ch == '!' || /* ==, != */
-        ch == '&' || ch == '|')   /* &&, ||, &=, |= */
-    {
+        ch == '>' || ch == '<' || /* <<, >>, <=, >=                  */
+        ch == '=' || ch == '!' || /* ==, !=                          */
+        ch == '&' || ch == '|' || /* &&, ||, &=, |=                  */
+        ch == '~'                 /* ~=, ~                           */
+    )  {
         lex_tokench(lex, ch);
 
         nextch = lex_getch(lex);
-        if (nextch == ch || nextch == '=') {
+        if (nextch == '=' || (nextch == ch && ch != '!')) {
             lex_tokench(lex, nextch);
         } else if (ch == '-' && nextch == '>') {
             lex_tokench(lex, nextch);
@@ -1160,6 +1310,17 @@ int lex_do(lex_file *lex)
                 lex_tokench(lex, nextch);
                 lex_tokench(lex, thirdch);
             }
+        }
+        else if (lex->flags.preprocessing &&
+                 ch == '-' && isdigit(nextch))
+        {
+            lex->tok.ttype = lex_finish_digit(lex, nextch);
+            if (lex->tok.ttype == TOKEN_INTCONST)
+                lex->tok.constval.i = -lex->tok.constval.i;
+            else
+                lex->tok.constval.f = -lex->tok.constval.f;
+            lex_endtoken(lex);
+            return lex->tok.ttype;
         } else
             lex_ungetch(lex, nextch);
 
@@ -1227,7 +1388,7 @@ int lex_do(lex_file *lex)
                 if (!strcmp(v, keywords_qc[kw]))
                     return (lex->tok.ttype = TOKEN_KEYWORD);
             }
-            if (opts_standard != COMPILER_QCC) {
+            if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
                 for (kw = 0; kw < num_keywords_fg; ++kw) {
                     if (!strcmp(v, keywords_fg[kw]))
                         return (lex->tok.ttype = TOKEN_KEYWORD);
@@ -1249,7 +1410,7 @@ int lex_do(lex_file *lex)
         while (!lex->flags.preprocessing && lex->tok.ttype == TOKEN_STRINGCONST)
         {
             /* Allow c style "string" "continuation" */
-            ch = lex_skipwhite(lex);
+            ch = lex_skipwhite(lex, false);
             if (ch != '"') {
                 lex_ungetch(lex, ch);
                 break;
@@ -1276,8 +1437,9 @@ int lex_do(lex_file *lex)
             lex_tokench(lex, ch);
         lex_endtoken(lex);
 
+        lex->tok.ttype = TOKEN_CHARCONST;
          /* It's a vector if we can successfully scan 3 floats */
-#ifdef WIN32
+#ifdef _MSC_VER
         if (sscanf_s(lex->tok.value, " %f %f %f ",
                    &lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
 #else
@@ -1288,6 +1450,24 @@ int lex_do(lex_file *lex)
         {
              lex->tok.ttype = TOKEN_VECTORCONST;
         }
+        else
+        {
+            if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) {
+                uchar_t u8char;
+                /* check for a valid utf8 character */
+                if (!OPTS_FLAG(UTF8) || !u8_analyze(lex->tok.value, NULL, NULL, &u8char, 8)) {
+                    if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER,
+                                ( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`"
+                                                  : "multibyte character: `%s`" ),
+                                lex->tok.value))
+                        return (lex->tok.ttype = TOKEN_ERROR);
+                }
+                else
+                    lex->tok.constval.i = u8char;
+            }
+            else
+                lex->tok.constval.i = lex->tok.value[0];
+        }
 
         return lex->tok.ttype;
     }
@@ -1299,6 +1479,12 @@ int lex_do(lex_file *lex)
         return lex->tok.ttype;
     }
 
-    lexerror(lex, "unknown token");
+    if (lex->flags.preprocessing) {
+        lex_tokench(lex, ch);
+        lex_endtoken(lex);
+        return (lex->tok.ttype = ch);
+    }
+
+    lexerror(lex, "unknown token: `%s`", lex->tok.value);
     return (lex->tok.ttype = TOKEN_ERROR);
 }