1
0
mirror of https://github.com/cc65/cc65.git synced 2024-09-30 08:57:49 +00:00
cc65/src/da65/scanner.c
2014-03-04 01:11:19 +01:00

536 lines
13 KiB
C

/*****************************************************************************/
/* */
/* scanner.c */
/* */
/* Configuration file scanner for the da65 disassembler */
/* */
/* */
/* */
/* (C) 2000-2005 Ullrich von Bassewitz */
/* Römerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* 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 <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* common */
#include "chartype.h"
#include "xsprintf.h"
/* ld65 */
#include "global.h"
#include "error.h"
#include "scanner.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Current token and attributes */
unsigned InfoTok;
char InfoSVal [CFG_MAX_IDENT_LEN+1];
long InfoIVal;
/* Error location */
unsigned InfoErrorLine;
unsigned InfoErrorCol;
/* Input sources for the configuration */
static const char* InfoFile = 0;
/* Other input stuff */
static int C = ' ';
static unsigned InputLine = 1;
static unsigned InputCol = 0;
static FILE* InputFile = 0;
/*****************************************************************************/
/* Error handling */
/*****************************************************************************/
void InfoWarning (const char* Format, ...)
/* Print a warning message adding file name and line number of the config file */
{
char Buf [512];
va_list ap;
va_start (ap, Format);
xvsprintf (Buf, sizeof (Buf), Format, ap);
va_end (ap);
Warning ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
}
void InfoError (const char* Format, ...)
/* Print an error message adding file name and line number of the config file */
{
char Buf [512];
va_list ap;
va_start (ap, Format);
xvsprintf (Buf, sizeof (Buf), Format, ap);
va_end (ap);
Error ("%s(%u): %s", InfoFile, InfoErrorLine, Buf);
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static void NextChar (void)
/* Read the next character from the input file */
{
/* Read from the file */
C = getc (InputFile);
/* Count columns */
if (C != EOF) {
++InputCol;
}
/* Count lines */
if (C == '\n') {
++InputLine;
InputCol = 0;
}
}
static unsigned DigitVal (int C)
/* Return the value for a numeric digit */
{
if (IsDigit (C)) {
return C - '0';
} else {
return toupper (C) - 'A' + 10;
}
}
void InfoNextTok (void)
/* Read the next token from the input stream */
{
unsigned I;
int Esc;
Again:
/* Skip whitespace */
while (IsSpace (C)) {
NextChar ();
}
/* Remember the current position */
InfoErrorLine = InputLine;
InfoErrorCol = InputCol;
/* Identifier? */
if (C == '_' || IsAlpha (C)) {
/* Read the identifier */
I = 0;
while (C == '_' || IsAlNum (C)) {
if (I < CFG_MAX_IDENT_LEN) {
InfoSVal [I++] = C;
}
NextChar ();
}
InfoSVal [I] = '\0';
InfoTok = INFOTOK_IDENT;
return;
}
/* Hex number? */
if (C == '$') {
NextChar ();
if (!IsXDigit (C)) {
InfoError ("Hex digit expected");
}
InfoIVal = 0;
while (IsXDigit (C)) {
InfoIVal = InfoIVal * 16 + DigitVal (C);
NextChar ();
}
InfoTok = INFOTOK_INTCON;
return;
}
/* Decimal number? */
if (IsDigit (C)) {
InfoIVal = 0;
while (IsDigit (C)) {
InfoIVal = InfoIVal * 10 + DigitVal (C);
NextChar ();
}
InfoTok = INFOTOK_INTCON;
return;
}
/* Other characters */
switch (C) {
case '{':
NextChar ();
InfoTok = INFOTOK_LCURLY;
break;
case '}':
NextChar ();
InfoTok = INFOTOK_RCURLY;
break;
case ';':
NextChar ();
InfoTok = INFOTOK_SEMI;
break;
case '.':
NextChar ();
InfoTok = INFOTOK_DOT;
break;
case ',':
NextChar ();
InfoTok = INFOTOK_COMMA;
break;
case '=':
NextChar ();
InfoTok = INFOTOK_EQ;
break;
case ':':
NextChar ();
InfoTok = INFOTOK_COLON;
break;
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);
}
}
if (I < CFG_MAX_IDENT_LEN) {
InfoSVal [I++] = C;
}
NextChar ();
}
NextChar ();
InfoSVal [I] = '\0';
InfoTok = INFOTOK_STRCON;
break;
case '\'':
NextChar ();
if (C == EOF || IsControl (C)) {
InfoError ("Invalid character constant");
}
InfoIVal = C;
NextChar ();
if (C != '\'') {
InfoError ("Unterminated character constant");
}
NextChar ();
InfoTok = INFOTOK_CHARCON;
break;
case '#':
/* Comment */
while (C != '\n' && C != EOF) {
NextChar ();
}
if (C != EOF) {
goto Again;
}
InfoTok = INFOTOK_EOF;
break;
case EOF:
InfoTok = INFOTOK_EOF;
break;
default:
InfoError ("Invalid character `%c'", C);
}
}
void InfoConsume (unsigned T, const char* Msg)
/* Skip a token, print an error message if not found */
{
if (InfoTok != T) {
InfoError (Msg);
}
InfoNextTok ();
}
void InfoConsumeLCurly (void)
/* Consume a left curly brace */
{
InfoConsume (INFOTOK_LCURLY, "`{' expected");
}
void InfoConsumeRCurly (void)
/* Consume a right curly brace */
{
InfoConsume (INFOTOK_RCURLY, "`}' expected");
}
void InfoConsumeSemi (void)
/* Consume a semicolon */
{
InfoConsume (INFOTOK_SEMI, "`;' expected");
}
void InfoConsumeColon (void)
/* Consume a colon */
{
InfoConsume (INFOTOK_COLON, "`:' expected");
}
void InfoOptionalComma (void)
/* Consume a comma if there is one */
{
if (InfoTok == INFOTOK_COMMA) {
InfoNextTok ();
}
}
void InfoOptionalAssign (void)
/* Consume an equal sign if there is one */
{
if (InfoTok == INFOTOK_EQ) {
InfoNextTok ();
}
}
void InfoAssureInt (void)
/* Make sure the next token is an integer */
{
if (InfoTok != INFOTOK_INTCON) {
InfoError ("Integer constant expected");
}
}
void InfoAssureStr (void)
/* Make sure the next token is a string constant */
{
if (InfoTok != INFOTOK_STRCON) {
InfoError ("String constant expected");
}
}
void InfoAssureChar (void)
/* Make sure the next token is a char constant */
{
if (InfoTok != INFOTOK_STRCON) {
InfoError ("Character constant expected");
}
}
void InfoAssureIdent (void)
/* Make sure the next token is an identifier */
{
if (InfoTok != INFOTOK_IDENT) {
InfoError ("Identifier expected");
}
}
void InfoRangeCheck (long Lo, long Hi)
/* Check the range of InfoIVal */
{
if (InfoIVal < Lo || InfoIVal > Hi) {
InfoError ("Range error");
}
}
void InfoSpecialToken (const IdentTok* Table, unsigned Size, const char* Name)
/* Map an identifier to one of the special tokens in the table */
{
unsigned I;
/* We need an identifier */
if (InfoTok == INFOTOK_IDENT) {
/* Make it upper case */
I = 0;
while (InfoSVal [I]) {
InfoSVal [I] = toupper (InfoSVal [I]);
++I;
}
/* Linear search */
for (I = 0; I < Size; ++I) {
if (strcmp (InfoSVal, Table [I].Ident) == 0) {
InfoTok = Table [I].Tok;
return;
}
}
}
/* Not found or no identifier */
InfoError ("%s expected", Name);
}
void InfoBoolToken (void)
/* Map an identifier or integer to a boolean token */
{
static const IdentTok Booleans [] = {
{ "YES", INFOTOK_TRUE },
{ "NO", INFOTOK_FALSE },
{ "TRUE", INFOTOK_TRUE },
{ "FALSE", INFOTOK_FALSE },
{ "ON", INFOTOK_TRUE },
{ "OFF", INFOTOK_FALSE },
};
/* If we have an identifier, map it to a boolean token */
if (InfoTok == INFOTOK_IDENT) {
InfoSpecialToken (Booleans, ENTRY_COUNT (Booleans), "Boolean");
} else {
/* We expected an integer here */
if (InfoTok != INFOTOK_INTCON) {
InfoError ("Boolean value expected");
}
InfoTok = (InfoIVal == 0)? INFOTOK_FALSE : INFOTOK_TRUE;
}
}
void InfoSetName (const char* Name)
/* Set a name for a config file */
{
InfoFile = Name;
}
const char* InfoGetName (void)
/* Get the name of the config file */
{
return InfoFile? InfoFile : "";
}
int InfoAvail ()
/* Return true if we have an info file given */
{
return (InfoFile != 0);
}
void InfoOpenInput (void)
/* Open the input file */
{
/* Open the file */
InputFile = fopen (InfoFile, "r");
if (InputFile == 0) {
Error ("Cannot open `%s': %s", InfoFile, strerror (errno));
}
/* Initialize variables */
C = ' ';
InputLine = 1;
InputCol = 0;
/* Start the ball rolling ... */
InfoNextTok ();
}
void InfoCloseInput (void)
/* Close the input file if we have one */
{
/* Close the input file if we had one */
if (InputFile) {
(void) fclose (InputFile);
InputFile = 0;
}
}