]> git.sur5r.net Git - cc65/blobdiff - src/da65/scanner.c
Support for preprocessing info file via cpp or m4.
[cc65] / src / da65 / scanner.c
index 8dc8d393a77fb389fa86fa87828b56ef938a4525..567b513485d69d8acd8a8be4886ab8bf350e241b 100644 (file)
@@ -41,6 +41,8 @@
 /* common */
 #include "chartype.h"
 #include "xsprintf.h"
+#include "xmalloc.h"
+#include "strbuf.h"
 
 /* ld65 */
 #include "global.h"
@@ -72,7 +74,10 @@ static int              C               = ' ';
 static unsigned         InputLine       = 1;
 static unsigned         InputCol        = 0;
 static FILE*            InputFile       = 0;
+static char*            InputSrcName    = 0;
 
+/* Options */
+unsigned char           InfoSyncLines   = 0;
 
 
 /*****************************************************************************/
@@ -91,7 +96,8 @@ void InfoWarning (const char* Format, ...)
     xvsprintf (Buf, sizeof (Buf), Format, ap);
     va_end (ap);
 
-    Warning ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
+    fprintf (stderr, "%s(%u): Warning: %s\n",
+            InputSrcName, InfoErrorLine, Buf);
 }
 
 
@@ -106,7 +112,9 @@ void InfoError (const char* Format, ...)
     xvsprintf (Buf, sizeof (Buf), Format, ap);
     va_end (ap);
 
-    Error ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
+    fprintf (stderr, "%s(%u): Error: %s\n",
+            InputSrcName, InfoErrorLine, Buf);
+    exit (EXIT_FAILURE);
 }
 
 
@@ -149,17 +157,171 @@ static unsigned DigitVal (int C)
 
 
 
