/*****************************************************************************/
/* */
-/* scanner.c */
+/* scanner.c */
/* */
-/* The scanner for the ca65 macroassembler */
+/* The scanner for the ca65 macroassembler */
/* */
/* */
/* */
-/* (C) 1998-2000 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
-/* EMail: uz@musoftware.de */
+/* (C) 1998-2013, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
#include <string.h>
#include <ctype.h>
#include <errno.h>
-#include <sys/types.h> /* EMX needs this */
-#include <sys/stat.h>
/* common */
+#include "addrsize.h"
+#include "attrib.h"
+#include "chartype.h"
#include "check.h"
+#include "filestat.h"
#include "fname.h"
#include "xmalloc.h"
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
-enum Token Tok = TOK_NONE; /* Current token */
-int WS; /* Flag: Whitespace before token */
-long IVal; /* Integer token attribute */
-char SVal [MAX_STR_LEN+1]; /* String token attribute */
+/* Current input token incl. attributes */
+Token CurTok = STATIC_TOKEN_INITIALIZER;
-FilePos CurPos = { 0, 0, 0 }; /* Name and position in current file */
+/* Struct to handle include files. */
+typedef struct InputFile InputFile;
+struct InputFile {
+ FILE* F; /* Input file descriptor */
+ FilePos Pos; /* Position in file */
+ token_t Tok; /* Last token */
+ int C; /* Last character */
+ StrBuf Line; /* The current input line */
+ int IncSearchPath; /* True if we've added a search path */
+ int BinSearchPath; /* True if we've added a search path */
+ InputFile* Next; /* Linked list of input files */
+};
+/* Struct to handle textual input data */
+typedef struct InputData InputData;
+struct InputData {
+ char* Text; /* Pointer to the text data */
+ const char* Pos; /* Pointer to current position */
+ int Malloced; /* Memory was malloced */
+ token_t Tok; /* Last token */
+ int C; /* Last character */
+ InputData* Next; /* Linked list of input data */
+};
+/* Input source: Either file or data */
+typedef struct CharSource CharSource;
-/* Struct to handle include files. Note: The length of the input line may
- * not exceed 255+1, since the column is stored in the file position struct
- * as a character. Increasing this value means changing the FilePos struct,
- * and the read and write routines in the assembler and linker.
- */
-typedef struct InputFile_ InputFile;
-struct InputFile_ {
- FILE* F; /* Input file descriptor */
- FilePos Pos; /* Position in file */
- enum Token Tok; /* Last token */
- int C; /* Last character */
- char Line[256]; /* The current input line */
- InputFile* Next; /* Linked list of input files */
+/* Set of input functions */
+typedef struct CharSourceFunctions CharSourceFunctions;
+struct CharSourceFunctions {
+ void (*MarkStart) (CharSource*); /* Mark the start pos of a token */
+ void (*NextChar) (CharSource*); /* Read next char from input */
+ void (*Done) (CharSource*); /* Close input source */
};
-/* Struct to handle textual input data */
-typedef struct InputData_ InputData;
-struct InputData_ {
- const char* Data; /* Pointer to the data */
- const char* Pos; /* Pointer to current position */
- int Malloced; /* Memory was malloced */
- enum Token Tok; /* Last token */
- int C; /* Last character */
- InputData* Next; /* Linked list of input data */
+/* Input source: Either file or data */
+struct CharSource {
+ CharSource* Next; /* Linked list of char sources */
+ token_t Tok; /* Last token */
+ int C; /* Last character */
+ const CharSourceFunctions* Func; /* Pointer to function table */
+ union {
+ InputFile File; /* File data */
+ InputData Data; /* Textual data */
+ } V;
};
/* Current input variables */
-static InputFile* IFile = 0; /* Current input file */
-static InputData* IData = 0; /* Current input memory data */
-static unsigned ICount = 0; /* Count of input files */
-static int C = 0; /* Current input character */
+static CharSource* Source = 0; /* Current char source */
+static unsigned FCount = 0; /* Count of input files */
+static int C = 0; /* Current input character */
/* Force end of assembly */
-int ForcedEnd = 0;
+int ForcedEnd = 0;
/* List of dot keywords with the corresponding tokens */
struct DotKeyword {
- const char* Key; /* MUST be first field */
- enum Token Tok;
+ const char* Key; /* MUST be first field */
+ token_t Tok;
} DotKeywords [] = {
- { "A16", TOK_A16 },
- { "A8", TOK_A8 },
- { "ADDR", TOK_ADDR },
- { "ALIGN", TOK_ALIGN },
- { "AND", TOK_BAND },
- { "ASCIIZ", TOK_ASCIIZ },
- { "AUTOIMPORT", TOK_AUTOIMPORT },
- { "BITAND", TOK_AND },
- { "BITNOT", TOK_NOT },
- { "BITOR", TOK_OR },
- { "BITXOR", TOK_XOR },
- { "BLANK", TOK_BLANK },
- { "BSS", TOK_BSS },
- { "BYT", TOK_BYTE },
- { "BYTE", TOK_BYTE },
- { "CASE", TOK_CASE },
- { "CODE", TOK_CODE },
- { "CONCAT", TOK_CONCAT },
- { "CONDES", TOK_CONDES },
- { "CONST", TOK_CONST },
- { "CONSTRUCTOR", TOK_CONSTRUCTOR },
- { "CPU", TOK_CPU },
- { "DATA", TOK_DATA },
- { "DBG", TOK_DBG },
- { "DBYT", TOK_DBYT },
- { "DEBUGINFO", TOK_DEBUGINFO },
- { "DEF", TOK_DEFINED },
- { "DEFINE", TOK_DEFINE },
- { "DEFINED", TOK_DEFINED },
- { "DESTRUCTOR", TOK_DESTRUCTOR },
- { "DWORD", TOK_DWORD },
- { "ELSE", TOK_ELSE },
- { "ELSEIF", TOK_ELSEIF },
- { "END", TOK_END },
- { "ENDIF", TOK_ENDIF },
- { "ENDMAC", TOK_ENDMACRO },
- { "ENDMACRO", TOK_ENDMACRO },
- { "ENDPROC", TOK_ENDPROC },
- { "ENDREP", TOK_ENDREP },
- { "ENDREPEAT", TOK_ENDREP },
- { "ERROR", TOK_ERROR },
- { "EXITMAC", TOK_EXITMACRO },
- { "EXITMACRO", TOK_EXITMACRO },
- { "EXPORT", TOK_EXPORT },
- { "EXPORTZP", TOK_EXPORTZP },
- { "FARADDR", TOK_FARADDR },
- { "FEATURE", TOK_FEATURE },
- { "FILEOPT", TOK_FILEOPT },
- { "FOPT", TOK_FILEOPT },
- { "FORCEWORD", TOK_FORCEWORD },
- { "GLOBAL", TOK_GLOBAL },
- { "GLOBALZP", TOK_GLOBALZP },
- { "I16", TOK_I16 },
- { "I8", TOK_I8 },
- { "IF", TOK_IF },
- { "IFBLANK", TOK_IFBLANK },
- { "IFCONST", TOK_IFCONST },
- { "IFDEF", TOK_IFDEF },
- { "IFNBLANK", TOK_IFNBLANK },
- { "IFNCONST", TOK_IFNCONST },
- { "IFNDEF", TOK_IFNDEF },
- { "IFNREF", TOK_IFNREF },
- { "IFP02", TOK_IFP02 },
- { "IFP816", TOK_IFP816 },
- { "IFPC02", TOK_IFPC02 },
- { "IFREF", TOK_IFREF },
- { "IMPORT", TOK_IMPORT },
- { "IMPORTZP", TOK_IMPORTZP },
- { "INCBIN", TOK_INCBIN },
- { "INCLUDE", TOK_INCLUDE },
- { "LEFT", TOK_LEFT },
- { "LINECONT", TOK_LINECONT },
- { "LIST", TOK_LIST },
- { "LISTBYTES", TOK_LISTBYTES },
- { "LOCAL", TOK_LOCAL },
- { "LOCALCHAR", TOK_LOCALCHAR },
- { "MAC", TOK_MACRO },
- { "MACPACK", TOK_MACPACK },
- { "MACRO", TOK_MACRO },
- { "MATCH", TOK_MATCH },
- { "MID", TOK_MID },
- { "MOD", TOK_MOD },
- { "NOT", TOK_BNOT },
- { "NULL", TOK_NULL },
- { "OR", TOK_BOR },
- { "ORG", TOK_ORG },
- { "OUT", TOK_OUT },
- { "P02", TOK_P02 },
- { "P816", TOK_P816 },
- { "PAGELEN", TOK_PAGELENGTH },
- { "PAGELENGTH", TOK_PAGELENGTH },
- { "PARAMCOUNT", TOK_PARAMCOUNT },
- { "PC02", TOK_PC02 },
- { "PROC", TOK_PROC },
- { "REF", TOK_REFERENCED },
- { "REFERENCED", TOK_REFERENCED },
- { "RELOC", TOK_RELOC },
- { "REPEAT", TOK_REPEAT },
- { "RES", TOK_RES },
- { "RIGHT", TOK_RIGHT },
- { "RODATA", TOK_RODATA },
- { "SEGMENT", TOK_SEGMENT },
- { "SHL", TOK_SHL },
- { "SHR", TOK_SHR },
- { "SMART", TOK_SMART },
- { "STRAT", TOK_STRAT },
- { "STRING", TOK_STRING },
- { "STRLEN", TOK_STRLEN },
- { "SUNPLUS", TOK_SUNPLUS },
- { "TCOUNT", TOK_TCOUNT },
- { "WARNING", TOK_WARNING },
- { "WORD", TOK_WORD },
- { "XMATCH", TOK_XMATCH },
- { "XOR", TOK_BXOR },
- { "ZEROPAGE", TOK_ZEROPAGE },
+ { ".A16", TOK_A16 },
+ { ".A8", TOK_A8 },
+ { ".ADDR", TOK_ADDR },
+ { ".ALIGN", TOK_ALIGN },
+ { ".AND", TOK_BOOLAND },
+ { ".ASCIIZ", TOK_ASCIIZ },
+ { ".ASSERT", TOK_ASSERT },
+ { ".AUTOIMPORT", TOK_AUTOIMPORT },
+ { ".BANK", TOK_BANK },
+ { ".BANKBYTE", TOK_BANKBYTE },
+ { ".BANKBYTES", TOK_BANKBYTES },
+ { ".BITAND", TOK_AND },
+ { ".BITNOT", TOK_NOT },
+ { ".BITOR", TOK_OR },
+ { ".BITXOR", TOK_XOR },
+ { ".BLANK", TOK_BLANK },
+ { ".BSS", TOK_BSS },
+ { ".BYT", TOK_BYTE },
+ { ".BYTE", TOK_BYTE },
+ { ".CASE", TOK_CASE },
+ { ".CHARMAP", TOK_CHARMAP },
+ { ".CODE", TOK_CODE },
+ { ".CONCAT", TOK_CONCAT },
+ { ".CONDES", TOK_CONDES },
+ { ".CONST", TOK_CONST },
+ { ".CONSTRUCTOR", TOK_CONSTRUCTOR },
+ { ".CPU", TOK_CPU },
+ { ".DATA", TOK_DATA },
+ { ".DBG", TOK_DBG },
+ { ".DBYT", TOK_DBYT },
+ { ".DEBUGINFO", TOK_DEBUGINFO },
+ { ".DEF", TOK_DEFINED },
+ { ".DEFINE", TOK_DEFINE },
+ { ".DEFINED", TOK_DEFINED },
+ { ".DELMAC", TOK_DELMAC },
+ { ".DELMACRO", TOK_DELMAC },
+ { ".DESTRUCTOR", TOK_DESTRUCTOR },
+ { ".DWORD", TOK_DWORD },
+ { ".ELSE", TOK_ELSE },
+ { ".ELSEIF", TOK_ELSEIF },
+ { ".END", TOK_END },
+ { ".ENDENUM", TOK_ENDENUM },
+ { ".ENDIF", TOK_ENDIF },
+ { ".ENDMAC", TOK_ENDMACRO },
+ { ".ENDMACRO", TOK_ENDMACRO },
+ { ".ENDPROC", TOK_ENDPROC },
+ { ".ENDREP", TOK_ENDREP },
+ { ".ENDREPEAT", TOK_ENDREP },
+ { ".ENDSCOPE", TOK_ENDSCOPE },
+ { ".ENDSTRUCT", TOK_ENDSTRUCT },
+ { ".ENDUNION", TOK_ENDUNION },
+ { ".ENUM", TOK_ENUM },
+ { ".ERROR", TOK_ERROR },
+ { ".EXITMAC", TOK_EXITMACRO },
+ { ".EXITMACRO", TOK_EXITMACRO },
+ { ".EXPORT", TOK_EXPORT },
+ { ".EXPORTZP", TOK_EXPORTZP },
+ { ".FARADDR", TOK_FARADDR },
+ { ".FATAL", TOK_FATAL },
+ { ".FEATURE", TOK_FEATURE },
+ { ".FILEOPT", TOK_FILEOPT },
+ { ".FOPT", TOK_FILEOPT },
+ { ".FORCEIMPORT", TOK_FORCEIMPORT },
+ { ".FORCEWORD", TOK_FORCEWORD },
+ { ".GLOBAL", TOK_GLOBAL },
+ { ".GLOBALZP", TOK_GLOBALZP },
+ { ".HIBYTE", TOK_HIBYTE },
+ { ".HIBYTES", TOK_HIBYTES },
+ { ".HIWORD", TOK_HIWORD },
+ { ".I16", TOK_I16 },
+ { ".I8", TOK_I8 },
+ { ".IDENT", TOK_MAKEIDENT },
+ { ".IF", TOK_IF },
+ { ".IFBLANK", TOK_IFBLANK },
+ { ".IFCONST", TOK_IFCONST },
+ { ".IFDEF", TOK_IFDEF },
+ { ".IFNBLANK", TOK_IFNBLANK },
+ { ".IFNCONST", TOK_IFNCONST },
+ { ".IFNDEF", TOK_IFNDEF },
+ { ".IFNREF", TOK_IFNREF },
+ { ".IFP02", TOK_IFP02 },
+ { ".IFP816", TOK_IFP816 },
+ { ".IFPC02", TOK_IFPC02 },
+ { ".IFPSC02", TOK_IFPSC02 },
+ { ".IFREF", TOK_IFREF },
+ { ".IMPORT", TOK_IMPORT },
+ { ".IMPORTZP", TOK_IMPORTZP },
+ { ".INCBIN", TOK_INCBIN },
+ { ".INCLUDE", TOK_INCLUDE },
+ { ".INTERRUPTOR", TOK_INTERRUPTOR },
+ { ".LEFT", TOK_LEFT },
+ { ".LINECONT", TOK_LINECONT },
+ { ".LIST", TOK_LIST },
+ { ".LISTBYTES", TOK_LISTBYTES },
+ { ".LOBYTE", TOK_LOBYTE },
+ { ".LOBYTES", TOK_LOBYTES },
+ { ".LOCAL", TOK_LOCAL },
+ { ".LOCALCHAR", TOK_LOCALCHAR },
+ { ".LOWORD", TOK_LOWORD },
+ { ".MAC", TOK_MACRO },
+ { ".MACPACK", TOK_MACPACK },
+ { ".MACRO", TOK_MACRO },
+ { ".MATCH", TOK_MATCH },
+ { ".MAX", TOK_MAX },
+ { ".MID", TOK_MID },
+ { ".MIN", TOK_MIN },
+ { ".MOD", TOK_MOD },
+ { ".NOT", TOK_BOOLNOT },
+ { ".NULL", TOK_NULL },
+ { ".OR", TOK_BOOLOR },
+ { ".ORG", TOK_ORG },
+ { ".OUT", TOK_OUT },
+ { ".P02", TOK_P02 },
+ { ".P816", TOK_P816 },
+ { ".PAGELEN", TOK_PAGELENGTH },
+ { ".PAGELENGTH", TOK_PAGELENGTH },
+ { ".PARAMCOUNT", TOK_PARAMCOUNT },
+ { ".PC02", TOK_PC02 },
+ { ".POPCPU", TOK_POPCPU },
+ { ".POPSEG", TOK_POPSEG },
+ { ".PROC", TOK_PROC },
+ { ".PSC02", TOK_PSC02 },
+ { ".PUSHCPU", TOK_PUSHCPU },
+ { ".PUSHSEG", TOK_PUSHSEG },
+ { ".REF", TOK_REFERENCED },
+ { ".REFERENCED", TOK_REFERENCED },
+ { ".RELOC", TOK_RELOC },
+ { ".REPEAT", TOK_REPEAT },
+ { ".RES", TOK_RES },
+ { ".RIGHT", TOK_RIGHT },
+ { ".RODATA", TOK_RODATA },
+ { ".SCOPE", TOK_SCOPE },
+ { ".SEGMENT", TOK_SEGMENT },
+ { ".SET", TOK_SET },
+ { ".SETCPU", TOK_SETCPU },
+ { ".SHL", TOK_SHL },
+ { ".SHR", TOK_SHR },
+ { ".SIZEOF", TOK_SIZEOF },
+ { ".SMART", TOK_SMART },
+ { ".SPRINTF", TOK_SPRINTF },
+ { ".STRAT", TOK_STRAT },
+ { ".STRING", TOK_STRING },
+ { ".STRLEN", TOK_STRLEN },
+ { ".STRUCT", TOK_STRUCT },
+ { ".TAG", TOK_TAG },
+ { ".TCOUNT", TOK_TCOUNT },
+ { ".TIME", TOK_TIME },
+ { ".UNDEF", TOK_UNDEF },
+ { ".UNDEFINE", TOK_UNDEF },
+ { ".UNION", TOK_UNION },
+ { ".VERSION", TOK_VERSION },
+ { ".WARNING", TOK_WARNING },
+ { ".WORD", TOK_WORD },
+ { ".XMATCH", TOK_XMATCH },
+ { ".XOR", TOK_BOOLXOR },
+ { ".ZEROPAGE", TOK_ZEROPAGE },
};
/*****************************************************************************/
-/* Forwards */
+/* CharSource functions */
/*****************************************************************************/
-static void NextChar (void);
-/* Read the next character from the input file */
+static void UseCharSource (CharSource* S)
+/* Initialize a new input source and start to use it. */
+{
+ /* Remember the current input char and token */
+ S->Tok = CurTok.Tok;
+ S->C = C;
+ /* Use the new input source */
+ S->Next = Source;
+ Source = S;
+ /* Read the first character from the new file */
+ S->Func->NextChar (S);
-/*****************************************************************************/
-/* Character classification functions */
-/*****************************************************************************/
+ /* Setup the next token so it will be skipped on the next call to
+ * NextRawTok().
+ */
+ CurTok.Tok = TOK_SEP;
+}
-static int IsBlank (int C)
-/* Return true if the character is a blank or tab */
+static void DoneCharSource (void)
+/* Close the top level character source */
{
- return (C == ' ' || C == '\t');
-}
+ CharSource* S;
+ /* First, call the type specific function */
+ Source->Func->Done (Source);
+ /* Restore the old token */
+ CurTok.Tok = Source->Tok;
+ C = Source->C;
-static int IsDigit (int C)
-/* Return true if the character is a digit */
-{
- return isdigit (C);
+ /* Remember the last stacked input source */
+ S = Source->Next;
+
+ /* Delete the top level one ... */
+ xfree (Source);
+
+ /* ... and use the one before */
+ Source = S;
}
-static int IsXDigit (int C)
-/* Return true if the character is a hexadecimal digit */
-{
- return isxdigit (C);
-}
+/*****************************************************************************/
+/* InputFile functions */
+/*****************************************************************************/
-static int IsDDigit (int C)
-/* Return true if the character is a dual digit */
+static void IFMarkStart (CharSource* S)
+/* Mark the start of the next token */
{
- return (C == '0' || C == '1');
+ CurTok.Pos = S->V.File.Pos;
}
-static int IsIdChar (int C)
-/* Return true if the character is a valid character for an identifier */
+static void IFNextChar (CharSource* S)
+/* Read the next character from the input file */
{
- return isalnum (C) ||
- (C == '_') ||
- (C == '@' && AtInIdents) ||
- (C == '$' && DollarInIdents);
-}
+ /* Check for end of line, read the next line if needed */
+ while (SB_GetIndex (&S->V.File.Line) >= SB_GetLen (&S->V.File.Line)) {
+ unsigned Len;
+ /* End of current line reached, read next line */
+ SB_Clear (&S->V.File.Line);
+ while (1) {
-static int IsIdStart (int C)
-/* Return true if the character may start an identifier */
-{
- return isalpha (C) || C == '_';
-}
+ int N = fgetc (S->V.File.F);
+ if (N == EOF) {
+ /* End of file. Accept files without a newline at the end */
+ if (SB_NotEmpty (&S->V.File.Line)) {
+ break;
+ }
+ /* No more data - add an empty line to the listing. This
+ * is a small hack needed to keep the PC output in sync.
+ */
+ NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount);
+ C = EOF;
+ return;
+ /* Check for end of line */
+ } else if (N == '\n') {
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
+ /* End of line */
+ break;
+ /* Collect other stuff */
+ } else {
+ /* Append data to line */
+ SB_AppendChar (&S->V.File.Line, N);
-void NewInputFile (const char* Name)
-/* Open a new input file */
-{
- InputFile* I;
- FILE* F;
+ }
+ }
- /* First try to open the file */
- F = fopen (Name, "r");
- if (F == 0) {
- char* PathName;
+ /* If we come here, we have a new input line. To avoid problems
+ * with strange line terminators, remove all whitespace from the
+ * end of the line, the add a single newline.
+ */
+ Len = SB_GetLen (&S->V.File.Line);
+ while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) {
+ --Len;
+ }
+ SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len);
+ SB_AppendChar (&S->V.File.Line, '\n');
- /* Error (fatal error if this is the main file) */
- if (ICount == 0) {
- Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno));
- }
+ /* Terminate the string buffer */
+ SB_Terminate (&S->V.File.Line);
- /* We are on include level. Search for the file in the include
- * directories.
- */
- PathName = FindInclude (Name);
- if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
- /* Not found or cannot open, print an error and bail out */
- Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
- }
+ /* One more line */
+ S->V.File.Pos.Line++;
- /* Free the allocated memory */
- xfree (PathName);
+ /* Remember the new line for the listing */
+ NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount);
}
- /* check again if we do now have an open file */
- if (F != 0) {
-
- unsigned FileIdx;
-
- /* Stat the file and remember the values */
- struct stat Buf;
- if (fstat (fileno (F), &Buf) != 0) {
- Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno));
- }
-
- /* Add the file to the input file table and remember the index */
- FileIdx = AddFile (Name, Buf.st_size, Buf.st_mtime);
-
- /* Create a new state variable and initialize it */
- I = xmalloc (sizeof (*I));
- I->F = F;
- I->Pos.Line = 0;
- I->Pos.Col = 0;
- I->Pos.Name = FileIdx;
- I->Tok = Tok;
- I->C = C;
- I->Line[0] = '\0';
-
- /* Use the new file */
- I->Next = IFile;
- IFile = I;
- ++ICount;
-
- /* Prime the pump */
- NextChar ();
- }
+ /* Set the column pointer */
+ S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line);
+
+ /* Return the next character from the buffer */
+ C = SB_Get (&S->V.File.Line);
}
-void DoneInputFile (void)
+void IFDone (CharSource* S)
/* Close the current input file */
{
- InputFile* I;
+ /* We're at the end of an include file. Check if we have any
+ * open .IFs, or any open token lists in this file. This
+ * enforcement is artificial, using conditionals that start
+ * in one file and end in another are uncommon, and don't
+ * allowing these things will help finding errors.
+ */
+ CheckOpenIfs ();
- /* Restore the old token */
- Tok = IFile->Tok;
- C = IFile->C;
+ /* If we've added search paths for this file, remove them */
+ if (S->V.File.IncSearchPath) {
+ PopSearchPath (IncSearchPath);
+ }
+ if (S->V.File.BinSearchPath) {
+ PopSearchPath (BinSearchPath);
+ }
- /* Save a pointer to the current struct, then set it back */
- I = IFile;
- IFile = I->Next;
+ /* Free the line buffer */
+ SB_Done (&S->V.File.Line);
- /* Cleanup the current stuff */
- fclose (I->F);
- xfree (I);
- --ICount;
+ /* Close the input file and decrement the file count. We will ignore
+ * errors here, since we were just reading from the file.
+ */
+ (void) fclose (S->V.File.F);
+ --FCount;
}
-void NewInputData (const char* Data, int Malloced)
-/* Add a chunk of input data to the input stream */
+/* Set of input file handling functions */
+static const CharSourceFunctions IFFunc = {
+ IFMarkStart,
+ IFNextChar,
+ IFDone
+};
+
+
+
+int NewInputFile (const char* Name)
+/* Open a new input file. Returns true if the file could be successfully opened
+ * and false otherwise.
+ */
{
- InputData* I;
+ int RetCode = 0; /* Return code. Assume an error. */
+ char* PathName = 0;
+ FILE* F;
+ struct stat Buf;
+ StrBuf NameBuf; /* No need to initialize */
+ StrBuf Path = AUTO_STRBUF_INITIALIZER;
+ unsigned FileIdx;
+ CharSource* S;
+
+
+ /* If this is the main file, just try to open it. If it's an include file,
+ * search for it using the include path list.
+ */
+ if (FCount == 0) {
+ /* Main file */
+ F = fopen (Name, "r");
+ if (F == 0) {
+ Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
+ }
+ } else {
+ /* We are on include level. Search for the file in the include
+ * directories.
+ */
+ PathName = SearchFile (IncSearchPath, Name);
+ if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
+ /* Not found or cannot open, print an error and bail out */
+ Error ("Cannot open include file `%s': %s", Name, strerror (errno));
+ goto ExitPoint;
+ }
+
+ /* Use the path name from now on */
+ Name = PathName;
+ }
- /* Create a new state variable and initialize it */
- I = xmalloc (sizeof (*I));
- I->Data = Data;
- I->Pos = Data;
- I->Malloced = Malloced;
- I->Tok = Tok;
- I->C = C;
+ /* Stat the file and remember the values. There a race condition here,
+ * since we cannot use fileno() (non standard identifier in standard
+ * header file), and therefore not fstat. When using stat with the
+ * file name, there's a risk that the file was deleted and recreated
+ * while it was open. Since mtime and size are only used to check
+ * if a file has changed in the debugger, we will ignore this problem
+ * here.
+ */
+ if (FileStat (Name, &Buf) != 0) {
+ Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
+ }
- /* Use the new data */
- I->Next = IData;
- IData = I;
+ /* Add the file to the input file table and remember the index */
+ FileIdx = AddFile (SB_InitFromString (&NameBuf, Name),
+ (FCount == 0)? FT_MAIN : FT_INCLUDE,
+ Buf.st_size, (unsigned long) Buf.st_mtime);
+
+ /* Create a new input source variable and initialize it */
+ S = xmalloc (sizeof (*S));
+ S->Func = &IFFunc;
+ S->V.File.F = F;
+ S->V.File.Pos.Line = 0;
+ S->V.File.Pos.Col = 0;
+ S->V.File.Pos.Name = FileIdx;
+ SB_Init (&S->V.File.Line);
+
+ /* Push the path for this file onto the include search lists */
+ SB_CopyBuf (&Path, Name, FindName (Name) - Name);
+ SB_Terminate (&Path);
+ S->V.File.IncSearchPath = PushSearchPath (IncSearchPath, SB_GetConstBuf (&Path));
+ S->V.File.BinSearchPath = PushSearchPath (BinSearchPath, SB_GetConstBuf (&Path));
+ SB_Done (&Path);
+
+ /* Count active input files */
+ ++FCount;
+
+ /* Use this input source */
+ UseCharSource (S);
+
+ /* File successfully opened */
+ RetCode = 1;
+
+ExitPoint:
+ /* Free an allocated name buffer */
+ xfree (PathName);
+
+ /* Return the success code */
+ return RetCode;
+}
- /* Prime the pump */
- NextChar ();
+
+
+/*****************************************************************************/
+/* InputData functions */
+/*****************************************************************************/
+
+
+
+static void IDMarkStart (CharSource* S attribute ((unused)))
+/* Mark the start of the next token */
+{
+ /* Nothing to do here */
}
-static void DoneInputData (void)
-/* End the current input data stream */
+static void IDNextChar (CharSource* S)
+/* Read the next character from the input text */
{
- InputData* I;
+ C = *S->V.Data.Pos++;
+ if (C == '\0') {
+ /* End of input data */
+ --S->V.Data.Pos;
+ C = EOF;
+ }
+}
- /* Restore the old token */
- Tok = IData->Tok;
- C = IData->C;
- /* Save a pointer to the current struct, then set it back */
- I = IData;
- IData = I->Next;
+void IDDone (CharSource* S)
+/* Close the current input data */
+{
/* Cleanup the current stuff */
- if (I->Malloced) {
- xfree (I->Data);
+ if (S->V.Data.Malloced) {
+ xfree (S->V.Data.Text);
}
- xfree (I);
}
-static unsigned DigitVal (unsigned char C)
-/* Convert a digit into it's numerical representation */
+/* Set of input data handling functions */
+static const CharSourceFunctions IDFunc = {
+ IDMarkStart,
+ IDNextChar,
+ IDDone
+};
+
+
+
+void NewInputData (char* Text, int Malloced)
+/* Add a chunk of input data to the input stream */
{
- if (IsDigit (C)) {
- return C - '0';
- } else {
- return toupper (C) - 'A' + 10;
- }
+ CharSource* S;
+
+ /* Create a new input source variable and initialize it */
+ S = xmalloc (sizeof (*S));
+ S->Func = &IDFunc;
+ S->V.Data.Text = Text;
+ S->V.Data.Pos = Text;
+ S->V.Data.Malloced = Malloced;
+
+ /* Use this input source */
+ UseCharSource (S);
}
-static void NextChar (void)
-/* Read the next character from the input file */
+/*****************************************************************************/
+/* Character classification functions */
+/*****************************************************************************/
+
+
+
+int IsIdChar (int C)
+/* Return true if the character is a valid character for an identifier */
{
- /* If we have an input data structure, read from there */
- if (IData) {
+ return IsAlNum (C) ||
+ (C == '_') ||
+ (C == '@' && AtInIdents) ||
+ (C == '$' && DollarInIdents);
+}
- C = *IData->Pos++;
- if (C == '\0') {
- /* End of input data, will set to last file char */
- DoneInputData ();
- }
- } else {
- /* Check for end of line, read the next line if needed */
- while (IFile->Line [IFile->Pos.Col] == '\0') {
+int IsIdStart (int C)
+/* Return true if the character may start an identifier */
+{
+ return IsAlpha (C) || C == '_';
+}
- /* End of current line reached, read next line */
- if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) {
- /* End of file. Add an empty line to the listing. This is a
- * small hack needed to keep the PC output in sync.
- */
- NewListingLine ("", IFile->Pos.Name, ICount);
- C = EOF;
- return;
- }
- /* One more line */
- IFile->Pos.Line++;
- IFile->Pos.Col = 0;
- /* Remember the new line for the listing */
- NewListingLine (IFile->Line, IFile->Pos.Name, ICount);
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
- }
- /* Return the next character from the file */
- C = IFile->Line [IFile->Pos.Col++];
+static unsigned DigitVal (unsigned char C)
+/* Convert a digit into it's numerical representation */
+{
+ if (IsDigit (C)) {
+ return C - '0';
+ } else {
+ return toupper (C) - 'A' + 10;
}
}
+static void NextChar (void)
+/* Read the next character from the input file */
+{
+ Source->Func->NextChar (Source);
+}
+
+
+
void LocaseSVal (void)
/* Make SVal lower case */
{
- unsigned I = 0;
- while (SVal [I]) {
- SVal [I] = tolower (SVal [I]);
- ++I;
- }
+ SB_ToLower (&CurTok.SVal);
}
void UpcaseSVal (void)
/* Make SVal upper case */
{
- unsigned I = 0;
- while (SVal [I]) {
- SVal [I] = toupper (SVal [I]);
- ++I;
- }
+ SB_ToUpper (&CurTok.SVal);
}
-static unsigned char FindDotKeyword (void)
+static token_t FindDotKeyword (void)
/* Find the dot keyword in SVal. Return the corresponding token if found,
* return TOK_NONE if not found.
*/
{
- static const struct DotKeyword K = { SVal, 0 };
+ struct DotKeyword K;
struct DotKeyword* R;
+ /* Initialize K */
+ K.Key = SB_GetConstBuf (&CurTok.SVal);
+ K.Tok = 0;
+
/* If we aren't in ignore case mode, we have to uppercase the keyword */
if (!IgnoreCase) {
- UpcaseSVal ();
+ UpcaseSVal ();
}
/* Search for the keyword */
R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
- sizeof (DotKeywords [0]), CmpDotKeyword);
+ sizeof (DotKeywords [0]), CmpDotKeyword);
if (R != 0) {
- return R->Tok;
+ return R->Tok;
} else {
- return TOK_NONE;
+ return TOK_NONE;
}
}
static void ReadIdent (void)
-/* Read an identifier from the current input position into Ident. It is
- * assumed that the first character has already been checked.
+/* Read an identifier from the current input position into Ident. Filling SVal
+ * starts at the current position with the next character in C. It is assumed
+ * that any characters already filled in are ok, and the character in C is
+ * checked.
*/
{
/* Read the identifier */
- unsigned I = 0;
do {
- if (I < MAX_STR_LEN) {
- SVal [I++] = C;
- }
- NextChar ();
+ SB_AppendChar (&CurTok.SVal, C);
+ NextChar ();
} while (IsIdChar (C));
- SVal [I] = '\0';
+ SB_Terminate (&CurTok.SVal);
/* If we should ignore case, convert the identifier to upper case */
if (IgnoreCase) {
- UpcaseSVal ();
+ UpcaseSVal ();
}
}
-static unsigned ReadStringConst (int StringTerm)
-/* Read a string constant into SVal. Check for maximum string length and all
- * other stuff. The length of the string is returned.
- */
+static void ReadStringConst (int StringTerm)
+/* Read a string constant into SVal. */
{
- unsigned I;
-
/* Skip the leading string terminator */
NextChar ();
/* Read the string */
- I = 0;
while (1) {
- if (C == StringTerm) {
- break;
- }
- if (C == '\n' || C == EOF) {
- Error (ERR_NEWLINE_IN_STRING);
- break;
- }
-
- /* Check for string length, print an error message once */
- if (I == MAX_STR_LEN) {
- Error (ERR_STRING_TOO_LONG);
- } else if (I < MAX_STR_LEN) {
- SVal [I] = C;
- }
- ++I;
-
- /* Skip the character */
- NextChar ();
+ if (C == StringTerm) {
+ break;
+ }
+ if (C == '\n' || C == EOF) {
+ Error ("Newline in string constant");
+ break;
+ }
+
+ /* Append the char to the string */
+ SB_AppendChar (&CurTok.SVal, C);
+
+ /* Skip the character */
+ NextChar ();
}
/* Skip the trailing terminator */
NextChar ();
/* Terminate the string */
- if (I >= MAX_STR_LEN) {
- I = MAX_STR_LEN;
+ SB_Terminate (&CurTok.SVal);
+}
+
+
+
+static int Sweet16Reg (const StrBuf* Id)
+/* Check if the given identifier is a sweet16 register. Return -1 if this is
+ * not the case, return the register number otherwise.
+ */
+{
+ unsigned RegNum;
+ char Check;
+
+ if (SB_GetLen (Id) < 2) {
+ return -1;
+ }
+ if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
+ return -1;
+ }
+ if (!IsDigit (SB_AtUnchecked (Id, 1))) {
+ return -1;
+ }
+
+ if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
+ /* Invalid register */
+ return -1;
}
- SVal [I] = '\0';
- /* Return the length of the string */
- return I;
+ /* The register number is valid */
+ return (int) RegNum;
}
void NextRawTok (void)
/* Read the next raw token from the input stream */
{
+ Macro* M;
+
/* If we've a forced end of assembly, don't read further */
if (ForcedEnd) {
- Tok = TOK_EOF;
- return;
+ CurTok.Tok = TOK_EOF;
+ return;
}
Restart:
/* Check if we have tokens from another input source */
if (InputFromStack ()) {
- return;
+ if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) {
+ /* This is a define style macro - expand it */
+ MacExpandStart (M);
+ goto Restart;
+ }
+ return;
}
Again:
/* Skip whitespace, remember if we had some */
- if ((WS = IsBlank (C)) != 0) {
- do {
- NextChar ();
+ if ((CurTok.WS = IsBlank (C)) != 0) {
+ do {
+ NextChar ();
} while (IsBlank (C));
}
- /* If we're reading from the file, update the location from where the
- * next token will be read. If we're reading from input data, keep the
- * current position.
- */
- if (IData == 0) {
- CurPos = IFile->Pos;
- }
+ /* Mark the file position of the next token */
+ Source->Func->MarkStart (Source);
+
+ /* Clear the string attribute */
+ SB_Clear (&CurTok.SVal);
+
+ /* Generate line info for the current token */
+ NewAsmLine ();
/* Hex number or PC symbol? */
if (C == '$') {
- NextChar ();
-
- /* Hex digit must follow or DollarIsPC must be enabled */
- if (!IsXDigit (C)) {
- if (DollarIsPC) {
- Tok = TOK_PC;
- return;
- } else {
- Error (ERR_HEX_DIGIT_EXPECTED);
- }
- }
-
- /* Read the number */
- IVal = 0;
- while (IsXDigit (C)) {
- if (IVal & 0xF0000000) {
- Error (ERR_NUM_OVERFLOW);
- IVal = 0;
- }
- IVal = (IVal << 4) + DigitVal (C);
- NextChar ();
- }
-
- /* This is an integer constant */
- Tok = TOK_INTCON;
- return;
+ NextChar ();
+
+ /* Hex digit must follow or DollarIsPC must be enabled */
+ if (!IsXDigit (C)) {
+ if (DollarIsPC) {
+ CurTok.Tok = TOK_PC;
+ return;
+ } else {
+ Error ("Hexadecimal digit expected");
+ }
+ }
+
+ /* Read the number */
+ CurTok.IVal = 0;
+ while (1) {
+ if (UnderlineInNumbers && C == '_') {
+ while (C == '_') {
+ NextChar ();
+ }
+ if (!IsXDigit (C)) {
+ Error ("Number may not end with underline");
+ }
+ }
+ if (IsXDigit (C)) {
+ if (CurTok.IVal & 0xF0000000) {
+ Error ("Overflow in hexadecimal number");
+ CurTok.IVal = 0;
+ }
+ CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C);
+ NextChar ();
+ } else {
+ break;
+ }
+ }
+
+ /* This is an integer constant */
+ CurTok.Tok = TOK_INTCON;
+ return;
}
- /* Dual number? */
+ /* Binary number? */
if (C == '%') {
- NextChar ();
-
- /* 0 or 1 must follow */
- if (!IsDDigit (C)) {
- Error (ERR_01_EXPECTED);
- }
-
- /* Read the number */
- IVal = 0;
- while (IsDDigit (C)) {
- if (IVal & 0x80000000) {
- Error (ERR_NUM_OVERFLOW);
- IVal = 0;
- }
- IVal = (IVal << 1) + DigitVal (C);
- NextChar ();
- }
-
- /* This is an integer constant */
- Tok = TOK_INTCON;
- return;
+ NextChar ();
+
+ /* 0 or 1 must follow */
+ if (!IsBDigit (C)) {
+ Error ("Binary digit expected");
+ }
+
+ /* Read the number */
+ CurTok.IVal = 0;
+ while (1) {
+ if (UnderlineInNumbers && C == '_') {
+ while (C == '_') {
+ NextChar ();
+ }
+ if (!IsBDigit (C)) {
+ Error ("Number may not end with underline");
+ }
+ }
+ if (IsBDigit (C)) {
+ if (CurTok.IVal & 0x80000000) {
+ Error ("Overflow in binary number");
+ CurTok.IVal = 0;
+ }
+ CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C);
+ NextChar ();
+ } else {
+ break;
+ }
+ }
+
+ /* This is an integer constant */
+ CurTok.Tok = TOK_INTCON;
+ return;
}
- /* Decimal number? */
+ /* Number? */
if (IsDigit (C)) {
- /* Read the number */
- IVal = 0;
- while (IsDigit (C)) {
- if (IVal > (0xFFFFFFFF / 10)) {
- Error (ERR_NUM_OVERFLOW);
- IVal = 0;
- }
- IVal = (IVal * 10) + DigitVal (C);
- NextChar ();
- }
-
- /* This is an integer constant */
- Tok = TOK_INTCON;
- return;
+ char Buf[16];
+ unsigned Digits;
+ unsigned Base;
+ unsigned I;
+ long Max;
+ unsigned DVal;
+
+ /* Ignore leading zeros */
+ while (C == '0') {
+ NextChar ();
+ }
+
+ /* Read the number into Buf counting the digits */
+ Digits = 0;
+ while (1) {
+ if (UnderlineInNumbers && C == '_') {
+ while (C == '_') {
+ NextChar ();
+ }
+ if (!IsXDigit (C)) {
+ Error ("Number may not end with underline");
+ }
+ }
+ if (IsXDigit (C)) {
+ /* Buf is big enough to allow any decimal and hex number to
+ * overflow, so ignore excess digits here, they will be detected
+ * when we convert the value.
+ */
+ if (Digits < sizeof (Buf)) {
+ Buf[Digits++] = C;
+ }
+ NextChar ();
+ } else {
+ break;
+ }
+ }
+
+ /* Allow zilog/intel style hex numbers with a 'h' suffix */
+ if (C == 'h' || C == 'H') {
+ NextChar ();
+ Base = 16;
+ Max = 0xFFFFFFFFUL / 16;
+ } else {
+ Base = 10;
+ Max = 0xFFFFFFFFUL / 10;
+ }
+
+ /* Convert the number using the given base */
+ CurTok.IVal = 0;
+ for (I = 0; I < Digits; ++I) {
+ if (CurTok.IVal > Max) {
+ Error ("Number out of range");
+ CurTok.IVal = 0;
+ break;
+ }
+ DVal = DigitVal (Buf[I]);
+ if (DVal > Base) {
+ Error ("Invalid digits in number");
+ CurTok.IVal = 0;
+ break;
+ }
+ CurTok.IVal = (CurTok.IVal * Base) + DVal;
+ }
+
+ /* This is an integer constant */
+ CurTok.Tok = TOK_INTCON;
+ return;
}
/* Control command? */
if (C == '.') {
- NextChar ();
-
- if (!IsIdStart (C)) {
- Error (ERR_PSEUDO_EXPECTED);
- /* Try to read an identifier */
- goto Again;
- }
-
- /* Read the identifier */
- ReadIdent ();
-
- /* Search the keyword */
- Tok = FindDotKeyword ();
- if (Tok == TOK_NONE) {
- /* Not found */
- Error (ERR_PSEUDO_EXPECTED);
- goto Again;
- }
- return;
+ /* Remember and skip the dot */
+ NextChar ();
+
+ /* Check if it's just a dot */
+ if (!IsIdStart (C)) {
+
+ /* Just a dot */
+ CurTok.Tok = TOK_DOT;
+
+ } else {
+
+ /* Read the remainder of the identifier */
+ SB_AppendChar (&CurTok.SVal, '.');
+ ReadIdent ();
+
+ /* Dot keyword, search for it */
+ CurTok.Tok = FindDotKeyword ();
+ if (CurTok.Tok == TOK_NONE) {
+
+ /* Not found */
+ if (!LeadingDotInIdents) {
+ /* Invalid pseudo instruction */
+ Error ("`%m%p' is not a recognized control command", &CurTok.SVal);
+ goto Again;
+ }
+
+ /* An identifier with a dot. Check if it's a define style
+ * macro.
+ */
+ if ((M = FindDefine (&CurTok.SVal)) != 0) {
+ /* This is a define style macro - expand it */
+ MacExpandStart (M);
+ goto Restart;
+ }
+
+ /* Just an identifier with a dot */
+ CurTok.Tok = TOK_IDENT;
+ }
+
+ }
+ return;
+ }
+
+ /* Indirect op for sweet16 cpu. Must check this before checking for local
+ * symbols, because these may also use the '@' symbol.
+ */
+ if (CPU == CPU_SWEET16 && C == '@') {
+ NextChar ();
+ CurTok.Tok = TOK_AT;
+ return;
}
/* Local symbol? */
if (C == LocalStart) {
- /* Read the identifier */
- ReadIdent ();
+ /* Read the identifier. */
+ ReadIdent ();
- /* Start character alone is not enough */
- if (SVal [1] == '\0') {
- Error (ERR_IDENT_EXPECTED);
- goto Again;
- }
+ /* Start character alone is not enough */
+ if (SB_GetLen (&CurTok.SVal) == 1) {
+ Error ("Invalid cheap local symbol");
+ goto Again;
+ }
- /* An identifier */
- Tok = TOK_IDENT;
- return;
+ /* A local identifier */
+ CurTok.Tok = TOK_LOCAL_IDENT;
+ return;
}
/* Identifier or keyword? */
if (IsIdStart (C)) {
- /* Read the identifier */
- ReadIdent ();
-
- /* Check for special names */
- if (SVal [1] == '\0') {
- switch (toupper (SVal [0])) {
-
- case 'A':
- Tok = TOK_A;
- return;
-
- case 'X':
- Tok = TOK_X;
- return;
-
- case 'Y':
- Tok = TOK_Y;
- return;
-
- case 'S':
- Tok = TOK_S;
- return;
-
- default:
- Tok = TOK_IDENT;
- return;
- }
- }
-
- /* Search for an opcode */
- IVal = FindInstruction (SVal);
- if (IVal >= 0) {
- /* This is a mnemonic */
- Tok = TOK_MNEMO;
- } else if (IsDefine (SVal)) {
- /* This is a define style macro - expand it */
- MacExpandStart ();
- goto Restart;
- } else {
- /* An identifier */
- Tok = TOK_IDENT;
- }
- return;
+ /* Read the identifier */
+ ReadIdent ();
+
+ /* Check for special names. Bail out if we have identified the type of
+ * the token. Go on if the token is an identifier.
+ */
+ if (SB_GetLen (&CurTok.SVal) == 1) {
+ switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) {
+
+ case 'A':
+ if (C == ':') {
+ NextChar ();
+ CurTok.Tok = TOK_OVERRIDE_ABS;
+ } else {
+ CurTok.Tok = TOK_A;
+ }
+ return;
+
+ case 'F':
+ if (C == ':') {
+ NextChar ();
+ CurTok.Tok = TOK_OVERRIDE_FAR;
+ return;
+ }
+ break;
+
+ case 'S':
+ if (CPU == CPU_65816) {
+ CurTok.Tok = TOK_S;
+ return;
+ }
+ break;
+
+ case 'X':
+ CurTok.Tok = TOK_X;
+ return;
+
+ case 'Y':
+ CurTok.Tok = TOK_Y;
+ return;
+
+ case 'Z':
+ if (C == ':') {
+ NextChar ();
+ CurTok.Tok = TOK_OVERRIDE_ZP;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } else if (CPU == CPU_SWEET16 &&
+ (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) {
+
+ /* A sweet16 register number in sweet16 mode */
+ CurTok.Tok = TOK_REG;
+ return;
+
+ }
+
+ /* Check for define style macro */
+ if ((M = FindDefine (&CurTok.SVal)) != 0) {
+ /* Macro - expand it */
+ MacExpandStart (M);
+ goto Restart;
+ } else {
+ /* An identifier */
+ CurTok.Tok = TOK_IDENT;
+ }
+ return;
}
/* Ok, let's do the switch */
CharAgain:
switch (C) {
- case '+':
- NextChar ();
- Tok = TOK_PLUS;
- return;
-
- case '-':
- NextChar ();
- Tok = TOK_MINUS;
- return;
-
- case '/':
- NextChar ();
- Tok = TOK_DIV;
- return;
-
- case '*':
- NextChar ();
- Tok = TOK_MUL;
- return;
-
- case '^':
- NextChar ();
- Tok = TOK_XOR;
- return;
-
- case '&':
- NextChar ();
- if (C == '&') {
- NextChar ();
- Tok = TOK_BAND;
- } else {
- Tok = TOK_AND;
- }
- return;
-
- case '|':
- NextChar ();
- if (C == '|') {
- NextChar ();
- Tok = TOK_BOR;
- } else {
- Tok = TOK_OR;
- }
- return;
-
- case ':':
- NextChar ();
- switch (C) {
-
- case ':':
- NextChar ();
- Tok = TOK_NAMESPACE;
- break;
-
- case '-':
- IVal = 0;
- do {
- --IVal;
- NextChar ();
- } while (C == '-');
- Tok = TOK_ULABEL;
- break;
-
- case '+':
- IVal = 0;
- do {
- ++IVal;
- NextChar ();
- } while (C == '+');
- Tok = TOK_ULABEL;
- break;
-
- default:
- Tok = TOK_COLON;
- break;
- }
- return;
-
- case ',':
- NextChar ();
- Tok = TOK_COMMA;
- return;
-
- case ';':
- NextChar ();
- while (C != '\n' && C != EOF) {
- NextChar ();
- }
- goto CharAgain;
-
- case '#':
- NextChar ();
- Tok = TOK_HASH;
- return;
-
- case '(':
- NextChar ();
- Tok = TOK_LPAREN;
- return;
-
- case ')':
- NextChar ();
- Tok = TOK_RPAREN;
- return;
-
- case '[':
- NextChar ();
- Tok = TOK_LBRACK;
- return;
-
- case ']':
- NextChar ();
- Tok = TOK_RBRACK;
- return;
-
- case '<':
- NextChar ();
- if (C == '=') {
- NextChar ();
- Tok = TOK_LE;
- } else if (C == '<') {
- NextChar ();
- Tok = TOK_SHL;
- } else if (C == '>') {
- NextChar ();
- Tok = TOK_NE;
- } else {
- Tok = TOK_LT;
- }
- return;
-
- case '=':
- NextChar ();
- Tok = TOK_EQ;
- return;
-
- case '!':
- NextChar ();
- Tok = TOK_BNOT;
- return;
-
- case '>':
- NextChar ();
- if (C == '=') {
- NextChar ();
- Tok = TOK_GE;
- } else if (C == '>') {
- NextChar ();
- Tok = TOK_SHR;
- } else {
- Tok = TOK_GT;
- }
- return;
+ case '+':
+ NextChar ();
+ CurTok.Tok = TOK_PLUS;
+ return;
+
+ case '-':
+ NextChar ();
+ CurTok.Tok = TOK_MINUS;
+ return;
+
+ case '/':
+ NextChar ();
+ if (C != '*') {
+ CurTok.Tok = TOK_DIV;
+ } else if (CComments) {
+ /* Remember the position, then skip the '*' */
+ Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
+ GetFullLineInfo (&LineInfos);
+ NextChar ();
+ do {
+ while (C != '*') {
+ if (C == EOF) {
+ LIError (&LineInfos, "Unterminated comment");
+ ReleaseFullLineInfo (&LineInfos);
+ DoneCollection (&LineInfos);
+ goto CharAgain;
+ }
+ NextChar ();
+ }
+ NextChar ();
+ } while (C != '/');
+ NextChar ();
+ ReleaseFullLineInfo (&LineInfos);
+ DoneCollection (&LineInfos);
+ goto Again;
+ }
+ return;
+
+ case '*':
+ NextChar ();
+ CurTok.Tok = TOK_MUL;
+ return;
+
+ case '^':
+ NextChar ();
+ CurTok.Tok = TOK_XOR;
+ return;
+
+ case '&':
+ NextChar ();
+ if (C == '&') {
+ NextChar ();
+ CurTok.Tok = TOK_BOOLAND;
+ } else {
+ CurTok.Tok = TOK_AND;
+ }
+ return;
+
+ case '|':
+ NextChar ();
+ if (C == '|') {
+ NextChar ();
+ CurTok.Tok = TOK_BOOLOR;
+ } else {
+ CurTok.Tok = TOK_OR;
+ }
+ return;
+
+ case ':':
+ NextChar ();
+ switch (C) {
+
+ case ':':
+ NextChar ();
+ CurTok.Tok = TOK_NAMESPACE;
+ break;
+
+ case '-':
+ CurTok.IVal = 0;
+ do {
+ --CurTok.IVal;
+ NextChar ();
+ } while (C == '-');
+ CurTok.Tok = TOK_ULABEL;
+ break;
+
+ case '+':
+ CurTok.IVal = 0;
+ do {
+ ++CurTok.IVal;
+ NextChar ();
+ } while (C == '+');
+ CurTok.Tok = TOK_ULABEL;
+ break;
+
+ case '=':
+ NextChar ();
+ CurTok.Tok = TOK_ASSIGN;
+ break;
+
+ default:
+ CurTok.Tok = TOK_COLON;
+ break;
+ }
+ return;
+
+ case ',':
+ NextChar ();
+ CurTok.Tok = TOK_COMMA;
+ return;
+
+ case ';':
+ NextChar ();
+ while (C != '\n' && C != EOF) {
+ NextChar ();
+ }
+ goto CharAgain;
+
+ case '#':
+ NextChar ();
+ CurTok.Tok = TOK_HASH;
+ return;
+
+ case '(':
+ NextChar ();
+ CurTok.Tok = TOK_LPAREN;
+ return;
+
+ case ')':
+ NextChar ();
+ CurTok.Tok = TOK_RPAREN;
+ return;
+
+ case '[':
+ NextChar ();
+ CurTok.Tok = TOK_LBRACK;
+ return;
+
+ case ']':
+ NextChar ();
+ CurTok.Tok = TOK_RBRACK;
+ return;
+
+ case '{':
+ NextChar ();
+ CurTok.Tok = TOK_LCURLY;
+ return;
+
+ case '}':
+ NextChar ();
+ CurTok.Tok = TOK_RCURLY;
+ return;
+
+ case '<':
+ NextChar ();
+ if (C == '=') {
+ NextChar ();
+ CurTok.Tok = TOK_LE;
+ } else if (C == '<') {
+ NextChar ();
+ CurTok.Tok = TOK_SHL;
+ } else if (C == '>') {
+ NextChar ();
+ CurTok.Tok = TOK_NE;
+ } else {
+ CurTok.Tok = TOK_LT;
+ }
+ return;
+
+ case '=':
+ NextChar ();
+ CurTok.Tok = TOK_EQ;
+ return;
+
+ case '!':
+ NextChar ();
+ CurTok.Tok = TOK_BOOLNOT;
+ return;
+
+ case '>':
+ NextChar ();
+ if (C == '=') {
+ NextChar ();
+ CurTok.Tok = TOK_GE;
+ } else if (C == '>') {
+ NextChar ();
+ CurTok.Tok = TOK_SHR;
+ } else {
+ CurTok.Tok = TOK_GT;
+ }
+ return;
case '~':
- NextChar ();
- Tok = TOK_NOT;
- return;
-
- case '\'':
- /* Hack: If we allow ' as terminating character for strings, read
- * the following stuff as a string, and check for a one character
- * string later.
- */
- if (LooseStringTerm) {
- if (ReadStringConst ('\'') == 1) {
- IVal = SVal[0];
- Tok = TOK_CHARCON;
- } else {
- Tok = TOK_STRCON;
- }
- } else {
- /* Always a character constant */
- NextChar ();
- if (C == '\n' || C == EOF) {
- Error (ERR_ILLEGAL_CHARCON);
- goto CharAgain;
- }
- IVal = C;
- Tok = TOK_CHARCON;
- NextChar ();
- if (C != '\'') {
- Error (ERR_ILLEGAL_CHARCON);
- } else {
- NextChar ();
- }
- }
- return;
-
- case '\"':
- ReadStringConst ('\"');
- Tok = TOK_STRCON;
- return;
-
- case '\\':
- /* Line continuation? */
- if (LineCont) {
- NextChar ();
- if (C == '\n') {
- /* Handle as white space */
- NextChar ();
- C = ' ';
- goto Again;
- }
- }
- break;
+ NextChar ();
+ CurTok.Tok = TOK_NOT;
+ return;
+
+ case '\'':
+ /* Hack: If we allow ' as terminating character for strings, read
+ * the following stuff as a string, and check for a one character
+ * string later.
+ */
+ if (LooseStringTerm) {
+ ReadStringConst ('\'');
+ if (SB_GetLen (&CurTok.SVal) == 1) {
+ CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0);
+ CurTok.Tok = TOK_CHARCON;
+ } else {
+ CurTok.Tok = TOK_STRCON;
+ }
+ } else {
+ /* Always a character constant */
+ NextChar ();
+ if (C == EOF || IsControl (C)) {
+ Error ("Illegal character constant");
+ goto CharAgain;
+ }
+ CurTok.IVal = C;
+ CurTok.Tok = TOK_CHARCON;
+ NextChar ();
+ if (C != '\'') {
+ if (!MissingCharTerm) {
+ Error ("Illegal character constant");
+ }
+ } else {
+ NextChar ();
+ }
+ }
+ return;
+
+ case '\"':
+ ReadStringConst ('\"');
+ CurTok.Tok = TOK_STRCON;
+ return;
+
+ case '\\':
+ /* Line continuation? */
+ if (LineCont) {
+ NextChar ();
+ if (C == '\n') {
+ /* Handle as white space */
+ NextChar ();
+ C = ' ';
+ goto Again;
+ }
+ }
+ break;
case '\n':
- NextChar ();
- Tok = TOK_SEP;
- return;
+ NextChar ();
+ CurTok.Tok = TOK_SEP;
+ return;
case EOF:
- /* Check if we have any open .IFs in this file */
- CheckOpenIfs ();
- /* Check if we have any open token lists in this file */
- CheckInputStack ();
-
- /* If this was an include file, then close it and handle like a
- * separator. Do not close the main file, but return EOF.
- */
- if (ICount > 1) {
- DoneInputFile ();
- } else {
- Tok = TOK_EOF;
- }
- return;
-
+ CheckInputStack ();
+ /* In case of the main file, do not close it, but return EOF. */
+ if (Source && Source->Next) {
+ DoneCharSource ();
+ goto Again;
+ } else {
+ CurTok.Tok = TOK_EOF;
+ }
+ return;
}
/* If we go here, we could not identify the current character. Skip it
* and try again.
*/
- Error (ERR_INVALID_CHAR, C & 0xFF);
+ Error ("Invalid input character: 0x%02X", C & 0xFF);
NextChar ();
goto Again;
}
-int TokHasSVal (enum Token Tok)
-/* Return true if the given token has an attached SVal */
-{
- return (Tok == TOK_IDENT || Tok == TOK_STRCON);
-}
-
-
-
-int TokHasIVal (enum Token Tok)
-/* Return true if the given token has an attached IVal */
-{
- return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_MNEMO);
-}
-
-
-
int GetSubKey (const char** Keys, unsigned Count)
/* Search for a subkey in a table of keywords. The current token must be an
* identifier and all keys must be in upper case. The identifier will be
unsigned I;
/* Must have an identifier */
- PRECONDITION (Tok == TOK_IDENT);
+ PRECONDITION (CurTok.Tok == TOK_IDENT);
/* If we aren't in ignore case mode, we have to uppercase the identifier */
if (!IgnoreCase) {
- UpcaseSVal ();
+ UpcaseSVal ();
}
/* Do a linear search (a binary search is not worth the effort) */
for (I = 0; I < Count; ++I) {
- if (strcmp (SVal, Keys [I]) == 0) {
- /* Found it */
- return I;
- }
+ if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) {
+ /* Found it */
+ return I;
+ }
}
/* Not found */
+unsigned char ParseAddrSize (void)
+/* Check if the next token is a keyword that denotes an address size specifier.
+ * If so, return the corresponding address size constant, otherwise output an
+ * error message and return ADDR_SIZE_DEFAULT.
+ */
+{
+ unsigned char AddrSize;
+
+ /* Check for an identifier */
+ if (CurTok.Tok != TOK_IDENT) {
+ Error ("Address size specifier expected");
+ return ADDR_SIZE_DEFAULT;
+ }
+
+ /* Convert the attribute */
+ AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal));
+ if (AddrSize == ADDR_SIZE_INVALID) {
+ Error ("Address size specifier expected");
+ AddrSize = ADDR_SIZE_DEFAULT;
+ }
+
+ /* Done */
+ return AddrSize;
+}
+
+
+
void InitScanner (const char* InFile)
/* Initialize the scanner, open the given input file */
{
void DoneScanner (void)
/* Release scanner resources */
{
- DoneInputFile ();
+ DoneCharSource ();
}
-
-
-