2000-05-28 13:40:48 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* scanner.c */
|
|
|
|
/* */
|
|
|
|
/* The scanner for the ca65 macroassembler */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* (C) 1998-2000 Ullrich von Bassewitz */
|
|
|
|
/* Wacholderweg 14 */
|
|
|
|
/* D-70597 Stuttgart */
|
|
|
|
/* EMail: uz@musoftware.de */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
/* */
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
/* */
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
/* distribution. */
|
|
|
|
/* */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2000-07-30 21:27:37 +00:00
|
|
|
/* common */
|
2000-08-01 15:17:43 +00:00
|
|
|
#include "check.h"
|
2000-07-30 21:27:37 +00:00
|
|
|
#include "fname.h"
|
|
|
|
#include "xmalloc.h"
|
2000-08-01 15:17:43 +00:00
|
|
|
|
2000-07-30 21:27:37 +00:00
|
|
|
/* ca65 */
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "condasm.h"
|
|
|
|
#include "error.h"
|
2000-08-01 21:36:45 +00:00
|
|
|
#include "filetab.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "global.h"
|
2000-05-29 18:40:56 +00:00
|
|
|
#include "incpath.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "instr.h"
|
2000-06-03 11:15:11 +00:00
|
|
|
#include "istack.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "listing.h"
|
|
|
|
#include "macro.h"
|
2000-06-03 11:15:11 +00:00
|
|
|
#include "toklist.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
#include "scanner.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Data */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum Token Tok = TOK_NONE; /* Current token */
|
2000-07-30 21:27:37 +00:00
|
|
|
int WS; /* Flag: Whitespace before token */
|
2000-05-28 13:40:48 +00:00
|
|
|
long IVal; /* Integer token attribute */
|
|
|
|
char SVal [MAX_STR_LEN+1]; /* String token attribute */
|
|
|
|
|
|
|
|
FilePos CurPos = { 0, 0, 0 }; /* Name and position in current file */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Current input variables */
|
2000-06-03 11:15:11 +00:00
|
|
|
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 */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Force end of assembly */
|
|
|
|
int ForcedEnd = 0;
|
|
|
|
|
|
|
|
/* List of dot keywords with the corresponding tokens */
|
|
|
|
struct DotKeyword {
|
|
|
|
const char* Key; /* MUST be first field */
|
|
|
|
enum Token Tok;
|
|
|
|
} DotKeywords [] = {
|
|
|
|
{ "A16", TOK_A16 },
|
|
|
|
{ "A8", TOK_A8 },
|
2000-07-29 15:53:33 +00:00
|
|
|
{ "ADDR", TOK_ADDR },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "ALIGN", TOK_ALIGN },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "AND", TOK_BAND },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "ASCIIZ", TOK_ASCIIZ },
|
|
|
|
{ "AUTOIMPORT", TOK_AUTOIMPORT },
|
|
|
|
{ "BITAND", TOK_AND },
|
|
|
|
{ "BITNOT", TOK_NOT },
|
|
|
|
{ "BITOR", TOK_OR },
|
|
|
|
{ "BITXOR", TOK_XOR },
|
|
|
|
{ "BLANK", TOK_BLANK },
|
|
|
|
{ "BSS", TOK_BSS },
|
|
|
|
{ "BYTE", TOK_BYTE },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "CASE", TOK_CASE },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "CODE", TOK_CODE },
|
2000-08-01 21:36:45 +00:00
|
|
|
{ "CONCAT", TOK_CONCAT },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "CONST", TOK_CONST },
|
2000-08-01 21:36:45 +00:00
|
|
|
{ "CPU", TOK_CPU },
|
|
|
|
{ "DATA", TOK_DATA },
|
|
|
|
{ "DBG", TOK_DBG },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "DBYT", TOK_DBYT },
|
|
|
|
{ "DEBUGINFO", TOK_DEBUGINFO },
|
|
|
|
{ "DEF", TOK_DEFINED },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "DEFINE", TOK_DEFINE },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "DEFINED", TOK_DEFINED },
|
|
|
|
{ "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 },
|
2000-07-29 15:53:33 +00:00
|
|
|
{ "FORCEWORD", TOK_FORCEWORD },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "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 },
|
2000-07-08 14:01:43 +00:00
|
|
|
{ "LEFT", TOK_LEFT },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "LINECONT", TOK_LINECONT },
|
|
|
|
{ "LIST", TOK_LIST },
|
|
|
|
{ "LISTBYTES", TOK_LISTBYTES },
|
|
|
|
{ "LOCAL", TOK_LOCAL },
|
|
|
|
{ "LOCALCHAR", TOK_LOCALCHAR },
|
|
|
|
{ "MAC", TOK_MACRO },
|
|
|
|
{ "MACPACK", TOK_MACPACK },
|
2000-07-25 21:32:11 +00:00
|
|
|
{ "MACRO", TOK_MACRO },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "MATCH", TOK_MATCH },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "MID", TOK_MID },
|
|
|
|
{ "MOD", TOK_MOD },
|
|
|
|
{ "NOT", TOK_BNOT },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "NULL", TOK_NULL },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "OR", TOK_BOR },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "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 },
|
2000-06-03 11:15:11 +00:00
|
|
|
{ "REF", TOK_REFERENCED },
|
2000-05-28 13:40:48 +00:00
|
|
|
{ "REFERENCED", TOK_REFERENCED },
|
|
|
|
{ "RELOC", TOK_RELOC },
|
|
|
|
{ "REPEAT", TOK_REPEAT },
|
|
|
|
{ "RES", TOK_RES },
|
2000-07-25 21:32:11 +00:00
|
|
|
{ "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 },
|
2000-07-27 18:47:57 +00:00
|
|
|
{ "WARNING", TOK_WARNING },
|
2000-07-25 21:32:11 +00:00
|
|
|
{ "WORD", TOK_WORD },
|
|
|
|
{ "XMATCH", TOK_XMATCH },
|
|
|
|
{ "XOR", TOK_BXOR },
|
|
|
|
{ "ZEROPAGE", TOK_ZEROPAGE },
|
2000-05-28 13:40:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Forwards */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void NextChar (void);
|
|
|
|
/* Read the next character from the input file */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Character classification functions */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsBlank (int C)
|
|
|
|
/* Return true if the character is a blank or tab */
|
|
|
|
{
|
|
|
|
return (C == ' ' || C == '\t');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsDigit (int C)
|
|
|
|
/* Return true if the character is a digit */
|
|
|
|
{
|
|
|
|
return isdigit (C);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsXDigit (int C)
|
|
|
|
/* Return true if the character is a hexadecimal digit */
|
|
|
|
{
|
|
|
|
return isxdigit (C);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsDDigit (int C)
|
|
|
|
/* Return true if the character is a dual digit */
|
|
|
|
{
|
|
|
|
return (C == '0' || C == '1');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsIdChar (int C)
|
|
|
|
/* Return true if the character is a valid character for an identifier */
|
|
|
|
{
|
|
|
|
return isalnum (C) ||
|
|
|
|
(C == '_') ||
|
|
|
|
(C == '@' && AtInIdents) ||
|
|
|
|
(C == '$' && DollarInIdents);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int IsIdStart (int C)
|
|
|
|
/* Return true if the character may start an identifier */
|
|
|
|
{
|
|
|
|
return isalpha (C) || C == '_';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Code */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
2000-05-29 18:40:56 +00:00
|
|
|
char* PathName;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Error (fatal error if this is the main file) */
|
|
|
|
if (ICount == 0) {
|
|
|
|
Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno));
|
2000-05-29 18:40:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
2000-05-28 13:40:48 +00:00
|
|
|
Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
|
|
|
|
}
|
|
|
|
|
2000-05-29 18:40:56 +00:00
|
|
|
/* Free the allocated memory */
|
2000-06-14 09:32:22 +00:00
|
|
|
xfree (PathName);
|
2000-05-29 18:40:56 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check again if we do now have an open file */
|
|
|
|
if (F != 0) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2000-08-01 21:36:45 +00:00
|
|
|
unsigned FileIdx;
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Stat the file and remember the values */
|
|
|
|
struct stat Buf;
|
|
|
|
if (fstat (fileno (F), &Buf) != 0) {
|
|
|
|
Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno));
|
|
|
|
}
|
2000-08-01 21:36:45 +00:00
|
|
|
|
|
|
|
/* Add the file to the input file table and remember the index */
|
|
|
|
FileIdx = AddFile (Name, Buf.st_size, Buf.st_mtime);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* Create a new state variable and initialize it */
|
2000-08-01 21:36:45 +00:00
|
|
|
I = xmalloc (sizeof (*I));
|
2000-05-29 18:40:56 +00:00
|
|
|
I->F = F;
|
|
|
|
I->Pos.Line = 0;
|
2000-05-28 13:40:48 +00:00
|
|
|
I->Pos.Col = 0;
|
2000-08-01 21:36:45 +00:00
|
|
|
I->Pos.Name = FileIdx;
|
2000-05-29 18:40:56 +00:00
|
|
|
I->Tok = Tok;
|
2000-08-01 21:36:45 +00:00
|
|
|
I->C = C;
|
2000-05-29 18:40:56 +00:00
|
|
|
I->Line[0] = '\0';
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2000-05-29 18:40:56 +00:00
|
|
|
/* Use the new file */
|
|
|
|
I->Next = IFile;
|
2000-08-01 21:36:45 +00:00
|
|
|
IFile = I;
|
2000-05-29 18:40:56 +00:00
|
|
|
++ICount;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
2000-05-29 18:40:56 +00:00
|
|
|
/* Prime the pump */
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DoneInputFile (void)
|
|
|
|
/* Close the current input file */
|
|
|
|
{
|
|
|
|
InputFile* I;
|
|
|
|
|
|
|
|
/* Restore the old token */
|
|
|
|
Tok = IFile->Tok;
|
|
|
|
C = IFile->C;
|
|
|
|
|
|
|
|
/* Save a pointer to the current struct, then set it back */
|
|
|
|
I = IFile;
|
|
|
|
IFile = I->Next;
|
|
|
|
|
|
|
|
/* Cleanup the current stuff */
|
|
|
|
fclose (I->F);
|
2000-06-14 09:32:22 +00:00
|
|
|
xfree (I);
|
2000-05-28 13:40:48 +00:00
|
|
|
--ICount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NewInputData (const char* Data, int Malloced)
|
|
|
|
/* Add a chunk of input data to the input stream */
|
|
|
|
{
|
|
|
|
InputData* I;
|
|
|
|
|
|
|
|
/* Create a new state variable and initialize it */
|
2000-06-14 09:32:22 +00:00
|
|
|
I = xmalloc (sizeof (*I));
|
2000-05-28 13:40:48 +00:00
|
|
|
I->Data = Data;
|
|
|
|
I->Pos = Data;
|
|
|
|
I->Malloced = Malloced;
|
|
|
|
I->Tok = Tok;
|
|
|
|
I->C = C;
|
|
|
|
|
|
|
|
/* Use the new data */
|
|
|
|
I->Next = IData;
|
|
|
|
IData = I;
|
|
|
|
|
|
|
|
/* Prime the pump */
|
|
|
|
NextChar ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void DoneInputData (void)
|
|
|
|
/* End the current input data stream */
|
|
|
|
{
|
|
|
|
InputData* I;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
/* Cleanup the current stuff */
|
|
|
|
if (I->Malloced) {
|
2000-06-14 09:32:22 +00:00
|
|
|
xfree (I->Data);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
2000-06-14 09:32:22 +00:00
|
|
|
xfree (I);
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
{
|
|
|
|
/* If we have an input data structure, read from there */
|
|
|
|
if (IData) {
|
|
|
|
|
|
|
|
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') {
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2000-07-29 15:53:33 +00:00
|
|
|
NewListingLine ("", IFile->Pos.Name, ICount);
|
2000-05-28 13:40:48 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the next character from the file */
|
|
|
|
C = IFile->Line [IFile->Pos.Col++];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UpcaseSVal (void)
|
|
|
|
/* Make SVal upper case */
|
|
|
|
{
|
|
|
|
unsigned I = 0;
|
|
|
|
while (SVal [I]) {
|
|
|
|
SVal [I] = toupper (SVal [I]);
|
|
|
|
++I;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int CmpDotKeyword (const void* K1, const void* K2)
|
|
|
|
/* Compare function for the dot keyword search */
|
|
|
|
{
|
|
|
|
return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned char 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* R;
|
|
|
|
|
|
|
|
/* If we aren't in ignore case mode, we have to uppercase the keyword */
|
|
|
|
if (!IgnoreCase) {
|
|
|
|
UpcaseSVal ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for the keyword */
|
|
|
|
R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
|
|
|
|
sizeof (DotKeywords [0]), CmpDotKeyword);
|
|
|
|
if (R != 0) {
|
|
|
|
return R->Tok;
|
|
|
|
} else {
|
|
|
|
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 the identifier */
|
|
|
|
unsigned I = 0;
|
|
|
|
do {
|
|
|
|
if (I < MAX_STR_LEN) {
|
|
|
|
SVal [I++] = C;
|
|
|
|
}
|
|
|
|
NextChar ();
|
|
|
|
} while (IsIdChar (C));
|
|
|
|
SVal [I] = '\0';
|
|
|
|
|
|
|
|
/* If we should ignore case, convert the identifier to upper case */
|
|
|
|
if (IgnoreCase) {
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
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 ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the trailing terminator */
|
|
|
|
NextChar ();
|
|
|
|
|
|
|
|
/* Terminate the string */
|
|
|
|
if (I >= MAX_STR_LEN) {
|
|
|
|
I = MAX_STR_LEN;
|
|
|
|
}
|
|
|
|
SVal [I] = '\0';
|
|
|
|
|
|
|
|
/* Return the length of the string */
|
|
|
|
return I;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-06-03 11:15:11 +00:00
|
|
|
void NextRawTok (void)
|
2000-05-28 13:40:48 +00:00
|
|
|
/* Read the next raw token from the input stream */
|
|
|
|
{
|
|
|
|
/* If we've a forced end of assembly, don't read further */
|
|
|
|
if (ForcedEnd) {
|
|
|
|
Tok = TOK_EOF;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2000-06-03 11:15:11 +00:00
|
|
|
Restart:
|
|
|
|
/* Check if we have tokens from another input source */
|
|
|
|
if (InputFromStack ()) {
|
2000-05-28 13:40:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Again:
|
|
|
|
/* Skip whitespace, remember if we had some */
|
|
|
|
if ((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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dual 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decimal number? */
|
|
|
|
if (IsDigit (C)) {
|
|
|
|
|
|
|
|
/* Read the number */
|
|
|
|
IVal = 0;
|
|
|
|
while (IsDigit (C)) {
|
|
|
|
if (IVal > (0xFFFFFFFF / 10)) {
|
2000-07-29 15:53:33 +00:00
|
|
|
Error (ERR_NUM_OVERFLOW);
|
2000-05-28 13:40:48 +00:00
|
|
|
IVal = 0;
|
|
|
|
}
|
|
|
|
IVal = (IVal * 10) + DigitVal (C);
|
|
|
|
NextChar ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is an integer constant */
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Local symbol? */
|
|
|
|
if (C == LocalStart) {
|
|
|
|
|
|
|
|
/* Read the identifier */
|
|
|
|
ReadIdent ();
|
|
|
|
|
|
|
|
/* Start character alone is not enough */
|
|
|
|
if (SVal [1] == '\0') {
|
|
|
|
Error (ERR_IDENT_EXPECTED);
|
|
|
|
goto Again;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An identifier */
|
|
|
|
Tok = TOK_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 ();
|
2000-06-03 11:15:11 +00:00
|
|
|
goto Restart;
|
2000-05-28 13:40:48 +00:00
|
|
|
} else {
|
|
|
|
/* An identifier */
|
|
|
|
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;
|
2000-08-01 21:36:45 +00:00
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
} while (C == '-');
|
|
|
|
Tok = TOK_ULABEL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
IVal = 0;
|
|
|
|
do {
|
|
|
|
++IVal;
|
2000-06-03 11:15:11 +00:00
|
|
|
NextChar ();
|
|
|
|
} while (C == '+');
|
2000-05-28 13:40:48 +00:00
|
|
|
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 ();
|
|
|
|
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 = ' ';
|
2000-06-03 11:15:11 +00:00
|
|
|
goto Again;
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\n':
|
|
|
|
NextChar ();
|
|
|
|
Tok = TOK_SEP;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case EOF:
|
|
|
|
/* Check if we have any open .IFs in this file */
|
|
|
|
CheckOpenIfs ();
|
2000-06-03 11:15:11 +00:00
|
|
|
/* Check if we have any open token lists in this file */
|
|
|
|
CheckInputStack ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we go here, we could not identify the current character. Skip it
|
|
|
|
* and try again.
|
|
|
|
*/
|
|
|
|
Error (ERR_INVALID_CHAR, 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
|
|
|
|
* uppercased in the process. The function returns the index of the keyword,
|
|
|
|
* or -1 if the keyword was not found.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
unsigned I;
|
|
|
|
|
|
|
|
/* Must have an identifier */
|
|
|
|
PRECONDITION (Tok == TOK_IDENT);
|
|
|
|
|
|
|
|
/* If we aren't in ignore case mode, we have to uppercase the identifier */
|
|
|
|
if (!IgnoreCase) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not found */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void InitScanner (const char* InFile)
|
|
|
|
/* Initialize the scanner, open the given input file */
|
|
|
|
{
|
|
|
|
/* Open the input file */
|
|
|
|
NewInputFile (InFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DoneScanner (void)
|
|
|
|
/* Release scanner resources */
|
|
|
|
{
|
|
|
|
DoneInputFile ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|