+static void SkipBlanks (int SingleLine)
+{
+    while (C != EOF && (!SingleLine || C != '\n') && IsSpace (C)) {
+        NextChar ();
+    }
+}
+
+static long GetDecimalToken ()
+{
+    long Value = 0;
+
+    while (C != EOF && IsDigit (C)) {
+        Value = Value * 10 + DigitVal (C);
+        NextChar ();
+    }
+    return Value;
+}
+
+static int GetEncodedChar (char *Buf, unsigned *IPtr, unsigned Size)
+{
+    char Decoded = 0;
+    int Count;
+
+    if (C == EOF) {
+        return -1;
+    } else if (C != '\\') {
+        Decoded = C;
+        NextChar ();
+        goto Store;
+    }
+    NextChar (); /* consume '\\' */
+    if (C == EOF) {
+        return -1;
+    } else if (IsODigit (C)) {
+        Count = 3;
+        do {
+            Decoded = Decoded * 8 + DigitVal (C);
+            NextChar ();
+            --Count;
+        } while (Count > 0 && C != EOF && IsODigit (C));
+    } else if (C == 'x') {
+        NextChar (); /* consume 'x' */
+        Count = 2;
+        while (Count > 0 && C != EOF && IsXDigit (C)) {
+            Decoded = Decoded * 16 + DigitVal (C);
+            NextChar ();
+            --Count;
+        }
+    } else {
+        switch (C) {
+            case '"': case '\'': case '\\':
+                        Decoded = C;       break;
+            case 't':  Decoded = '\t';     break;
+            case 'r':  Decoded = '\r';     break;
+            case 'n':  Decoded = '\n';     break;
+            default:   return -1;
+        }
+        NextChar ();
+    }
+Store:
+    if (*IPtr < Size - 1) {
+        Buf [(*IPtr)++] = Decoded;
+    }
+    Buf [*IPtr] = 0;
+    return 0;
+}
+
+static void LineMarkerOrComment ()
+/* Handle a line beginning with '#'. Possible interpretations are:
+ * - #line <lineno> ["<filename>"]         (C preprocessor input)
+ * - # <lineno> "<filename>" [<flag>]...    (gcc preprocessor output)
+ * - #<comment>
+ */
+{
+    unsigned long LineNo = 0;
+    int LineDirective = 0;
+    StrBuf SrcNameBuf = AUTO_STRBUF_INITIALIZER;
+    
+    /* Skip the first "# " */
+    NextChar ();
+    SkipBlanks (1);
+
+    /* Check "line" */
+    if (C == 'l') {
+        char MaybeLine [6];
+        unsigned I;
+        for (I = 0; I < sizeof MaybeLine - 1 && C != EOF && IsAlNum (C); ++I) {
+            MaybeLine [I] = C;
+            NextChar ();
+        }
+        MaybeLine [I] = 0;
+        if (strcmp (MaybeLine, "line") != 0) {
+            goto NotMarker;
+        }
+        LineDirective = 1;
+        SkipBlanks (1);
+    }
+
+    /* Get line number */
+    if (C == EOF || !IsDigit (C)) {
+        goto NotMarker;
+    }
+    LineNo = GetDecimalToken ();
+    SkipBlanks (1);
+
+    /* Get the source file name */
+    if (C != '\"') {
+        /* The source file name is missing */
+        if (LineDirective && C == '\n') {
+            /* got #line <lineno> */
+            NextChar ();
+            InputLine = LineNo;
+            goto Last;
+        } else {
+            goto NotMarker;
+        }
+    }
+    NextChar ();
+    while (C != EOF && C != '\n' && C != '\"') {
+        char DecodeBuf [2];
+        unsigned I = 0;
+        if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0) {
+            goto BadMarker;
+        }
+        SB_AppendBuf (&SrcNameBuf, DecodeBuf, I);
+    }
+    if (C != '\"') {
+        goto BadMarker;
+    }
+    NextChar ();
+
+    /* Ignore until the end of line */
+    while (C != EOF && C != '\n') {
+        NextChar ();
+    }
+
+    /* Accepted a line marker */
+    SB_Terminate (&SrcNameBuf);
+    xfree (InputSrcName);
+    InputSrcName = SB_GetBuf (&SrcNameBuf);
+    SB_Init (&SrcNameBuf);
+    InputLine = (unsigned)LineNo;
+    NextChar ();
+    goto Last;
+
+BadMarker:
+    InfoWarning ("Bad line marker");
+NotMarker:
+    while (C != EOF && C != '\n') {
+        NextChar ();
+    }
+    NextChar ();
+Last:
+    SB_Done (&SrcNameBuf);
+}
+
 void InfoNextTok (void)
 /* Read the next token from the input stream */
 {
     unsigned I;
-    int      Esc;
+    char DecodeBuf [2];
 
 Again:
     /* Skip whitespace */
-    while (IsSpace (C)) {
-        NextChar ();
-    }
+    SkipBlanks (0);
 
     /* Remember the current position */
     InfoErrorLine = InputLine;
@@ -198,11 +360,7 @@ Again:
 
     /* Decimal number? */
     if (IsDigit (C)) {
-        InfoIVal = 0;
-        while (IsDigit (C)) {
-            InfoIVal = InfoIVal * 10 + DigitVal (C);
-            NextChar ();
-        }
+        InfoIVal = GetDecimalToken ();
         InfoTok = INFOTOK_INTCON;
         return;
     }
@@ -248,38 +406,31 @@ Again:
         case '\"':
             NextChar ();
             I = 0;
-            while (C != '\"') {
-                Esc = (C == '\\');
-                if (Esc) {
-                    NextChar ();
-                }
-                if (C == EOF || C == '\n') {
-                    InfoError ("Unterminated string");
-                }
-                if (Esc) {
-                    switch (C) {
-                        case '\"':      C = '\"';       break;
-                        case '\'':      C = '\'';       break;
-                        default:        InfoError ("Invalid escape char: %c", C);
+            while (C != EOF && C != '\"') {
+                if (GetEncodedChar (InfoSVal, &I, sizeof InfoSVal) < 0) {
+                    if (C == EOF) {
+                        InfoError ("Unterminated string");
+                    } else  {
+                        InfoError ("Invalid escape char: %c", C);
                     }
                 }
-                if (I < CFG_MAX_IDENT_LEN) {
-                    InfoSVal [I++] = C;
-                }
-                NextChar ();
             }
-            NextChar ();
-            InfoSVal [I] = '\0';
+            if (C != '\"') {
+                InfoError ("Unterminated string");
+            }
+           NextChar ();
             InfoTok = INFOTOK_STRCON;
             break;
 
         case '\'':
             NextChar ();
-            if (C == EOF || IsControl (C)) {
+            if (C == EOF || IsControl (C) || C == '\'') {
                 InfoError ("Invalid character constant");
             }
-            InfoIVal = C;
-            NextChar ();
+            if (GetEncodedChar (DecodeBuf, &I, sizeof DecodeBuf) < 0 || I != 1) {
+                InfoError ("Invalid character constant");
+            }
+            InfoIVal = DecodeBuf [0];
             if (C != '\'') {
                 InfoError ("Unterminated character constant");
             }
@@ -288,8 +439,13 @@ Again:
             break;
 
         case '#':
-            /* Comment */
-            while (C != '\n' && C != EOF) {
+            /* # lineno "sourcefile" or # comment */
+            if (InfoSyncLines && InputCol == 1) {
+                LineMarkerOrComment ();
+            } else {
+                do {
+                    NextChar ();
+                } while (C != EOF && C != '\n');
                 NextChar ();
             }
             if (C != EOF) {
@@ -298,6 +454,21 @@ Again:
             InfoTok = INFOTOK_EOF;
             break;
 
+        case '/':
+            /* C++ style comment */
+            NextChar ();
+            if (C != '/') {
+                InfoError ("Invalid token `/'");
+            }
+            do {
+                NextChar ();
+            } while (C != '\n' && C != EOF);
+            if (C != EOF) {
+                goto Again;
+            }
+            InfoTok = INFOTOK_EOF;
+            break;
+
         case EOF:
             InfoTok = INFOTOK_EOF;
             break;
@@ -484,15 +655,19 @@ void InfoSetName (const char* Name)
 /* Set a name for a config file */
 {
     InfoFile = Name;
+    xfree(InputSrcName);
+    InputSrcName = xstrdup(Name);
 }
 
 
 
+#ifdef unused
 const char* InfoGetName (void)
 /* Get the name of the config file */
 {
     return InfoFile? InfoFile : "";
 }
+#endif /* unused */