mirror of
https://github.com/cc65/cc65.git
synced 2024-12-28 06:30:16 +00:00
Rewrote most of the #pragma parsing code. I'm still not satisfied, but at
least, it's a bit better than before. #pragma warn (...) is now used to switch single warnings instead of a global on/off switch. git-svn-id: svn://svn.cc65.org/cc65/trunk@4362 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
583b9f8fcc
commit
a1fb355a9a
@ -731,7 +731,7 @@ If the first parameter is <tt/push/, the old value is saved onto a stack
|
||||
before changing it. The value may later be restored by using the <tt/pop/
|
||||
parameter with the <tt/#pragma/.
|
||||
|
||||
<sect1><tt>#pragma bssseg ([push,]<name>)</tt><p>
|
||||
<sect1><tt>#pragma bssseg ([push,] <name>)</tt><p>
|
||||
|
||||
This pragma changes the name used for the BSS segment (the BSS segment
|
||||
is used to store uninitialized data). The argument is a string enclosed
|
||||
@ -787,7 +787,7 @@ parameter with the <tt/#pragma/.
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1><tt>#pragma checkstack ([push,]on|off)</tt><label id="pragma-checkstack"><p>
|
||||
<sect1><tt>#pragma checkstack ([push,] on|off)</tt><label id="pragma-checkstack"><p>
|
||||
|
||||
Tells the compiler to insert calls to a stack checking subroutine to detect
|
||||
stack overflows. The stack checking code will lead to somewhat larger and
|
||||
@ -800,7 +800,7 @@ parameter with the <tt/#pragma/.
|
||||
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
<sect1><tt>#pragma codeseg ([push,]<name>)</tt><p>
|
||||
<sect1><tt>#pragma codeseg ([push,] <name>)</tt><p>
|
||||
|
||||
This pragma changes the name used for the CODE segment (the CODE segment
|
||||
is used to store executable code). The argument is a string enclosed in
|
||||
@ -818,7 +818,7 @@ parameter with the <tt/#pragma/.
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1><tt>#pragma codesize ([push,]<int>)</tt><label id="pragma-codesize"><p>
|
||||
<sect1><tt>#pragma codesize ([push,] <int>)</tt><label id="pragma-codesize"><p>
|
||||
|
||||
This pragma allows finer control about speed vs. size decisions in the code
|
||||
generation and optimization phase. It gives the allowed size increase factor
|
||||
@ -828,7 +828,7 @@ parameter with the <tt/#pragma/.
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
|
||||
<sect1><tt>#pragma dataseg ([push,]<name>)</tt><p>
|
||||
<sect1><tt>#pragma dataseg ([push,] <name>)</tt><p>
|
||||
|
||||
This pragma changes the name used for the DATA segment (the DATA segment
|
||||
is used to store initialized data). The argument is a string enclosed in
|
||||
@ -846,7 +846,7 @@ parameter with the <tt/#pragma/.
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1><tt>#pragma optimize ([push,]on|off)</tt><label id="pragma-optimize"><p>
|
||||
<sect1><tt>#pragma optimize ([push,] on|off)</tt><label id="pragma-optimize"><p>
|
||||
|
||||
Switch optimization on or off. If the argument is "off", optimization is
|
||||
disabled, otherwise it is enabled. Please note that this pragma only effects
|
||||
@ -862,7 +862,7 @@ parameter with the <tt/#pragma/.
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
|
||||
<sect1><tt>#pragma rodataseg ([push,]<name>)</tt><p>
|
||||
<sect1><tt>#pragma rodataseg ([push,] <name>)</tt><p>
|
||||
|
||||
This pragma changes the name used for the RODATA segment (the RODATA
|
||||
segment is used to store readonly data). The argument is a string
|
||||
@ -880,7 +880,7 @@ parameter with the <tt/#pragma/.
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1><tt>#pragma regvaraddr ([push,]on|off)</tt><p>
|
||||
<sect1><tt>#pragma regvaraddr ([push,] on|off)</tt><p>
|
||||
|
||||
The compiler does not allow to take the address of register variables.
|
||||
The regvaraddr pragma changes this. Taking the address of a register
|
||||
@ -904,7 +904,7 @@ parameter with the <tt/#pragma/.
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<sect1><tt>#pragma regvars ([push,]on|off)</tt><label id="pragma-regvars"><p>
|
||||
<sect1><tt>#pragma regvars ([push,] on|off)</tt><label id="pragma-regvars"><p>
|
||||
|
||||
Enables or disables use of register variables. If register variables are
|
||||
disabled (the default), the <tt/register/ keyword is ignored. Register
|
||||
@ -914,7 +914,7 @@ parameter with the <tt/#pragma/.
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
|
||||
<sect1><tt>#pragma signedchars ([push,]on|off)</tt><label id="pragma-signedchars"><p>
|
||||
<sect1><tt>#pragma signedchars ([push,] on|off)</tt><label id="pragma-signedchars"><p>
|
||||
|
||||
Changes the signedness of the default character type. If the argument is
|
||||
"on", default characters are signed, otherwise characters are unsigned.
|
||||
@ -925,7 +925,7 @@ parameter with the <tt/#pragma/.
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
|
||||
<sect1><tt>#pragma staticlocals ([push,]on|off)</tt><label id="pragma-staticlocals"<p>
|
||||
<sect1><tt>#pragma staticlocals ([push,] on|off)</tt><label id="pragma-staticlocals"<p>
|
||||
|
||||
Use variables in the bss segment instead of variables on the stack. This
|
||||
pragma changes the default set by the compiler option <tt/-Cl/. If the
|
||||
@ -935,13 +935,13 @@ parameter with the <tt/#pragma/.
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
|
||||
|
||||
<sect1><tt>#pragma warn ([push,]on|off)</tt><label id="pragma-warn"><p>
|
||||
<sect1><tt>#pragma warn (name, [push,] on|off)</tt><label id="pragma-warn"><p>
|
||||
|
||||
Switch compiler warnings on or off. If the argument is "off", warnings are
|
||||
disabled, otherwise they're enabled. The default is "on", but may be changed
|
||||
with the <tt/<ref name="-W" id="option-W">/ compiler option.
|
||||
|
||||
The <tt/#pragma/ understands the push and pop parameters as explained above.
|
||||
Switch compiler warnings on or off. "name" is the name of a warning (see the
|
||||
<tt/<ref name="-W" id="option-W">/ compiler option for a list). The name is
|
||||
either followed by "pop", which restores the last pushed state, or by "on" or
|
||||
"off", optionally preceeded by "push" to push the current state before
|
||||
changing it.
|
||||
|
||||
|
||||
<sect1><tt>#pragma zpsym (<name>)</tt><p>
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/* common */
|
||||
#include "chartype.h"
|
||||
#include "segnames.h"
|
||||
#include "tgttrans.h"
|
||||
|
||||
@ -84,9 +85,9 @@ static const struct Pragma {
|
||||
const char* Key; /* Keyword */
|
||||
pragma_t Tok; /* Token */
|
||||
} Pragmas[PR_COUNT] = {
|
||||
{ "bssseg", PR_BSSSEG },
|
||||
{ "bssseg", PR_BSSSEG },
|
||||
{ "charmap", PR_CHARMAP },
|
||||
{ "checkstack", PR_CHECKSTACK },
|
||||
{ "checkstack", PR_CHECKSTACK },
|
||||
{ "codeseg", PR_CODESEG },
|
||||
{ "codesize", PR_CODESIZE },
|
||||
{ "dataseg", PR_DATASEG },
|
||||
@ -94,16 +95,24 @@ static const struct Pragma {
|
||||
{ "regvaraddr", PR_REGVARADDR },
|
||||
{ "regvars", PR_REGVARS },
|
||||
{ "rodataseg", PR_RODATASEG },
|
||||
{ "signedchars", PR_SIGNEDCHARS },
|
||||
{ "staticlocals", PR_STATICLOCALS },
|
||||
{ "signedchars", PR_SIGNEDCHARS },
|
||||
{ "staticlocals", PR_STATICLOCALS },
|
||||
{ "warn", PR_WARN },
|
||||
{ "zpsym", PR_ZPSYM },
|
||||
};
|
||||
|
||||
/* Result of ParsePushPop */
|
||||
typedef enum {
|
||||
PP_NONE,
|
||||
PP_POP,
|
||||
PP_PUSH,
|
||||
PP_ERROR,
|
||||
} PushPopResult;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/* Helper functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@ -127,29 +136,214 @@ static int CmpKey (const void* Key, const void* Elem)
|
||||
|
||||
|
||||
|
||||
static pragma_t FindPragma (const char* Key)
|
||||
static pragma_t FindPragma (const StrBuf* Key)
|
||||
/* Find a pragma and return the token. Return PR_ILLEGAL if the keyword is
|
||||
* not a valid pragma.
|
||||
*/
|
||||
{
|
||||
struct Pragma* P;
|
||||
P = bsearch (Key, Pragmas, PR_COUNT, sizeof (Pragmas[0]), CmpKey);
|
||||
P = bsearch (SB_GetConstBuf (Key), Pragmas, PR_COUNT, sizeof (Pragmas[0]), CmpKey);
|
||||
return P? P->Tok : PR_ILLEGAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GetComma (StrBuf* B)
|
||||
/* Expects and skips a comma in B. Prints an error and returns zero if no
|
||||
* comma is found. Return a value <> 0 otherwise.
|
||||
*/
|
||||
{
|
||||
SB_SkipWhite (B);
|
||||
if (SB_Get (B) != ',') {
|
||||
Error ("Comma expected");
|
||||
return 0;
|
||||
}
|
||||
SB_SkipWhite (B);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GetString (StrBuf* B, StrBuf* S)
|
||||
/* Expects and skips a string in B. Prints an error and returns zero if no
|
||||
* string is found. Returns a value <> 0 otherwise.
|
||||
*/
|
||||
{
|
||||
if (!SB_GetString (B, S)) {
|
||||
Error ("String literal expected");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int GetNumber (StrBuf* B, long* Val)
|
||||
/* Expects and skips a number in B. Prints an eror and returns zero if no
|
||||
* number is found. Returns a value <> 0 otherwise.
|
||||
*/
|
||||
{
|
||||
if (!SB_GetNumber (B, Val)) {
|
||||
Error ("Constant integer expected");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static IntStack* GetWarning (StrBuf* B)
|
||||
/* Get a warning name from the string buffer. Returns a pointer to the intstack
|
||||
* that holds the state of the warning, and NULL in case of errors. The
|
||||
* function will output error messages in case of problems.
|
||||
*/
|
||||
{
|
||||
IntStack* S = 0;
|
||||
StrBuf W = AUTO_STRBUF_INITIALIZER;
|
||||
|
||||
/* The warning name is a symbol but the '-' char is allowed within */
|
||||
if (SB_GetSym (B, &W, "-")) {
|
||||
|
||||
/* Map the warning name to an IntStack that contains its state */
|
||||
S = FindWarning (SB_GetConstBuf (&W));
|
||||
|
||||
/* Handle errors */
|
||||
if (S == 0) {
|
||||
Error ("Pragma expects a warning name as first argument");
|
||||
}
|
||||
}
|
||||
|
||||
/* Deallocate the string */
|
||||
SB_Done (&W);
|
||||
|
||||
/* Done */
|
||||
return S;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int HasStr (StrBuf* B, const char* E)
|
||||
/* Checks if E follows in B. If so, skips it and returns true */
|
||||
{
|
||||
unsigned Len = strlen (E);
|
||||
if (SB_GetLen (B) - SB_GetIndex (B) >= Len) {
|
||||
if (strncmp (SB_GetConstBuf (B) + SB_GetIndex (B), E, Len) == 0) {
|
||||
/* Found */
|
||||
SB_SkipMultiple (B, Len);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PushPopResult ParsePushPop (StrBuf* B)
|
||||
/* Check for and parse the "push" and "pop" keywords. In case of "push", a
|
||||
* following comma is expected and skipped.
|
||||
*/
|
||||
{
|
||||
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
|
||||
PushPopResult Res = PP_NONE;
|
||||
|
||||
|
||||
/* Try to read an identifier */
|
||||
if (SB_GetSym (B, &Ident, 0)) {
|
||||
|
||||
/* Check if we have a first argument named "pop" */
|
||||
if (SB_CompareStr (&Ident, "pop") == 0) {
|
||||
|
||||
Res = PP_POP;
|
||||
|
||||
/* Check if we have a first argument named "push" */
|
||||
} else if (SB_CompareStr (&Ident, "push") == 0) {
|
||||
|
||||
Res = PP_PUSH;
|
||||
|
||||
/* Skip the following comma */
|
||||
if (!GetComma (B)) {
|
||||
Res = PP_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Unknown keyword */
|
||||
Error ("Invalid pragma arguments");
|
||||
Res = PP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the string buffer and return the result */
|
||||
SB_Done (&Ident);
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void PopInt (IntStack* S)
|
||||
/* Pops an integer from an IntStack. Prints an error if the stack is empty */
|
||||
{
|
||||
if (IS_GetCount (S) < 2) {
|
||||
Error ("Cannot pop, stack is empty");
|
||||
} else {
|
||||
IS_Drop (S);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void PushInt (IntStack* S, long Val)
|
||||
/* Pushes an integer onto an IntStack. Prints an error if the stack is full */
|
||||
{
|
||||
if (IS_IsFull (S)) {
|
||||
Error ("Cannot push: stack overflow");
|
||||
} else {
|
||||
IS_Push (S, Val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int BoolKeyword (StrBuf* Ident)
|
||||
/* Check if the identifier in Ident is a keyword for a boolean value. Currently
|
||||
* accepted are true/false/on/off.
|
||||
*/
|
||||
{
|
||||
if (SB_CompareStr (Ident, "true") == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (SB_CompareStr (Ident, "on") == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (SB_CompareStr (Ident, "false") == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (SB_CompareStr (Ident, "off") == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Error */
|
||||
Error ("Pragma argument must be one of `on', `off', `true' or `false'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Pragma handling functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static void StringPragma (StrBuf* B, void (*Func) (const char*))
|
||||
/* Handle a pragma that expects a string parameter */
|
||||
{
|
||||
StrBuf S;
|
||||
StrBuf S = AUTO_STRBUF_INITIALIZER;
|
||||
|
||||
/* We expect a string here */
|
||||
if (SB_GetString (B, &S)) {
|
||||
if (GetString (B, &S)) {
|
||||
/* Call the given function with the string argument */
|
||||
Func (SB_GetConstBuf (&S));
|
||||
} else {
|
||||
Error ("String literal expected");
|
||||
}
|
||||
|
||||
/* Call the string buf destructor */
|
||||
@ -161,67 +355,64 @@ static void StringPragma (StrBuf* B, void (*Func) (const char*))
|
||||
static void SegNamePragma (StrBuf* B, segment_t Seg)
|
||||
/* Handle a pragma that expects a segment name parameter */
|
||||
{
|
||||
ident Ident;
|
||||
StrBuf S;
|
||||
StrBuf S = AUTO_STRBUF_INITIALIZER;
|
||||
const char* Name;
|
||||
|
||||
/* Try to read an identifier */
|
||||
/* Check for the "push" or "pop" keywords */
|
||||
int Push = 0;
|
||||
if (SB_GetSym (B, Ident)) {
|
||||
switch (ParsePushPop (B)) {
|
||||
|
||||
/* Check if we have a first argument named "pop" */
|
||||
if (strcmp (Ident, "pop") == 0) {
|
||||
case PP_NONE:
|
||||
break;
|
||||
|
||||
/* Pop the old value */
|
||||
case PP_PUSH:
|
||||
Push = 1;
|
||||
break;
|
||||
|
||||
case PP_POP:
|
||||
/* Pop the old value and output it */
|
||||
PopSegName (Seg);
|
||||
|
||||
/* Set the segment name */
|
||||
g_segname (Seg);
|
||||
|
||||
/* Done */
|
||||
return;
|
||||
goto ExitPoint;
|
||||
|
||||
/* Check if we have a first argument named "push" */
|
||||
} else if (strcmp (Ident, "push") == 0) {
|
||||
case PP_ERROR:
|
||||
/* Bail out */
|
||||
goto ExitPoint;
|
||||
|
||||
Push = 1;
|
||||
SB_SkipWhite (B);
|
||||
if (SB_Get (B) != ',') {
|
||||
Error ("Comma expected");
|
||||
return;
|
||||
}
|
||||
SB_SkipWhite (B);
|
||||
default:
|
||||
Internal ("Invalid result from ParsePushPop");
|
||||
|
||||
} else {
|
||||
Error ("Invalid pragma arguments");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* A string argument must follow */
|
||||
if (!SB_GetString (B, &S)) {
|
||||
Error ("String literal expected");
|
||||
return;
|
||||
if (!GetString (B, &S)) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* Get the string */
|
||||
Name = SB_GetConstBuf (&S);
|
||||
|
||||
/* Check if the name is valid */
|
||||
if (!ValidSegName (Name)) {
|
||||
if (ValidSegName (Name)) {
|
||||
|
||||
/* Set the new name */
|
||||
if (Push) {
|
||||
PushSegName (Seg, Name);
|
||||
} else {
|
||||
SetSegName (Seg, Name);
|
||||
}
|
||||
g_segname (Seg);
|
||||
|
||||
} else {
|
||||
|
||||
/* Segment name is invalid */
|
||||
Error ("Illegal segment name: `%s'", Name);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* Set the new name */
|
||||
if (Push) {
|
||||
PushSegName (Seg, Name);
|
||||
} else {
|
||||
SetSegName (Seg, Name);
|
||||
}
|
||||
g_segname (Seg);
|
||||
|
||||
ExitPoint:
|
||||
/* Call the string buf destructor */
|
||||
SB_Done (&S);
|
||||
}
|
||||
@ -234,7 +425,7 @@ static void CharMapPragma (StrBuf* B)
|
||||
long Index, C;
|
||||
|
||||
/* Read the character index */
|
||||
if (!SB_GetNumber (B, &Index)) {
|
||||
if (!GetNumber (B, &Index)) {
|
||||
return;
|
||||
}
|
||||
if (Index < 1 || Index > 255) {
|
||||
@ -248,15 +439,12 @@ static void CharMapPragma (StrBuf* B)
|
||||
}
|
||||
|
||||
/* Comma follows */
|
||||
SB_SkipWhite (B);
|
||||
if (SB_Get (B) != ',') {
|
||||
Error ("Comma expected");
|
||||
if (!GetComma (B)) {
|
||||
return;
|
||||
}
|
||||
SB_SkipWhite (B);
|
||||
|
||||
/* Read the character code */
|
||||
if (!SB_GetNumber (B, &C)) {
|
||||
if (!GetNumber (B, &C)) {
|
||||
return;
|
||||
}
|
||||
if (C < 1 || C > 255) {
|
||||
@ -275,50 +463,52 @@ static void CharMapPragma (StrBuf* B)
|
||||
|
||||
|
||||
|
||||
static void FlagPragma (StrBuf* B, IntStack* Stack)
|
||||
/* Handle a pragma that expects a boolean paramater */
|
||||
static void WarnPragma (StrBuf* B)
|
||||
/* Enable/disable warnings */
|
||||
{
|
||||
ident Ident;
|
||||
long Val;
|
||||
int Push;
|
||||
long Val;
|
||||
int Push;
|
||||
|
||||
/* Try to read an identifier */
|
||||
int IsIdent = SB_GetSym (B, Ident);
|
||||
|
||||
/* Check if we have a first argument named "pop" */
|
||||
if (IsIdent && strcmp (Ident, "pop") == 0) {
|
||||
if (IS_GetCount (Stack) < 2) {
|
||||
Error ("Cannot pop, stack is empty");
|
||||
} else {
|
||||
IS_Drop (Stack);
|
||||
}
|
||||
/* No other arguments allowed */
|
||||
/* A warning name must follow */
|
||||
IntStack* S =GetWarning (B);
|
||||
if (S == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we have a first argument named "push" */
|
||||
if (IsIdent && strcmp (Ident, "push") == 0) {
|
||||
Push = 1;
|
||||
SB_SkipWhite (B);
|
||||
if (SB_Get (B) != ',') {
|
||||
Error ("Comma expected");
|
||||
/* Comma follows */
|
||||
if (!GetComma (B)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for the "push" or "pop" keywords */
|
||||
switch (ParsePushPop (B)) {
|
||||
|
||||
case PP_NONE:
|
||||
Push = 0;
|
||||
break;
|
||||
|
||||
case PP_PUSH:
|
||||
Push = 1;
|
||||
break;
|
||||
|
||||
case PP_POP:
|
||||
/* Pop the old value and bail out */
|
||||
PopInt (S);
|
||||
return;
|
||||
}
|
||||
SB_SkipWhite (B);
|
||||
IsIdent = SB_GetSym (B, Ident);
|
||||
} else {
|
||||
Push = 0;
|
||||
|
||||
case PP_ERROR:
|
||||
/* Bail out */
|
||||
return;
|
||||
|
||||
default:
|
||||
Internal ("Invalid result from ParsePushPop");
|
||||
}
|
||||
|
||||
/* Boolean argument follows */
|
||||
if (IsIdent) {
|
||||
if (strcmp (Ident, "true") == 0 || strcmp (Ident, "on") == 0) {
|
||||
Val = 1;
|
||||
} else if (strcmp (Ident, "false") == 0 || strcmp (Ident, "off") == 0) {
|
||||
Val = 0;
|
||||
} else {
|
||||
Error ("Pragma argument must be one of `on', `off', `true' or `false'");
|
||||
}
|
||||
if (HasStr (B, "true") || HasStr (B, "on")) {
|
||||
Val = 1;
|
||||
} else if (HasStr (B, "false") || HasStr (B, "off")) {
|
||||
Val = 0;
|
||||
} else if (!SB_GetNumber (B, &Val)) {
|
||||
Error ("Invalid pragma argument");
|
||||
return;
|
||||
@ -326,14 +516,60 @@ static void FlagPragma (StrBuf* B, IntStack* Stack)
|
||||
|
||||
/* Set/push the new value */
|
||||
if (Push) {
|
||||
if (IS_IsFull (Stack)) {
|
||||
Error ("Cannot push: stack overflow");
|
||||
} else {
|
||||
IS_Push (Stack, Val);
|
||||
PushInt (S, Val);
|
||||
} else {
|
||||
IS_Set (S, Val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void FlagPragma (StrBuf* B, IntStack* Stack)
|
||||
/* Handle a pragma that expects a boolean paramater */
|
||||
{
|
||||
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
|
||||
long Val;
|
||||
int Push;
|
||||
|
||||
|
||||
/* Try to read an identifier */
|
||||
int IsIdent = SB_GetSym (B, &Ident, 0);
|
||||
|
||||
/* Check if we have a first argument named "pop" */
|
||||
if (IsIdent && SB_CompareStr (&Ident, "pop") == 0) {
|
||||
PopInt (Stack);
|
||||
/* No other arguments allowed */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we have a first argument named "push" */
|
||||
if (IsIdent && SB_CompareStr (&Ident, "push") == 0) {
|
||||
Push = 1;
|
||||
if (!GetComma (B)) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
IsIdent = SB_GetSym (B, &Ident, 0);
|
||||
} else {
|
||||
Push = 0;
|
||||
}
|
||||
|
||||
/* Boolean argument follows */
|
||||
if (IsIdent) {
|
||||
Val = BoolKeyword (&Ident);
|
||||
} else if (!GetNumber (B, &Val)) {
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* Set/push the new value */
|
||||
if (Push) {
|
||||
PushInt (Stack, Val);
|
||||
} else {
|
||||
IS_Set (Stack, Val);
|
||||
}
|
||||
|
||||
ExitPoint:
|
||||
/* Free the identifier */
|
||||
SB_Done (&Ident);
|
||||
}
|
||||
|
||||
|
||||
@ -341,41 +577,36 @@ static void FlagPragma (StrBuf* B, IntStack* Stack)
|
||||
static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
|
||||
/* Handle a pragma that expects an int paramater */
|
||||
{
|
||||
ident Ident;
|
||||
long Val;
|
||||
int Push;
|
||||
|
||||
/* Try to read an identifier */
|
||||
int IsIdent = SB_GetSym (B, Ident);
|
||||
/* Check for the "push" or "pop" keywords */
|
||||
switch (ParsePushPop (B)) {
|
||||
|
||||
/* Check if we have a first argument named "pop" */
|
||||
if (IsIdent && strcmp (Ident, "pop") == 0) {
|
||||
if (IS_GetCount (Stack) < 2) {
|
||||
Error ("Cannot pop, stack is empty");
|
||||
} else {
|
||||
IS_Drop (Stack);
|
||||
}
|
||||
/* No other arguments allowed */
|
||||
return;
|
||||
}
|
||||
case PP_NONE:
|
||||
Push = 0;
|
||||
break;
|
||||
|
||||
/* Check if we have a first argument named "push" */
|
||||
if (IsIdent && strcmp (Ident, "push") == 0) {
|
||||
Push = 1;
|
||||
SB_SkipWhite (B);
|
||||
if (SB_Get (B) != ',') {
|
||||
Error ("Comma expected");
|
||||
case PP_PUSH:
|
||||
Push = 1;
|
||||
break;
|
||||
|
||||
case PP_POP:
|
||||
/* Pop the old value and bail out */
|
||||
PopInt (Stack);
|
||||
return;
|
||||
}
|
||||
SB_SkipWhite (B);
|
||||
IsIdent = 0;
|
||||
} else {
|
||||
Push = 0;
|
||||
|
||||
case PP_ERROR:
|
||||
/* Bail out */
|
||||
return;
|
||||
|
||||
default:
|
||||
Internal ("Invalid result from ParsePushPop");
|
||||
|
||||
}
|
||||
|
||||
/* Integer argument follows */
|
||||
if (IsIdent || !SB_GetNumber (B, &Val)) {
|
||||
Error ("Pragma argument must be numeric");
|
||||
if (!GetNumber (B, &Val)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -387,11 +618,7 @@ static void IntPragma (StrBuf* B, IntStack* Stack, long Low, long High)
|
||||
|
||||
/* Set/push the new value */
|
||||
if (Push) {
|
||||
if (IS_IsFull (Stack)) {
|
||||
Error ("Cannot push: stack overflow");
|
||||
} else {
|
||||
IS_Push (Stack, Val);
|
||||
}
|
||||
PushInt (Stack, Val);
|
||||
} else {
|
||||
IS_Set (Stack, Val);
|
||||
}
|
||||
@ -403,7 +630,7 @@ static void ParsePragma (void)
|
||||
/* Parse the contents of the _Pragma statement */
|
||||
{
|
||||
pragma_t Pragma;
|
||||
ident Ident;
|
||||
StrBuf Ident = AUTO_STRBUF_INITIALIZER;
|
||||
|
||||
/* Create a string buffer from the string literal */
|
||||
StrBuf B = AUTO_STRBUF_INITIALIZER;
|
||||
@ -421,13 +648,13 @@ static void ParsePragma (void)
|
||||
|
||||
/* Get the pragma name from the string */
|
||||
SB_SkipWhite (&B);
|
||||
if (!SB_GetSym (&B, Ident)) {
|
||||
if (!SB_GetSym (&B, &Ident, "-")) {
|
||||
Error ("Invalid pragma");
|
||||
return;
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* Search for the name */
|
||||
Pragma = FindPragma (Ident);
|
||||
Pragma = FindPragma (&Ident);
|
||||
|
||||
/* Do we know this pragma? */
|
||||
if (Pragma == PR_ILLEGAL) {
|
||||
@ -435,15 +662,15 @@ static void ParsePragma (void)
|
||||
* for unknown pragmas, however, we're allowed to warn - and we will
|
||||
* do so. Otherwise one typo may give you hours of bug hunting...
|
||||
*/
|
||||
Warning ("Unknown pragma `%s'", Ident);
|
||||
return;
|
||||
Warning ("Unknown pragma `%s'", SB_GetConstBuf (&Ident));
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* Check for an open paren */
|
||||
SB_SkipWhite (&B);
|
||||
if (SB_Get (&B) != '(') {
|
||||
Error ("'(' expected");
|
||||
return;
|
||||
goto ExitPoint;
|
||||
}
|
||||
|
||||
/* Skip white space before the argument */
|
||||
@ -501,7 +728,7 @@ static void ParsePragma (void)
|
||||
break;
|
||||
|
||||
case PR_WARN:
|
||||
FlagPragma (&B, &WarnEnable);
|
||||
WarnPragma (&B);
|
||||
break;
|
||||
|
||||
case PR_ZPSYM:
|
||||
@ -516,7 +743,7 @@ static void ParsePragma (void)
|
||||
SB_SkipWhite (&B);
|
||||
if (SB_Get (&B) != ')') {
|
||||
Error ("')' expected");
|
||||
return;
|
||||
goto ExitPoint;
|
||||
}
|
||||
SB_SkipWhite (&B);
|
||||
|
||||
@ -531,8 +758,10 @@ static void ParsePragma (void)
|
||||
Error ("Unexpected input following pragma directive");
|
||||
}
|
||||
|
||||
/* Release the StrBuf */
|
||||
ExitPoint:
|
||||
/* Release the string buffers */
|
||||
SB_Done (&B);
|
||||
SB_Done (&Ident);
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,23 +162,31 @@ void SB_SkipWhite (StrBuf* B)
|
||||
|
||||
|
||||
|
||||
int SB_GetSym (StrBuf* B, char* S)
|
||||
/* Get a symbol from the string buffer. S must be able to hold MAX_IDENTLEN
|
||||
* characters. Returns 1 if a symbol was found and 0 otherwise.
|
||||
int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars)
|
||||
/* Get a symbol from the string buffer. If SpecialChars is not NULL, it
|
||||
* points to a string that contains characters allowed within the string in
|
||||
* addition to letters, digits and the underline. Note: The identifier must
|
||||
* still begin with a letter.
|
||||
* Returns 1 if a symbol was found and 0 otherwise but doesn't output any
|
||||
* errors.
|
||||
*/
|
||||
{
|
||||
/* Handle a NULL argument for SpecialChars transparently */
|
||||
if (SpecialChars == 0) {
|
||||
SpecialChars = "";
|
||||
}
|
||||
|
||||
/* Clear Ident */
|
||||
SB_Clear (Ident);
|
||||
|
||||
if (IsIdent (SB_Peek (B))) {
|
||||
unsigned I = 0;
|
||||
char C = SB_Peek (B);
|
||||
do {
|
||||
if (I < MAX_IDENTLEN) {
|
||||
++I;
|
||||
*S++ = C;
|
||||
}
|
||||
SB_AppendChar (Ident, C);
|
||||
SB_Skip (B);
|
||||
C = SB_Peek (B);
|
||||
} while (IsIdent (C) || IsDigit (C));
|
||||
*S = '\0';
|
||||
} while (IsIdent (C) || IsDigit (C) || strchr (SpecialChars, C) != 0);
|
||||
SB_Terminate (Ident);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
@ -188,15 +196,17 @@ int SB_GetSym (StrBuf* B, char* S)
|
||||
|
||||
|
||||
int SB_GetString (StrBuf* B, StrBuf* S)
|
||||
/* Get a string from the string buffer. S will be initialized by the function
|
||||
* and will return the correctly terminated string on return. The function
|
||||
* returns 1 if a string was found and 0 otherwise.
|
||||
/* Get a string from the string buffer. Returns 1 if a string was found and 0
|
||||
* otherwise. Errors are only output in case of invalid strings (missing end
|
||||
* of string).
|
||||
*/
|
||||
{
|
||||
char C;
|
||||
|
||||
/* Initialize S */
|
||||
SB_Init (S);
|
||||
/* Clear S */
|
||||
SB_Clear (S);
|
||||
|
||||
/* A string starts with quote marks */
|
||||
if (SB_Peek (B) == '\"') {
|
||||
|
||||
/* String follows, be sure to concatenate strings */
|
||||
@ -241,7 +251,7 @@ int SB_GetNumber (StrBuf* B, long* Val)
|
||||
/* Get a number from the string buffer. Accepted formats are decimal, octal,
|
||||
* hex and character constants. Numeric constants may be preceeded by a
|
||||
* minus or plus sign. The function returns 1 if a number was found and
|
||||
* zero otherwise.
|
||||
* zero otherwise. Errors are only output for invalid numbers.
|
||||
*/
|
||||
{
|
||||
int Sign;
|
||||
@ -249,65 +259,12 @@ int SB_GetNumber (StrBuf* B, long* Val)
|
||||
unsigned Base;
|
||||
unsigned DigitVal;
|
||||
|
||||
|
||||
/* Initialize Val */
|
||||
*Val = 0;
|
||||
|
||||
/* Check for a sign */
|
||||
Sign = 1;
|
||||
switch (SB_Peek (B)) {
|
||||
case '-':
|
||||
Sign = -1;
|
||||
/* FALLTHROUGH */
|
||||
case '+':
|
||||
SB_Skip (B);
|
||||
SB_SkipWhite (B);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for the different formats */
|
||||
C = SB_Peek (B);
|
||||
if (IsDigit (C)) {
|
||||
|
||||
if (C == '0') {
|
||||
/* Hex or octal */
|
||||
SB_Skip (B);
|
||||
if (tolower (SB_Peek (B)) == 'x') {
|
||||
SB_Skip (B);
|
||||
Base = 16;
|
||||
if (!IsXDigit (SB_Peek (B))) {
|
||||
Error ("Invalid hexadecimal number");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
Base = 8;
|
||||
}
|
||||
} else {
|
||||
Base = 10;
|
||||
}
|
||||
|
||||
/* Read the number */
|
||||
while (IsXDigit (C = SB_Peek (B)) && (DigitVal = HexVal (C)) < Base) {
|
||||
*Val = (*Val * Base) + DigitVal;
|
||||
SB_Skip (B);
|
||||
}
|
||||
|
||||
/* Allow optional 'U' and 'L' modifiers */
|
||||
C = SB_Peek (B);
|
||||
if (C == 'u' || C == 'U') {
|
||||
SB_Skip (B);
|
||||
C = SB_Peek (B);
|
||||
if (C == 'l' || C == 'L') {
|
||||
SB_Skip (B);
|
||||
}
|
||||
} else if (C == 'l' || C == 'L') {
|
||||
SB_Skip (B);
|
||||
C = SB_Peek (B);
|
||||
if (C == 'u' || C == 'U') {
|
||||
SB_Skip (B);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (C == '\'') {
|
||||
/* Handle character constants */
|
||||
if (SB_Peek (B) == '\'') {
|
||||
|
||||
/* Character constant */
|
||||
SB_Skip (B);
|
||||
@ -318,14 +275,70 @@ int SB_GetNumber (StrBuf* B, long* Val)
|
||||
} else {
|
||||
/* Skip the quote */
|
||||
SB_Skip (B);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Check for a sign. A sign must be followed by a digit, otherwise it's
|
||||
* not a number
|
||||
*/
|
||||
Sign = 1;
|
||||
switch (SB_Peek (B)) {
|
||||
case '-':
|
||||
Sign = -1;
|
||||
/* FALLTHROUGH */
|
||||
case '+':
|
||||
if (!IsDigit (SB_LookAt (B, SB_GetIndex (B) + 1))) {
|
||||
return 0;
|
||||
}
|
||||
SB_Skip (B);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalid number */
|
||||
Error ("Numeric constant expected");
|
||||
/* We must have a digit now, otherwise its not a number */
|
||||
C = SB_Peek (B);
|
||||
if (!IsDigit (C)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine the base */
|
||||
if (C == '0') {
|
||||
/* Hex or octal */
|
||||
SB_Skip (B);
|
||||
if (tolower (SB_Peek (B)) == 'x') {
|
||||
SB_Skip (B);
|
||||
Base = 16;
|
||||
if (!IsXDigit (SB_Peek (B))) {
|
||||
Error ("Invalid hexadecimal number");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
Base = 8;
|
||||
}
|
||||
} else {
|
||||
Base = 10;
|
||||
}
|
||||
|
||||
/* Read the number */
|
||||
while (IsXDigit (C = SB_Peek (B)) && (DigitVal = HexVal (C)) < Base) {
|
||||
*Val = (*Val * Base) + DigitVal;
|
||||
SB_Skip (B);
|
||||
}
|
||||
|
||||
/* Allow optional 'U' and 'L' modifiers */
|
||||
C = SB_Peek (B);
|
||||
if (C == 'u' || C == 'U') {
|
||||
SB_Skip (B);
|
||||
C = SB_Peek (B);
|
||||
if (C == 'l' || C == 'L') {
|
||||
SB_Skip (B);
|
||||
}
|
||||
} else if (C == 'l' || C == 'L') {
|
||||
SB_Skip (B);
|
||||
C = SB_Peek (B);
|
||||
if (C == 'u' || C == 'U') {
|
||||
SB_Skip (B);
|
||||
}
|
||||
}
|
||||
|
||||
/* Success, value read is in Val */
|
||||
|
@ -6,10 +6,10 @@
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2002 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* (C) 2002-2009, Ullrich von Bassewitz */
|
||||
/* Roemerstrasse 52 */
|
||||
/* D-70794 Filderstadt */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
@ -52,22 +52,26 @@
|
||||
void SB_SkipWhite (StrBuf* B);
|
||||
/* Skip whitespace in the string buffer */
|
||||
|
||||
int SB_GetSym (StrBuf* B, char* S);
|
||||
/* Get a symbol from the string buffer. S must be able to hold MAX_IDENTLEN
|
||||
* characters. Returns 1 if a symbol was found and 0 otherwise.
|
||||
int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars);
|
||||
/* Get a symbol from the string buffer. If SpecialChars is not NULL, it
|
||||
* points to a string that contains characters allowed within the string in
|
||||
* addition to letters, digits and the underline. Note: The identifier must
|
||||
* still begin with a letter.
|
||||
* Returns 1 if a symbol was found and 0 otherwise but doesn't output any
|
||||
* errors.
|
||||
*/
|
||||
|
||||
int SB_GetString (StrBuf* B, StrBuf* S);
|
||||
/* Get a string from the string buffer. S will be initialized by the function
|
||||
* and will return the correctly terminated string on return. The function
|
||||
* returns 1 if a string was found and 0 otherwise.
|
||||
/* Get a string from the string buffer. Returns 1 if a string was found and 0
|
||||
* otherwise. Errors are only output in case of invalid strings (missing end
|
||||
* of string).
|
||||
*/
|
||||
|
||||
int SB_GetNumber (StrBuf* B, long* Val);
|
||||
/* Get a number from the string buffer. Accepted formats are decimal, octal,
|
||||
* hex and character constants. Numeric constants may be preceeded by a
|
||||
* minus or plus sign. The function returns 1 if a number was found and
|
||||
* zero otherwise.
|
||||
* zero otherwise. Errors are only output for invalid numbers.
|
||||
*/
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user