ref: 4f13a336b9610c5a58bc1a63a04220a89d07253b
dir: /src/asm/lexer.c/
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/rpn.h"
#include "asm/section.h"
#include "asm/warning.h"
#include "extern/err.h"
#include "asmy.h"
#include "platform.h" // strncasecmp, strdup
struct sLexString {
char *tzName;
uint32_t nToken;
uint32_t nNameLength;
struct sLexString *next;
};
#define pLexBufferRealStart (pCurrentBuffer->pBufferRealStart)
#define pLexBuffer (pCurrentBuffer->pBuffer)
#define AtLineStart (pCurrentBuffer->oAtLineStart)
#define SAFETYMARGIN 1024
#define BOM_SIZE 3
struct sLexFloat tLexFloat[32];
struct sLexString *tLexHash[LEXHASHSIZE];
YY_BUFFER_STATE pCurrentBuffer;
uint32_t nLexMaxLength; // max length of all keywords and operators
uint32_t tFloatingSecondChar[256];
uint32_t tFloatingFirstChar[256];
uint32_t tFloatingChars[256];
uint32_t nFloating;
enum eLexerState lexerstate = LEX_STATE_NORMAL;
struct sStringExpansionPos *pCurrentStringExpansion;
static unsigned int nNbStringExpansions;
/* UTF-8 byte order mark */
static const unsigned char bom[BOM_SIZE] = { 0xEF, 0xBB, 0xBF };
void upperstring(char *s)
{
while (*s) {
*s = toupper(*s);
s++;
}
}
void lowerstring(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
void yyskipbytes(uint32_t count)
{
pLexBuffer += count;
}
void yyunputbytes(uint32_t count)
{
pLexBuffer -= count;
}
void yyunput(char c)
{
if (pLexBuffer <= pLexBufferRealStart)
fatalerror("Buffer safety margin exceeded\n");
*(--pLexBuffer) = c;
}
void yyunputstr(const char *s)
{
int32_t len;
len = strlen(s);
/*
* It would be undefined behavior to subtract `len` from pLexBuffer and
* potentially have it point outside of pLexBufferRealStart's buffer,
* this is why the check is done this way.
* Refer to https://github.com/rednex/rgbds/pull/411#discussion_r319779797
*/
if (pLexBuffer - pLexBufferRealStart < len)
fatalerror("Buffer safety margin exceeded\n");
pLexBuffer -= len;
memcpy(pLexBuffer, s, len);
}
/*
* Marks that a new string expansion with name `tzName` ends here
* Enforces recursion depth
*/
void lex_BeginStringExpansion(const char *tzName)
{
if (++nNbStringExpansions > nMaxRecursionDepth)
fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth);
struct sStringExpansionPos *pNewStringExpansion =
malloc(sizeof(*pNewStringExpansion));
char *tzNewExpansionName = strdup(tzName);
if (!pNewStringExpansion || !tzNewExpansionName)
fatalerror("Could not allocate memory to expand '%s'\n", tzName);
pNewStringExpansion->tzName = tzNewExpansionName;
pNewStringExpansion->pBuffer = pLexBufferRealStart;
pNewStringExpansion->pBufferPos = pLexBuffer;
pNewStringExpansion->pParent = pCurrentStringExpansion;
pCurrentStringExpansion = pNewStringExpansion;
}
void yy_switch_to_buffer(YY_BUFFER_STATE buf)
{
pCurrentBuffer = buf;
}
void yy_set_state(enum eLexerState i)
{
lexerstate = i;
}
void yy_delete_buffer(YY_BUFFER_STATE buf)
{
free(buf->pBufferStart - SAFETYMARGIN);
free(buf);
}
/*
* Maintains the following invariants:
* 1. nBufferSize < capacity
* 2. The buffer is terminated with 0
* 3. nBufferSize is the size without the terminator
*/
static void yy_buffer_append(YY_BUFFER_STATE buf, size_t capacity, char c)
{
assert(buf->pBufferStart[buf->nBufferSize] == 0);
assert(buf->nBufferSize + 1 < capacity);
buf->pBufferStart[buf->nBufferSize++] = c;
buf->pBufferStart[buf->nBufferSize] = 0;
}
static void yy_buffer_append_newlines(YY_BUFFER_STATE buf, size_t capacity)
{
/* Add newline if file doesn't end with one */
if (buf->nBufferSize == 0
|| buf->pBufferStart[buf->nBufferSize - 1] != '\n')
yy_buffer_append(buf, capacity, '\n');
/* Add newline if \ will eat the last newline */
if (buf->nBufferSize >= 2) {
size_t pos = buf->nBufferSize - 2;
/* Skip spaces and tabs */
while (pos > 0 && (buf->pBufferStart[pos] == ' '
|| buf->pBufferStart[pos] == '\t'))
pos--;
if (buf->pBufferStart[pos] == '\\')
yy_buffer_append(buf, capacity, '\n');
}
}
YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size)
{
YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state));
if (pBuffer == NULL)
fatalerror("%s: Out of memory!\n", __func__);
size_t capacity = size + 3; /* space for 2 newlines and terminator */
pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN);
if (pBuffer->pBufferRealStart == NULL)
fatalerror("%s: Out of memory for buffer!\n", __func__);
pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
memcpy(pBuffer->pBuffer, mem, size);
pBuffer->pBuffer[size] = 0;
pBuffer->nBufferSize = size;
yy_buffer_append_newlines(pBuffer, capacity);
pBuffer->oAtLineStart = 1;
return pBuffer;
}
YY_BUFFER_STATE yy_create_buffer(FILE *f)
{
YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state));
if (pBuffer == NULL)
fatalerror("%s: Out of memory!\n", __func__);
size_t size = 0, capacity = -1;
char *buf = NULL;
/*
* Check if we can get the file size without implementation-defined
* behavior:
*
* From ftell(3p):
* [On error], ftell() and ftello() shall return −1, and set errno to
* indicate the error.
*
* The ftell() and ftello() functions shall fail if: [...]
* ESPIPE The file descriptor underlying stream is associated with a
* pipe, FIFO, or socket.
*
* From fseek(3p):
* The behavior of fseek() on devices which are incapable of seeking
* is implementation-defined.
*/
if (ftell(f) != -1) {
fseek(f, 0, SEEK_END);
capacity = ftell(f);
rewind(f);
}
// If ftell errored or the block above wasn't executed
if (capacity == -1)
capacity = 4096;
// Handle 0-byte files gracefully
else if (capacity == 0)
capacity = 1;
do {
if (buf == NULL || size >= capacity) {
if (buf)
capacity *= 2;
/* Give extra room for 2 newlines and terminator */
buf = realloc(buf, capacity + SAFETYMARGIN + 3);
if (buf == NULL)
fatalerror("%s: Out of memory for buffer!\n",
__func__);
}
char *bufpos = buf + SAFETYMARGIN + size;
size_t read_count = fread(bufpos, 1, capacity - size, f);
if (read_count == 0 && !feof(f))
fatalerror("%s: fread error\n", __func__);
size += read_count;
} while (!feof(f));
pBuffer->pBufferRealStart = buf;
pBuffer->pBufferStart = buf + SAFETYMARGIN;
pBuffer->pBuffer = buf + SAFETYMARGIN;
pBuffer->pBuffer[size] = 0;
pBuffer->nBufferSize = size;
/* This is added here to make the buffer scaling above easy to express,
* while taking the newline space into account
* for the yy_buffer_append_newlines() call below.
*/
capacity += 3;
/* Skip UTF-8 byte order mark. */
if (pBuffer->nBufferSize >= BOM_SIZE
&& !memcmp(pBuffer->pBuffer, bom, BOM_SIZE))
pBuffer->pBuffer += BOM_SIZE;
/* Convert all line endings to LF and spaces */
char *mem = pBuffer->pBuffer;
int32_t lineCount = 0;
while (*mem) {
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
mem += 2;
} else {
/* LF CR and CR LF */
if (((mem[0] == '\n') && (mem[1] == '\r'))
|| ((mem[0] == '\r') && (mem[1] == '\n'))) {
*mem++ = ' ';
*mem++ = '\n';
lineCount++;
/* LF and CR */
} else if ((mem[0] == '\n') || (mem[0] == '\r')) {
*mem++ = '\n';
lineCount++;
} else {
mem++;
}
}
}
if (mem != pBuffer->pBuffer + size) {
nLineNo = lineCount + 1;
fatalerror("Found null character\n");
}
/* Remove comments */
mem = pBuffer->pBuffer;
bool instring = false;
while (*mem) {
if (*mem == '\"')
instring = !instring;
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
mem += 2;
} else if (instring) {
mem++;
} else {
/* Comments that start with ; anywhere in a line */
if (*mem == ';') {
while (!((*mem == '\n') || (*mem == '\0')))
*mem++ = ' ';
/* Comments that start with * at the start of a line */
} else if ((mem[0] == '\n') && (mem[1] == '*')) {
warning(WARNING_OBSOLETE,
"'*' is deprecated for comments, please use ';' instead\n");
mem++;
while (!((*mem == '\n') || (*mem == '\0')))
*mem++ = ' ';
} else {
mem++;
}
}
}
yy_buffer_append_newlines(pBuffer, capacity);
pBuffer->oAtLineStart = 1;
return pBuffer;
}
uint32_t lex_FloatAlloc(const struct sLexFloat *token)
{
tLexFloat[nFloating] = *token;
return (1 << (nFloating++));
}
/*
* Make sure that only non-zero ASCII characters are used. Also, check if the
* start is greater than the end of the range.
*/
bool lex_CheckCharacterRange(uint16_t start, uint16_t end)
{
if (start > end || start < 1 || end > 127) {
error("Invalid character range (start: %" PRIu16 ", end: %" PRIu16 ")\n",
start, end);
return false;
}
return true;
}
void lex_FloatDeleteRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingChars[start] &= ~id;
start++;
}
}
}
void lex_FloatAddRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingChars[start] |= id;
start++;
}
}
}
void lex_FloatDeleteFirstRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingFirstChar[start] &= ~id;
start++;
}
}
}
void lex_FloatAddFirstRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingFirstChar[start] |= id;
start++;
}
}
}
void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingSecondChar[start] &= ~id;
start++;
}
}
}
void lex_FloatAddSecondRange(uint32_t id, uint16_t start, uint16_t end)
{
if (lex_CheckCharacterRange(start, end)) {
while (start <= end) {
tFloatingSecondChar[start] |= id;
start++;
}
}
}
static struct sLexFloat *lexgetfloat(uint32_t nFloatMask)
{
if (nFloatMask == 0)
fatalerror("Internal error in %s\n", __func__);
int32_t i = 0;
while ((nFloatMask & 1) == 0) {
nFloatMask >>= 1;
i++;
}
return &tLexFloat[i];
}
static uint32_t lexcalchash(char *s)
{
uint32_t hash = 0;
while (*s)
hash = (hash * 283) ^ toupper(*s++);
return hash % LEXHASHSIZE;
}
void lex_Init(void)
{
uint32_t i;
for (i = 0; i < LEXHASHSIZE; i++)
tLexHash[i] = NULL;
for (i = 0; i < 256; i++) {
tFloatingFirstChar[i] = 0;
tFloatingSecondChar[i] = 0;
tFloatingChars[i] = 0;
}
nLexMaxLength = 0;
nFloating = 0;
pCurrentStringExpansion = NULL;
nNbStringExpansions = 0;
}
void lex_AddStrings(const struct sLexInitString *lex)
{
while (lex->tzName) {
struct sLexString **ppHash;
uint32_t hash = lexcalchash(lex->tzName);
ppHash = &tLexHash[hash];
while (*ppHash)
ppHash = &((*ppHash)->next);
*ppHash = malloc(sizeof(struct sLexString));
if (*ppHash == NULL)
fatalerror("Out of memory!\n");
(*ppHash)->tzName = (char *)strdup(lex->tzName);
if ((*ppHash)->tzName == NULL)
fatalerror("Out of memory!\n");
(*ppHash)->nNameLength = strlen(lex->tzName);
(*ppHash)->nToken = lex->nToken;
(*ppHash)->next = NULL;
upperstring((*ppHash)->tzName);
if ((*ppHash)->nNameLength > nLexMaxLength)
nLexMaxLength = (*ppHash)->nNameLength;
lex++;
}
}
/*
* Gets the "float" mask and "float" length.
* "Float" refers to the token type of a token that is not a keyword.
* The character classes floatingFirstChar, floatingSecondChar, and
* floatingChars are defined separately for each token type.
* It uses bit masks to match against a set of simple regular expressions
* of the form /[floatingFirstChar]([floatingSecondChar][floatingChars]*)?/.
* The token types with the longest match from the current position in the
* buffer will have their bits set in the float mask.
*/
void yylex_GetFloatMaskAndFloatLen(uint32_t *pnFloatMask, uint32_t *pnFloatLen)
{
/*
* Note that '\0' should always have a bit mask of 0 in the "floating"
* tables, so it doesn't need to be checked for separately.
*/
char *s = pLexBuffer;
uint32_t nOldFloatMask = 0;
uint32_t nFloatMask = tFloatingFirstChar[(uint8_t)*s];
if (nFloatMask != 0) {
s++;
nOldFloatMask = nFloatMask;
nFloatMask &= tFloatingSecondChar[(uint8_t)*s];
while (nFloatMask != 0) {
s++;
nOldFloatMask = nFloatMask;
nFloatMask &= tFloatingChars[(uint8_t)*s];
}
}
*pnFloatMask = nOldFloatMask;
*pnFloatLen = (uint32_t)(s - pLexBuffer);
}
/*
* Gets the longest keyword/operator from the current position in the buffer.
*/
struct sLexString *yylex_GetLongestFixed(void)
{
struct sLexString *pLongestFixed = NULL;
char *s = pLexBuffer;
uint32_t hash = 0;
uint32_t length = 0;
while (length < nLexMaxLength && *s) {
hash = (hash * 283) ^ toupper(*s);
s++;
length++;
struct sLexString *lex = tLexHash[hash % LEXHASHSIZE];
while (lex) {
if (lex->nNameLength == length
&& strncasecmp(pLexBuffer, lex->tzName, length) == 0) {
pLongestFixed = lex;
break;
}
lex = lex->next;
}
}
return pLongestFixed;
}
size_t CopyMacroArg(char *dest, size_t maxLength, char c)
{
size_t i;
char const *s;
if (c == '@')
s = macro_GetUniqueIDStr();
else if (c >= '1' && c <= '9')
s = macro_GetArg(c - '0');
else
return 0;
if (s == NULL)
fatalerror("Macro argument '\\%c' not defined\n", c);
// TODO: `strncpy`, nay?
for (i = 0; s[i] != 0; i++) {
if (i >= maxLength)
fatalerror("Macro argument too long to fit buffer\n");
dest[i] = s[i];
}
return i;
}
static inline void yylex_StringWriteChar(char *s, size_t index, char c)
{
if (index >= MAXSTRLEN)
fatalerror("String too long\n");
s[index] = c;
}
static inline void yylex_SymbolWriteChar(char *s, size_t index, char c)
{
if (index >= MAXSYMLEN)
fatalerror("Symbol too long\n");
s[index] = c;
}
/*
* Trims white space at the end of a string.
* The index parameter is the index of the 0 at the end of the string.
*/
void yylex_TrimEnd(char *s, size_t index)
{
int32_t i = (int32_t)index - 1;
while ((i >= 0) && (s[i] == ' ' || s[i] == '\t')) {
s[i] = 0;
i--;
}
}
size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
{
char sym[MAXSYMLEN + 1];
char ch;
size_t i = 0;
size_t length, maxLength;
const char *mode = NULL;
for (ch = *pLexBuffer;
ch != '}' && ch != '"' && ch != '\n';
ch = *(++pLexBuffer)) {
if (ch == '\\') {
ch = *(++pLexBuffer);
maxLength = MAXSYMLEN - i;
length = CopyMacroArg(&sym[i], maxLength, ch);
if (length != 0)
i += length;
else
fatalerror("Illegal character escape '%c'\n", ch);
} else if (ch == '{') {
/* Handle nested symbols */
++pLexBuffer;
i += yylex_ReadBracketedSymbol(sym, i);
--pLexBuffer;
} else if (ch == ':' && !mode) { /* Only grab 1st colon */
/* Use a whitelist of modes, which does prevent the
* use of some features such as precision,
* but also avoids a security flaw
*/
const char *acceptedModes = "bxXd";
/* Binary isn't natively supported,
* so it's handled differently
*/
static const char * const formatSpecifiers[] = {
"", "%" PRIx32, "%" PRIX32, "%" PRId32
};
/* Prevent reading out of bounds! */
const char *designatedMode;
if (i != 1)
fatalerror("Print types are exactly 1 character long\n");
designatedMode = strchr(acceptedModes, sym[i - 1]);
if (!designatedMode)
fatalerror("Illegal print type '%c'\n", sym[i - 1]);
mode = formatSpecifiers[designatedMode - acceptedModes];
/* Begin writing the symbol again */
i = 0;
} else {
yylex_SymbolWriteChar(sym, i++, ch);
}
}
/* Properly terminate the string */
yylex_SymbolWriteChar(sym, i, 0);
/* It's assumed we're writing to a T_STRING */
maxLength = MAXSTRLEN - index;
length = symvaluetostring(&dest[index], maxLength, sym, mode);
if (*pLexBuffer == '}')
pLexBuffer++;
else
fatalerror("Missing }\n");
return length;
}
static void yylex_ReadQuotedString(void)
{
size_t index = 0;
size_t length, maxLength;
while (*pLexBuffer != '"' && *pLexBuffer != '\n') {
char ch = *pLexBuffer++;
if (ch == '\\') {
ch = *pLexBuffer++;
switch (ch) {
case 'n':
ch = '\n';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case '\\':
ch = '\\';
break;
case '"':
ch = '"';
break;
case ',':
ch = ',';
break;
case '{':
ch = '{';
break;
case '}':
ch = '}';
break;
default:
maxLength = MAXSTRLEN - index;
length = CopyMacroArg(&yylval.tzString[index],
maxLength, ch);
if (length != 0)
index += length;
else
fatalerror("Illegal character escape '%c'\n", ch);
ch = 0;
break;
}
} else if (ch == '{') {
// Get bracketed symbol within string.
index += yylex_ReadBracketedSymbol(yylval.tzString,
index);
ch = 0;
}
if (ch)
yylex_StringWriteChar(yylval.tzString, index++, ch);
}
yylex_StringWriteChar(yylval.tzString, index, 0);
if (*pLexBuffer == '"')
pLexBuffer++;
else
fatalerror("Unterminated string\n");
}
static uint32_t yylex_NORMAL(void)
{
struct sLexString *pLongestFixed = NULL;
uint32_t nFloatMask, nFloatLen;
uint32_t linestart = AtLineStart;
AtLineStart = 0;
scanagain:
while (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
linestart = 0;
pLexBuffer++;
}
if (*pLexBuffer == 0) {
// Reached the end of a file, macro, or rept.
if (yywrap() == 0) {
linestart = AtLineStart;
AtLineStart = 0;
goto scanagain;
}
}
/* Check for line continuation character */
if (*pLexBuffer == '\\') {
/*
* Look for line continuation character after a series of
* spaces. This is also useful for files that use Windows line
* endings: "\r\n" is replaced by " \n" before the lexer has the
* opportunity to see it.
*/
if (pLexBuffer[1] == ' ' || pLexBuffer[1] == '\t') {
pLexBuffer += 2;
while (1) {
if (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
pLexBuffer++;
} else if (*pLexBuffer == '\n') {
pLexBuffer++;
nLineNo++;
goto scanagain;
} else {
error("Expected a new line after the continuation character.\n");
pLexBuffer++;
}
}
}
/* Line continuation character */
if (pLexBuffer[1] == '\n') {
pLexBuffer += 2;
nLineNo++;
goto scanagain;
}
/*
* If there isn't a newline character or a space, ignore the
* character '\'. It will eventually be handled by other
* functions like PutMacroArg().
*/
}
/*
* Try to match an identifier, macro argument (e.g. \1),
* or numeric literal.
*/
yylex_GetFloatMaskAndFloatLen(&nFloatMask, &nFloatLen);
/* Try to match a keyword or operator. */
pLongestFixed = yylex_GetLongestFixed();
if (nFloatLen == 0 && pLongestFixed == NULL) {
/*
* No keyword, identifier, operator, or numerical literal
* matches.
*/
if (*pLexBuffer == '"') {
pLexBuffer++;
yylex_ReadQuotedString();
return T_STRING;
} else if (*pLexBuffer == '{') {
pLexBuffer++;
size_t len = yylex_ReadBracketedSymbol(yylval.tzString,
0);
yylval.tzString[len] = 0;
return T_STRING;
}
/*
* It's not a keyword, operator, identifier, macro argument,
* numeric literal, string, or bracketed symbol, so just return
* the ASCII character.
*/
unsigned char ch = *pLexBuffer++;
if (ch == '\n')
AtLineStart = 1;
/*
* Check for invalid unprintable characters.
* They may not be readily apparent in a text editor,
* so this is useful for identifying encoding problems.
*/
if (ch != 0
&& ch != '\n'
&& !(ch >= 0x20 && ch <= 0x7E))
fatalerror("Found garbage character: 0x%02X\n", ch);
return ch;
}
if (pLongestFixed == NULL || nFloatLen > pLongestFixed->nNameLength) {
/*
* Longest match was an identifier, macro argument, or numeric
* literal.
*/
struct sLexFloat *token = lexgetfloat(nFloatMask);
if (token->Callback) {
int32_t done = token->Callback(pLexBuffer, nFloatLen);
if (!done)
goto scanagain;
}
uint32_t type = token->nToken;
if (type == T_ID && strchr(yylval.tzSym, '.'))
type = T_LOCAL_ID;
if (linestart && type == T_ID)
return T_LABEL;
return type;
}
/* Longest match was a keyword or operator. */
pLexBuffer += pLongestFixed->nNameLength;
yylval.nConstValue = pLongestFixed->nToken;
return pLongestFixed->nToken;
}
static uint32_t yylex_MACROARGS(void)
{
size_t index = 0;
size_t length, maxLength;
while ((*pLexBuffer == ' ') || (*pLexBuffer == '\t'))
pLexBuffer++;
while ((*pLexBuffer != ',') && (*pLexBuffer != '\n')) {
char ch = *pLexBuffer++;
if (ch == '\\') {
ch = *pLexBuffer++;
switch (ch) {
case 'n':
ch = '\n';
break;
case 't':
ch = '\t';
break;
case '\\':
ch = '\\';
break;
case '"':
ch = '\"';
break;
case ',':
ch = ',';
break;
case '{':
ch = '{';
break;
case '}':
ch = '}';
break;
case ' ':
case '\t':
/*
* Look for line continuation character after a
* series of spaces. This is also useful for
* files that use Windows line endings: "\r\n"
* is replaced by " \n" before the lexer has the
* opportunity to see it.
*/
while (1) {
if (*pLexBuffer == ' '
|| *pLexBuffer == '\t') {
pLexBuffer++;
} else if (*pLexBuffer == '\n') {
pLexBuffer++;
nLineNo++;
ch = 0;
break;
} else {
error("Expected a new line after the continuation character.\n");
}
}
break;
case '\n':
/* Line continuation character */
nLineNo++;
ch = 0;
break;
default:
maxLength = MAXSTRLEN - index;
length = CopyMacroArg(&yylval.tzString[index],
maxLength, ch);
if (length != 0)
index += length;
else
fatalerror("Illegal character escape '%c'\n", ch);
ch = 0;
break;
}
} else if (ch == '{') {
index += yylex_ReadBracketedSymbol(yylval.tzString,
index);
ch = 0;
}
if (ch)
yylex_StringWriteChar(yylval.tzString, index++, ch);
}
if (index) {
yylex_StringWriteChar(yylval.tzString, index, 0);
/* trim trailing white space at the end of the line */
if (*pLexBuffer == '\n')
yylex_TrimEnd(yylval.tzString, index);
return T_STRING;
} else if (*pLexBuffer == '\n') {
pLexBuffer++;
AtLineStart = 1;
return '\n';
} else if (*pLexBuffer == ',') {
pLexBuffer++;
return ',';
}
fatalerror("Internal error in %s\n", __func__);
}
int yylex(void)
{
int returnedChar;
switch (lexerstate) {
case LEX_STATE_NORMAL:
returnedChar = yylex_NORMAL();
break;
case LEX_STATE_MACROARGS:
returnedChar = yylex_MACROARGS();
break;
default:
fatalerror("%s: Internal error.\n", __func__);
}
/* Check if string expansions were fully read */
while (pCurrentStringExpansion
&& pCurrentStringExpansion->pBuffer == pLexBufferRealStart
&& pCurrentStringExpansion->pBufferPos <= pLexBuffer) {
struct sStringExpansionPos *pParent =
pCurrentStringExpansion->pParent;
free(pCurrentStringExpansion->tzName);
free(pCurrentStringExpansion);
pCurrentStringExpansion = pParent;
nNbStringExpansions--;
}
return returnedChar;
}