Merge pull request #2777 from kugelfuhr/kugelfuhr/ca65-errors

Improve error handling for ca65
This commit is contained in:
Bob Andrews
2025-07-10 17:05:32 +02:00
committed by GitHub
94 changed files with 973 additions and 399 deletions

View File

@@ -238,8 +238,7 @@ void DbgInfoFunc (void)
ConsumeComma (); ConsumeComma ();
/* Type */ /* Type */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
Type = ValidateType (&CurTok.SVal); Type = ValidateType (&CurTok.SVal);
@@ -267,8 +266,7 @@ void DbgInfoFunc (void)
ConsumeComma (); ConsumeComma ();
/* Assembler name follows */ /* Assembler name follows */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
AsmName = GetStrBufId (&CurTok.SVal); AsmName = GetStrBufId (&CurTok.SVal);
@@ -321,8 +319,7 @@ void DbgInfoLine (void)
ConsumeComma (); ConsumeComma ();
/* The name of the file follows */ /* The name of the file follows */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
@@ -371,8 +368,7 @@ void DbgInfoSym (void)
ConsumeComma (); ConsumeComma ();
/* Name */ /* Name */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
Name = GetStrBufId (&CurTok.SVal); Name = GetStrBufId (&CurTok.SVal);
@@ -382,8 +378,7 @@ void DbgInfoSym (void)
ConsumeComma (); ConsumeComma ();
/* Type */ /* Type */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
Type = ValidateType (&CurTok.SVal); Type = ValidateType (&CurTok.SVal);
@@ -418,8 +413,7 @@ void DbgInfoSym (void)
Offs = ConstExpression (); Offs = ConstExpression ();
} else { } else {
/* Register, extern or static: Assembler name follows */ /* Register, extern or static: Assembler name follows */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
AsmName = GetStrBufId (&CurTok.SVal); AsmName = GetStrBufId (&CurTok.SVal);

View File

@@ -62,11 +62,11 @@ void GetEA (EffAddr* A)
if (BracketAsIndirect) { if (BracketAsIndirect) {
IndirectEnter = TOK_LBRACK; IndirectEnter = TOK_LBRACK;
IndirectLeave = TOK_RBRACK; IndirectLeave = TOK_RBRACK;
IndirectExpect = "']' expected"; IndirectExpect = "Expected ']'";
} else { } else {
IndirectEnter = TOK_LPAREN; IndirectEnter = TOK_LPAREN;
IndirectLeave = TOK_RPAREN; IndirectLeave = TOK_RPAREN;
IndirectExpect = "')' expected"; IndirectExpect = "Expected ')'";
} }
/* Clear the output struct */ /* Clear the output struct */
@@ -136,16 +136,22 @@ void GetEA (EffAddr* A)
/* (adr,x) */ /* (adr,x) */
NextTok (); NextTok ();
A->AddrModeSet = AM65_ABS_X_IND | AM65_DIR_X_IND; A->AddrModeSet = AM65_ABS_X_IND | AM65_DIR_X_IND;
Consume (IndirectLeave, IndirectExpect); if (!Consume (IndirectLeave, IndirectExpect)) {
SkipUntilSep ();
}
} else if (CurTok.Tok == TOK_S) { } else if (CurTok.Tok == TOK_S) {
/* (rel,s),y */ /* (rel,s),y */
NextTok (); NextTok ();
A->AddrModeSet = AM65_STACK_REL_IND_Y; A->AddrModeSet = AM65_STACK_REL_IND_Y;
Consume (IndirectLeave, IndirectExpect); if (!Consume (IndirectLeave, IndirectExpect) ||
ConsumeComma (); !ConsumeComma () ||
Consume (TOK_Y, "'Y' expected"); !Consume (TOK_Y, "Expected 'Y'")) {
/* In case of errors skip anything else on the line */
SkipUntilSep ();
}
} else { } else {
Error ("Syntax error"); ErrorExpect ("Expected 'X' or 'S'");
SkipUntilSep ();
} }
} else { } else {
@@ -162,7 +168,9 @@ void GetEA (EffAddr* A)
A->AddrModeSet = AM65_DIR_IND; A->AddrModeSet = AM65_DIR_IND;
break; break;
default: default:
Consume (TOK_Y, "'Y' expected"); if (!Consume (TOK_Y, "Expected 'Y'")) {
SkipUntilSep ();
}
A->AddrModeSet = AM65_DIR_IND_Y; A->AddrModeSet = AM65_DIR_IND_Y;
break; break;
} }
@@ -190,16 +198,22 @@ void GetEA (EffAddr* A)
/* [dir] or [dir],y */ /* [dir] or [dir],y */
NextTok (); NextTok ();
A->Expr = Expression (); A->Expr = Expression ();
Consume (TOK_RBRACK, "']' expected"); if (!Consume (TOK_RBRACK, "Expected ']'")) {
SkipUntilSep ();
}
if (CurTok.Tok == TOK_COMMA) { if (CurTok.Tok == TOK_COMMA) {
/* [dir],y */ /* [dir],y */
NextTok (); NextTok ();
if (GetCPU () == CPU_45GS02) { if (GetCPU () == CPU_45GS02) {
Consume (TOK_Z, "'Z' expected"); if (!Consume (TOK_Z, "Expected 'Z'")) {
SkipUntilSep ();
}
A->AddrModeSet = AM65_32BIT_BASE_IND_Z; A->AddrModeSet = AM65_32BIT_BASE_IND_Z;
} }
else { else {
Consume (TOK_Y, "'Y' expected"); if (!Consume (TOK_Y, "Expected 'Y'")) {
SkipUntilSep ();
}
A->AddrModeSet = AM65_DIR_IND_LONG_Y; A->AddrModeSet = AM65_DIR_IND_LONG_Y;
} }
} else { } else {

View File

@@ -96,7 +96,7 @@ void GetSweet16EA (EffAddr* A)
/* Register number */ /* Register number */
A->Reg = (unsigned) Reg; A->Reg = (unsigned) Reg;
} else { } else {
ErrorSkip ("Register or register number expected"); ErrorExpect ("Expected register or register number");
A->Reg = 0; A->Reg = 0;
} }

View File

@@ -87,12 +87,13 @@ void DoEnum (void)
continue; continue;
} }
/* The format is "identifier [ = value ]" */ /* Allow conditionals within an enum */
if (CurTok.Tok != TOK_IDENT) { if (CheckConditionals ()) {
/* Maybe it's a conditional? */ continue;
if (!CheckConditionals ()) {
ErrorSkip ("Identifier expected");
} }
/* The format is "identifier [ = value ]" */
if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
continue; continue;
} }

View File

@@ -108,18 +108,6 @@ static void VPrintMsg (const FilePos* Pos, const char* Desc,
static void PrintMsg (const FilePos* Pos, const char* Desc,
const char* Format, ...)
/* Format and output an error/warning message. */
{
va_list ap;
va_start (ap, Format);
VPrintMsg (Pos, Desc, Format, ap);
va_end (ap);
}
static void AddNotifications (const Collection* LineInfos) static void AddNotifications (const Collection* LineInfos)
/* Output additional notifications for an error or warning */ /* Output additional notifications for an error or warning */
{ {
@@ -165,7 +153,7 @@ static void AddNotifications (const Collection* LineInfos)
/* Output until an upper limit of messages is reached */ /* Output until an upper limit of messages is reached */
if (Msg) { if (Msg) {
if (Output < MAX_NOTES) { if (Output < MAX_NOTES) {
PrintMsg (GetSourcePos (LI), "Note", "%s", Msg); PNotification (GetSourcePos (LI), "%s", Msg);
++Output; ++Output;
} else { } else {
++Skipped; ++Skipped;
@@ -176,13 +164,31 @@ static void AddNotifications (const Collection* LineInfos)
/* Add a note if we have more stuff that we won't output */ /* Add a note if we have more stuff that we won't output */
if (Skipped > 0) { if (Skipped > 0) {
const LineInfo* LI = CollConstAt (LineInfos, 0); const LineInfo* LI = CollConstAt (LineInfos, 0);
PrintMsg (GetSourcePos (LI), "Note", PNotification (GetSourcePos (LI), "Dropping %u additional line infos",
"Dropping %u additional line infos", Skipped); Skipped);
} }
} }
/*****************************************************************************/
/* Notifications */
/*****************************************************************************/
void PNotification (const FilePos* Pos, const char* Format, ...)
/* Print a notification message. */
{
/* Output the message */
va_list ap;
va_start (ap, Format);
VPrintMsg (Pos, "Note", Format, ap);
va_end (ap);
}
/*****************************************************************************/ /*****************************************************************************/
/* Warnings */ /* Warnings */
/*****************************************************************************/ /*****************************************************************************/

View File

@@ -72,6 +72,9 @@ extern unsigned WarningCount;
void PNotification (const FilePos* Pos, const char* Format, ...) attribute ((format (printf, 2, 3)));
/* Print a notification message. */
void Warning (unsigned Level, const char* Format, ...) attribute ((format (printf, 2, 3))); void Warning (unsigned Level, const char* Format, ...) attribute ((format (printf, 2, 3)));
/* Print warning message. */ /* Print warning message. */

View File

@@ -421,8 +421,7 @@ static ExprNode* FuncCapability (void)
capability_t Cap; capability_t Cap;
/* We must have an identifier */ /* We must have an identifier */
if (CurTok.Tok != TOK_IDENT) { if (!Expect (TOK_IDENT, "Expected a capability name")) {
Error ("Arguments to .CAPABILITY must be identifiers");
/* Skip tokens until closing paren or end of line */ /* Skip tokens until closing paren or end of line */
while (CurTok.Tok != TOK_RPAREN && !TokIsSep (CurTok.Tok)) { while (CurTok.Tok != TOK_RPAREN && !TokIsSep (CurTok.Tok)) {
NextTok (); NextTok ();
@@ -942,8 +941,7 @@ static ExprNode* FuncStrAt (void)
unsigned char C = 0; unsigned char C = 0;
/* String constant expected */ /* String constant expected */
if (CurTok.Tok != TOK_STRCON) { if (!Expect (TOK_STRCON, "Expected a string constant")) {
Error ("String constant expected");
NextTok (); NextTok ();
goto ExitPoint; goto ExitPoint;
} }
@@ -985,9 +983,8 @@ static ExprNode* FuncStrLen (void)
int Len; int Len;
/* String constant expected */ /* String constant expected */
if (CurTok.Tok != TOK_STRCON) { if (!Expect (TOK_STRCON, "Expected a string constant")) {
Error ("String constant expected");
/* Smart error recovery */ /* Smart error recovery */
if (CurTok.Tok != TOK_RPAREN) { if (CurTok.Tok != TOK_RPAREN) {
NextTok (); NextTok ();
@@ -1062,9 +1059,7 @@ static ExprNode* Function (ExprNode* (*F) (void))
NextTok (); NextTok ();
/* Expression must be enclosed in braces */ /* Expression must be enclosed in braces */
if (CurTok.Tok != TOK_LPAREN) { if (!ExpectSkip (TOK_LPAREN, "Expected '('")) {
Error ("'(' expected");
SkipUntilSep ();
return GenLiteral0 (); return GenLiteral0 ();
} }
NextTok (); NextTok ();
@@ -1296,7 +1291,7 @@ static ExprNode* Factor (void)
NextTok (); NextTok ();
} else { } else {
N = GenLiteral0 (); /* Dummy */ N = GenLiteral0 (); /* Dummy */
Error ("Syntax error"); ErrorExpect ("Expected an expression");
} }
break; break;
} }
@@ -1957,8 +1952,7 @@ ExprNode* GenNearAddrExpr (ExprNode* Expr)
if (IsEasyConst (Expr, &Val)) { if (IsEasyConst (Expr, &Val)) {
FreeExpr (Expr); FreeExpr (Expr);
Expr = GenLiteralExpr (Val & 0xFFFF); Expr = GenLiteralExpr (Val & 0xFFFF);
if (Val > 0xFFFF) if (Val > 0xFFFF) {
{
Error ("Range error: constant too large for assumed near address."); Error ("Range error: constant too large for assumed near address.");
} }
} else { } else {

View File

@@ -102,10 +102,12 @@ struct Macro {
unsigned TokCount; /* Number of tokens for this macro */ unsigned TokCount; /* Number of tokens for this macro */
TokNode* TokRoot; /* Root of token list */ TokNode* TokRoot; /* Root of token list */
TokNode* TokLast; /* Pointer to last token in list */ TokNode* TokLast; /* Pointer to last token in list */
FilePos DefPos; /* Position of definition */
StrBuf Name; /* Macro name, dynamically allocated */ StrBuf Name; /* Macro name, dynamically allocated */
unsigned Expansions; /* Number of active macro expansions */ unsigned Expansions; /* Number of active macro expansions */
unsigned char Style; /* Macro style */ unsigned char Style; /* Macro style */
unsigned char Incomplete; /* Macro is currently built */ unsigned char Incomplete; /* Macro is currently built */
unsigned char HasError; /* Macro has errors */
}; };
/* Hash table functions */ /* Hash table functions */
@@ -232,7 +234,7 @@ static void FreeIdDescList (IdDesc* ID)
static Macro* NewMacro (const StrBuf* Name, unsigned char Style) static Macro* NewMacro (const StrBuf* Name, const FilePos* P, unsigned char Style)
/* Generate a new macro entry, initialize and return it */ /* Generate a new macro entry, initialize and return it */
{ {
/* Allocate memory */ /* Allocate memory */
@@ -247,11 +249,14 @@ static Macro* NewMacro (const StrBuf* Name, unsigned char Style)
M->TokCount = 0; M->TokCount = 0;
M->TokRoot = 0; M->TokRoot = 0;
M->TokLast = 0; M->TokLast = 0;
M->DefPos = *P;
SB_Init (&M->Name); SB_Init (&M->Name);
SB_Copy (&M->Name, Name); SB_Copy (&M->Name, Name);
SB_Terminate (&M->Name); /* So the name can be used with %s */
M->Expansions = 0; M->Expansions = 0;
M->Style = Style; M->Style = Style;
M->Incomplete = 1; M->Incomplete = 1;
M->HasError = 0;
/* Insert the macro into the hash table */ /* Insert the macro into the hash table */
HT_Insert (&MacroTab, &M->Node); HT_Insert (&MacroTab, &M->Node);
@@ -364,18 +369,27 @@ static void FreeMacExp (MacExp* E)
static void MacSkipDef (unsigned Style) static void MacSkipDef (unsigned Style, const FilePos* StartPos)
/* Skip a macro definition */ /* Skip a macro definition */
{ {
if (Style == MAC_STYLE_CLASSIC) { if (Style == MAC_STYLE_CLASSIC) {
/* Skip tokens until we reach the final .endmacro */ /* Skip tokens until we reach the final .endmacro. Implement the same
while (CurTok.Tok != TOK_ENDMACRO && CurTok.Tok != TOK_EOF) { ** behavior as when parsing the macro regularily: .endmacro needs to
NextTok (); ** be at the start of the line to end the macro definition.
*/
int LastWasSep = 0;
while (1) {
if (CurTok.Tok == TOK_EOF) {
ErrorExpect ("Expected '.ENDMACRO'");
PNotification (StartPos, "Macro definition started here");
break;
} }
if (CurTok.Tok != TOK_EOF) { if (CurTok.Tok == TOK_ENDMACRO && LastWasSep) {
SkipUntilSep (); NextTok ();
} else { break;
Error ("'.ENDMACRO' expected"); }
LastWasSep = (CurTok.Tok == TOK_SEP);
NextTok ();
} }
} else { } else {
/* Skip until end of line */ /* Skip until end of line */
@@ -388,48 +402,49 @@ static void MacSkipDef (unsigned Style)
void MacDef (unsigned Style) void MacDef (unsigned Style)
/* Parse a macro definition */ /* Parse a macro definition */
{ {
Macro* Existing;
Macro* M; Macro* M;
TokNode* N; TokNode* N;
FilePos Pos;
int HaveParams; int HaveParams;
int LastTokWasSep;
/* Remember the current file position */
FilePos StartPos = CurTok.Pos;
/* For classic macros, remember if we are at the beginning of the line. /* For classic macros, remember if we are at the beginning of the line.
** If the macro name and parameters pass our checks then we will be on a ** If the macro name and parameters pass our checks then we will be on a
** new line, so set it now ** new line, so set it now
*/ */
LastTokWasSep = 1; int LastTokWasSep = 1;
/* Save the position of the start of the macro definition to allow
** using Perror to display the error if .endmacro isn't found
*/
Pos = CurTok.Pos;
/* We expect a macro name here */ /* We expect a macro name here */
if (CurTok.Tok != TOK_IDENT) { if (!Expect (TOK_IDENT, "Expected an identifier")) {
Error ("Identifier expected"); MacSkipDef (Style, &StartPos);
MacSkipDef (Style);
return; return;
} else if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) { }
if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) {
/* The identifier is a name of a 6502 instruction, which is not /* The identifier is a name of a 6502 instruction, which is not
** allowed if not explicitly enabled. ** allowed if not explicitly enabled.
*/ */
Error ("Cannot use an instruction as macro name"); Error ("Cannot use an instruction as macro name");
MacSkipDef (Style); MacSkipDef (Style, &StartPos);
return; return;
} }
/* Did we already define that macro? */ /* Did we already define that macro? */
if (HT_Find (&MacroTab, &CurTok.SVal) != 0) { Existing = HT_Find (&MacroTab, &CurTok.SVal);
if (Existing != 0) {
/* Macro is already defined */ /* Macro is already defined */
Error ("A macro named '%m%p' is already defined", &CurTok.SVal); Error ("A macro named '%m%p' is already defined", &CurTok.SVal);
PNotification (&Existing->DefPos,
"Previous definition of macro '%s' was here",
SB_GetConstBuf (&Existing->Name));
/* Skip tokens until we reach the final .endmacro */ /* Skip tokens until we reach the final .endmacro */
MacSkipDef (Style); MacSkipDef (Style, &StartPos);
return; return;
} }
/* Define the macro */ /* Define the macro */
M = NewMacro (&CurTok.SVal, Style); M = NewMacro (&CurTok.SVal, &StartPos, Style);
/* Switch to raw token mode and skip the macro name */ /* Switch to raw token mode and skip the macro name */
EnterRawTokenMode (); EnterRawTokenMode ();
@@ -439,7 +454,7 @@ void MacDef (unsigned Style)
** otherwise, we may have parameters without parentheses. ** otherwise, we may have parameters without parentheses.
*/ */
if (Style == MAC_STYLE_CLASSIC) { if (Style == MAC_STYLE_CLASSIC) {
HaveParams = 1; HaveParams = (CurTok.Tok != TOK_SEP);
} else { } else {
if (CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) {
HaveParams = 1; HaveParams = 1;
@@ -451,7 +466,7 @@ void MacDef (unsigned Style)
/* Parse the parameter list */ /* Parse the parameter list */
if (HaveParams) { if (HaveParams) {
while (CurTok.Tok == TOK_IDENT) { while (Expect (TOK_IDENT, "Expected a parameter name")) {
/* Create a struct holding the identifier */ /* Create a struct holding the identifier */
IdDesc* I = NewIdDesc (&CurTok.SVal); IdDesc* I = NewIdDesc (&CurTok.SVal);
@@ -463,7 +478,8 @@ void MacDef (unsigned Style)
while (1) { while (1) {
if (SB_Compare (&List->Id, &CurTok.SVal) == 0) { if (SB_Compare (&List->Id, &CurTok.SVal) == 0) {
Error ("Duplicate symbol '%m%p'", &CurTok.SVal); Error ("Duplicate macro parameter '%m%p'", &CurTok.SVal);
M->HasError = 1;
} }
if (List->Next == 0) { if (List->Next == 0) {
break; break;
@@ -513,7 +529,10 @@ void MacDef (unsigned Style)
} }
/* May not have end of file in a macro definition */ /* May not have end of file in a macro definition */
if (CurTok.Tok == TOK_EOF) { if (CurTok.Tok == TOK_EOF) {
PError (&Pos, "'.ENDMACRO' expected for macro '%m%p'", &M->Name); Error ("Missing '.ENDMACRO' for definition of macro '%s'",
SB_GetConstBuf (&M->Name));
PNotification (&StartPos, "Macro definition started here");
M->HasError = 1;
goto Done; goto Done;
} }
} else { } else {
@@ -533,8 +552,9 @@ void MacDef (unsigned Style)
/* Need an identifer */ /* Need an identifer */
if (CurTok.Tok != TOK_IDENT && CurTok.Tok != TOK_LOCAL_IDENT) { if (CurTok.Tok != TOK_IDENT && CurTok.Tok != TOK_LOCAL_IDENT) {
Error ("Identifier expected"); ErrorExpect ("Expected an identifier");
SkipUntilSep (); SkipUntilSep ();
M->HasError = 1;
break; break;
} }
@@ -553,7 +573,10 @@ void MacDef (unsigned Style)
} }
/* We need end of line after the locals */ /* We need end of line after the locals */
ConsumeSep (); if (!ExpectSep ()) {
M->HasError = 1;
}
NextTok ();
continue; continue;
} }
@@ -626,7 +649,8 @@ void MacUndef (const StrBuf* Name, unsigned char Style)
return; return;
} }
if (M->Expansions > 0) { if (M->Expansions > 0) {
Error ("Cannot delete a macro that is currently expanded"); Error ("Cannot delete macro '%s' which is currently expanded",
SB_GetConstBuf (&M->Name));
return; return;
} }
@@ -813,7 +837,11 @@ static void StartExpClassic (MacExp* E)
/* Check for maximum parameter count */ /* Check for maximum parameter count */
if (E->ParamCount >= E->M->ParamCount) { if (E->ParamCount >= E->M->ParamCount) {
ErrorSkip ("Too many macro parameters"); ErrorSkip ("Too many parameters for macro '%s'",
SB_GetConstBuf (&E->M->Name));
PNotification (&E->M->DefPos,
"See definition of macro '%s' which was here",
SB_GetConstBuf (&E->M->Name));
break; break;
} }
@@ -827,7 +855,7 @@ static void StartExpClassic (MacExp* E)
/* Check for end of file */ /* Check for end of file */
if (CurTok.Tok == TOK_EOF) { if (CurTok.Tok == TOK_EOF) {
Error ("Unexpected end of file"); Error ("Unexpected end of file in macro parameter list");
FreeMacExp (E); FreeMacExp (E);
return; return;
} }
@@ -938,10 +966,8 @@ static void StartExpDefine (MacExp* E)
/* Check for a comma */ /* Check for a comma */
if (Count > 0) { if (Count > 0) {
if (CurTok.Tok == TOK_COMMA) { if (Expect (TOK_COMMA, "Expected ','")) {
NextTok (); NextTok ();
} else {
Error ("',' expected");
} }
} }
} }
@@ -972,9 +998,21 @@ void MacExpandStart (Macro* M)
Pos = CurTok.Pos; Pos = CurTok.Pos;
NextTok (); NextTok ();
/* We cannot expand a macro with errors */
if (M->HasError) {
PError (&Pos, "Macro '%s' contains errors and cannot be expanded",
SB_GetConstBuf (&M->Name));
PNotification (&M->DefPos, "Definition of macro '%s' was here",
SB_GetConstBuf (&M->Name));
return;
}
/* We cannot expand an incomplete macro */ /* We cannot expand an incomplete macro */
if (M->Incomplete) { if (M->Incomplete) {
PError (&Pos, "Cannot expand an incomplete macro"); PError (&Pos, "Macro '%s' is incomplete and cannot be expanded",
SB_GetConstBuf (&M->Name));
PNotification (&M->DefPos, "Definition of macro '%s' was here",
SB_GetConstBuf (&M->Name));
return; return;
} }
@@ -982,7 +1020,8 @@ void MacExpandStart (Macro* M)
** to force an endless loop and assembler crash. ** to force an endless loop and assembler crash.
*/ */
if (MacExpansions >= MAX_MACEXPANSIONS) { if (MacExpansions >= MAX_MACEXPANSIONS) {
PError (&Pos, "Too many nested macro expansions"); PError (&Pos, "Too many nested macro expansions for macro '%s'",
SB_GetConstBuf (&M->Name));
return; return;
} }

View File

@@ -727,7 +727,7 @@ static void DoPCAssign (void)
{ {
long PC = ConstExpression (); long PC = ConstExpression ();
if (PC < 0 || PC > 0xFFFFFF) { if (PC < 0 || PC > 0xFFFFFF) {
Error ("Range error"); Error ("Program counter value is out of valid range");
} else { } else {
EnterAbsoluteMode (PC); EnterAbsoluteMode (PC);
} }
@@ -823,8 +823,7 @@ static void OneLine (void)
SymDef (Sym, Expression (), ADDR_SIZE_DEFAULT, Flags); SymDef (Sym, Expression (), ADDR_SIZE_DEFAULT, Flags);
/* Don't allow anything after a symbol definition */ /* Don't allow anything after a symbol definition */
ConsumeSep (); goto Done;
return;
} else if (CurTok.Tok == TOK_SET) { } else if (CurTok.Tok == TOK_SET) {
@@ -842,8 +841,7 @@ static void OneLine (void)
SymDef (Sym, Expr, ADDR_SIZE_DEFAULT, SF_VAR); SymDef (Sym, Expr, ADDR_SIZE_DEFAULT, SF_VAR);
/* Don't allow anything after a symbol definition */ /* Don't allow anything after a symbol definition */
ConsumeSep (); goto Done;
return;
} else { } else {
@@ -853,26 +851,24 @@ static void OneLine (void)
Seg = ActiveSeg; Seg = ActiveSeg;
PC = GetPC (); PC = GetPC ();
/* Define the label */
SymDef (Sym, GenCurrentPC (), ADDR_SIZE_DEFAULT, SF_LABEL);
/* Skip the colon. If NoColonLabels is enabled, allow labels /* Skip the colon. If NoColonLabels is enabled, allow labels
** without a colon if there is no whitespace before the ** without a colon if there is no whitespace before the
** identifier. ** identifier.
*/ */
if (CurTok.Tok != TOK_COLON) { if (CurTok.Tok != TOK_COLON) {
if (HadWS || !NoColonLabels) { if (HadWS || !NoColonLabels) {
Error ("':' expected"); ErrorExpect ("Expected ':' after identifier to form a label");
/* Try some smart error recovery */ SkipUntilSep ();
if (CurTok.Tok == TOK_NAMESPACE) { goto Done;
NextTok ();
}
} }
} else { } else {
/* Skip the colon */ /* Skip the colon */
NextTok (); NextTok ();
} }
/* Define the label */
SymDef (Sym, GenCurrentPC (), ADDR_SIZE_DEFAULT, SF_LABEL);
/* If we come here, a new identifier may be waiting, which may /* If we come here, a new identifier may be waiting, which may
** be a macro or instruction. ** be a macro or instruction.
*/ */
@@ -906,16 +902,22 @@ static void OneLine (void)
HandleInstruction (Instr); HandleInstruction (Instr);
} else if (PCAssignment && (CurTok.Tok == TOK_STAR || CurTok.Tok == TOK_PC)) { } else if (PCAssignment && (CurTok.Tok == TOK_STAR || CurTok.Tok == TOK_PC)) {
NextTok (); NextTok ();
if (CurTok.Tok != TOK_EQ) { if (!ExpectSkip (TOK_EQ, "Expected '='")) {
Error ("'=' expected"); goto Done;
SkipUntilSep (); }
} else {
/* Skip the equal sign */ /* Skip the equal sign */
NextTok (); NextTok ();
/* Enter absolute mode */ /* Enter absolute mode */
DoPCAssign (); DoPCAssign ();
} else if ((CurTok.Tok >= TOK_FIRSTOP && CurTok.Tok <= TOK_LASTOP) ||
(CurTok.Tok >= TOK_FIRSTREG && CurTok.Tok <= TOK_LASTREG) ||
CurTok.Tok == TOK_INTCON || CurTok.Tok == TOK_CHARCON ||
CurTok.Tok == TOK_STRCON) {
ErrorExpect ("Expected a mnemonic");
SkipUntilSep ();
goto Done;
} }
}
/* If we have defined a label, remember its size. Sym is also set by /* If we have defined a label, remember its size. Sym is also set by
** a symbol assignment, but in this case Done is false, so we don't ** a symbol assignment, but in this case Done is false, so we don't
@@ -938,6 +940,7 @@ static void OneLine (void)
} }
} }
Done:
/* Line separator must come here */ /* Line separator must come here */
ConsumeSep (); ConsumeSep ();
} }

View File

@@ -725,52 +725,118 @@ void NextTok (void)
void Consume (token_t Expected, const char* ErrMsg) void ErrorExpect (const char* Msg)
/* Consume Expected, print an error if we don't find it */ /* Output an error message about some expected token using Msg and the
* description of the following token. This means that Msg should contain
* something like "xyz expected". The actual error message would then be
* "xyz expected but found zyx".
*/
{
StrBuf S = AUTO_STRBUF_INITIALIZER;
TokenDesc (&CurTok, &S);
Error ("%s but found '%s'", Msg, SB_GetConstBuf (&S));
SB_Done (&S);
}
int Expect (token_t Expected, const char* Msg)
/* Check if the next token is the expected one. If not, print Msg plus some
* information about the token that was actually found. This means that Msg
* should contain something like "xyz expected". The actual error message would
* then be "xyz expected but found zyx".
* Returns true if the token was found, otherwise false.
*/
{ {
if (CurTok.Tok == Expected) { if (CurTok.Tok == Expected) {
return 1;
}
ErrorExpect (Msg);
return 0;
}
int ExpectSkip (token_t Expected, const char* Msg)
/* Check if the next token is the expected one. If not, print Msg plus some
* information about the token that was actually found and skip the remainder
* of the line. This means that Msg should contain something like "xyz
* expected". The actual error message would then be "xyz expected but found
* zyx".
* Returns true if the token was found, otherwise false.
*/
{
if (CurTok.Tok == Expected) {
return 1;
}
ErrorExpect (Msg);
SkipUntilSep ();
return 0;
}
int Consume (token_t Expected, const char* ErrMsg)
/* Consume Token, print an error if we don't find it. Return true if the token
** was found and false otherwise.
*/
{
if (Expect (Expected, ErrMsg)) {
NextTok (); NextTok ();
return 1;
} else { } else {
Error ("%s", ErrMsg); return 0;
} }
} }
void ConsumeSep (void) int ConsumeSep (void)
/* Consume a separator token */ /* Consume a separator token. Return true if the token was found and false
* otherwise.
*/
{ {
/* We expect a separator token */ /* We expect a separator token */
ExpectSep (); int Found = ExpectSep ();
/* If we are at end of line, skip it */ /* If we are at end of line, skip it */
if (CurTok.Tok == TOK_SEP) { if (CurTok.Tok == TOK_SEP) {
NextTok (); NextTok ();
} }
return Found;
} }
void ConsumeLParen (void) int ConsumeLParen (void)
/* Consume a left paren */ /* Consume a left paren. Return true if the token was found and false
** otherwise.
*/
{ {
Consume (TOK_LPAREN, "'(' expected"); return Consume (TOK_LPAREN, "Expected '('");
} }
void ConsumeRParen (void) int ConsumeRParen (void)
/* Consume a right paren */ /* Consume a right paren. Return true if the token was found and false
** otherwise.
*/
{ {
Consume (TOK_RPAREN, "')' expected"); return Consume (TOK_RPAREN, "Expected ')'");
} }
void ConsumeComma (void) int ConsumeComma (void)
/* Consume a comma */ /* Consume a comma. Return true if the token was found and false
** otherwise.
*/
{ {
Consume (TOK_COMMA, "',' expected"); return Consume (TOK_COMMA, "Expected ','");
} }
@@ -785,13 +851,21 @@ void SkipUntilSep (void)
void ExpectSep (void) int ExpectSep (void)
/* Check if we've reached a line separator, and output an error if not. Do /* Check if we've reached a line separator. If so, return true. If not, output
** not skip the line separator. ** an error and skip all tokens until the line separator is reached. Then
** return false.
*/ */
{ {
if (!TokIsSep (CurTok.Tok)) { if (!TokIsSep (CurTok.Tok)) {
ErrorSkip ("Unexpected trailing garbage characters"); /* Try to be helpful by giving information about the token that was
* unexpected.
*/
ErrorExpect ("Expected 'end-of-line'");
SkipUntilSep ();
return 0;
} else {
return 1;
} }
} }

View File

@@ -51,27 +51,62 @@
void NextTok (void); void NextTok (void);
/* Get next token and handle token level functions */ /* Get next token and handle token level functions */
void Consume (token_t Expected, const char* ErrMsg); void ErrorExpect (const char* Msg);
/* Consume Token, print an error if we don't find it */ /* Output an error message about some expected token using Msg and the
* description of the following token. This means that Msg should contain
* something like "xyz expected". The actual error message would then be
* "xyz expected but found zyx".
*/
void ConsumeSep (void); int Expect (token_t Expected, const char* Msg);
/* Consume a separator token */ /* Check if the next token is the expected one. If not, print Msg plus some
* information about the token that was actually found. This means that Msg
* should contain something like "xyz expected". The actual error message would
* then be "xyz expected but found zyx".
* Returns true if the token was found, otherwise false.
*/
void ConsumeLParen (void); int ExpectSkip (token_t Expected, const char* Msg);
/* Consume a left paren */ /* Check if the next token is the expected one. If not, print Msg plus some
* information about the token that was actually found and skip the remainder
* of the line. This means that Msg should contain something like "xyz
* expected". The actual error message would then be "xyz expected but found
* zyx".
* Returns true if the token was found, otherwise false.
*/
void ConsumeRParen (void); int Consume (token_t Expected, const char* ErrMsg);
/* Consume a right paren */ /* Consume Token, print an error if we don't find it. Return true if the token
** was found and false otherwise.
*/
void ConsumeComma (void); int ConsumeSep (void);
/* Consume a comma */ /* Consume a separator token. Return true if the token was found and false
* otherwise.
*/
int ConsumeLParen (void);
/* Consume a left paren. Return true if the token was found and false
** otherwise.
*/
int ConsumeRParen (void);
/* Consume a right paren. Return true if the token was found and false
** otherwise.
*/
int ConsumeComma (void);
/* Consume a comma. Return true if the token was found and false
** otherwise.
*/
void SkipUntilSep (void); void SkipUntilSep (void);
/* Skip tokens until we reach a line separator or end of file */ /* Skip tokens until we reach a line separator or end of file */
void ExpectSep (void); int ExpectSep (void);
/* Check if we've reached a line separator, and output an error if not. Do /* Check if we've reached a line separator. If so, return true. If not, output
** not skip the line separator. ** an error and skip all tokens until the line separator is reached. Then
** return false.
*/ */
void EnterRawTokenMode (void); void EnterRawTokenMode (void);

View File

@@ -167,13 +167,17 @@ static void SetBoolOption (unsigned char* Flag)
switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) { switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
case 0: *Flag = 0; NextTok (); break; case 0: *Flag = 0; NextTok (); break;
case 1: *Flag = 1; NextTok (); break; case 1: *Flag = 1; NextTok (); break;
default: ErrorSkip ("'on' or 'off' expected"); break; default:
ErrorExpect ("Expected ON or OFF");
SkipUntilSep ();
break;
} }
} else if (TokIsSep (CurTok.Tok)) { } else if (TokIsSep (CurTok.Tok)) {
/* Without anything assume switch on */ /* Without anything assume switch on */
*Flag = 1; *Flag = 1;
} else { } else {
ErrorSkip ("'on' or 'off' expected"); ErrorExpect ("Expected ON or OFF");
SkipUntilSep ();
} }
} }
@@ -216,8 +220,7 @@ static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
while (1) { while (1) {
/* We need an identifier here */ /* We need an identifier here */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
@@ -283,7 +286,7 @@ static void ConDes (const StrBuf* Name, unsigned Type)
Prio = ConstExpression (); Prio = ConstExpression ();
if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) { if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
/* Value out of range */ /* Value out of range */
Error ("Range error"); Error ("Given priority is out of range");
return; return;
} }
} else { } else {
@@ -400,7 +403,7 @@ static void DoAlign (void)
/* Read the alignment value */ /* Read the alignment value */
Alignment = ConstExpression (); Alignment = ConstExpression ();
if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) { if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
ErrorSkip ("Range error"); ErrorSkip ("Alignment is out of range");
return; return;
} }
@@ -410,7 +413,7 @@ static void DoAlign (void)
FillVal = ConstExpression (); FillVal = ConstExpression ();
/* We need a byte value here */ /* We need a byte value here */
if (!IsByteRange (FillVal)) { if (!IsByteRange (FillVal)) {
ErrorSkip ("Range error"); ErrorSkip ("Fill value is not in byte range");
return; return;
} }
} else { } else {
@@ -428,8 +431,7 @@ static void DoASCIIZ (void)
{ {
while (1) { while (1) {
/* Must have a string constant */ /* Must have a string constant */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
@@ -463,11 +465,10 @@ static void DoAssert (void)
/* First we have the expression that has to evaluated */ /* First we have the expression that has to evaluated */
ExprNode* Expr = Expression (); ExprNode* Expr = Expression ();
ConsumeComma ();
/* Action follows */ /* Followed by comma and action */
if (CurTok.Tok != TOK_IDENT) { if (!ConsumeComma () || !Expect (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected"); SkipUntilSep ();
return; return;
} }
switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) { switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
@@ -496,9 +497,8 @@ static void DoAssert (void)
default: default:
Error ("Illegal assert action specifier"); Error ("Illegal assert action specifier");
/* Use lderror - there won't be an .o file anyway */ SkipUntilSep ();
Action = ASSERT_ACT_LDERROR; return;
break;
} }
NextTok (); NextTok ();
@@ -512,8 +512,7 @@ static void DoAssert (void)
NextTok (); NextTok ();
/* Read the message */ /* Read the message */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
@@ -642,20 +641,23 @@ static void DoCharMap (void)
/* Read the index as numerical value */ /* Read the index as numerical value */
Index = ConstExpression (); Index = ConstExpression ();
if (Index < 0 || Index > 255) { if (IsByteRange (Index)) {
/* Value out of range */ /* Value out of range */
ErrorSkip ("Index range error"); ErrorSkip ("Index must be in byte range");
return; return;
} }
/* Comma follows */ /* Comma follows */
ConsumeComma (); if (!ConsumeComma ()) {
SkipUntilSep ();
return;
}
/* Read the character code */ /* Read the character code */
Code = ConstExpression (); Code = ConstExpression ();
if (Code < 0 || Code > 255) { if (!IsByteRange (Code)) {
/* Value out of range */ /* Value out of range */
ErrorSkip ("Code range error"); ErrorSkip ("Replacement character code must be in byte range");
return; return;
} }
@@ -685,15 +687,17 @@ static void DoConDes (void)
long Type; long Type;
/* Symbol name follows */ /* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
NextTok (); NextTok ();
/* Type follows. May be encoded as identifier or numerical */ /* Type follows. May be encoded as identifier or numerical */
ConsumeComma (); if (!ConsumeComma ()) {
SkipUntilSep ();
goto ExitPoint;
}
if (CurTok.Tok == TOK_IDENT) { if (CurTok.Tok == TOK_IDENT) {
/* Map the following keyword to a number, then skip it */ /* Map the following keyword to a number, then skip it */
@@ -702,7 +706,8 @@ static void DoConDes (void)
/* Check if we got a valid keyword */ /* Check if we got a valid keyword */
if (Type < 0) { if (Type < 0) {
ErrorSkip ("Syntax error"); ErrorExpect ("Expected CONSTRUCTOR, DESTRUCTOR or INTERRUPTOR");
SkipUntilSep ();
goto ExitPoint; goto ExitPoint;
} }
@@ -712,7 +717,7 @@ static void DoConDes (void)
Type = ConstExpression (); Type = ConstExpression ();
if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) { if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
/* Value out of range */ /* Value out of range */
ErrorSkip ("Range error"); ErrorSkip ("Numeric condes type is out of range");
goto ExitPoint; goto ExitPoint;
} }
@@ -734,8 +739,7 @@ static void DoConstructor (void)
StrBuf Name = STATIC_STRBUF_INITIALIZER; StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */ /* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
@@ -771,8 +775,7 @@ static void DoDbg (void)
/* We expect a subkey */ /* We expect a subkey */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
@@ -788,7 +791,10 @@ static void DoDbg (void)
case 1: DbgInfoFunc (); break; case 1: DbgInfoFunc (); break;
case 2: DbgInfoLine (); break; case 2: DbgInfoLine (); break;
case 3: DbgInfoSym (); break; case 3: DbgInfoSym (); break;
default: ErrorSkip ("Syntax error"); break; default:
ErrorExpect ("Expected FILE, FUNC, LINE or SYM");
SkipUntilSep ();
break;
} }
} }
@@ -853,9 +859,7 @@ static void DoDelMac (void)
/* Delete a classic macro */ /* Delete a classic macro */
{ {
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) { if (ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
} else {
MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC); MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
NextTok (); NextTok ();
} }
@@ -869,8 +873,7 @@ static void DoDestructor (void)
StrBuf Name = STATIC_STRBUF_INITIALIZER; StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */ /* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
@@ -938,9 +941,7 @@ static void DoEndScope (void)
static void DoError (void) static void DoError (void)
/* User error */ /* User error */
{ {
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
Error ("User error: %m%p", &CurTok.SVal); Error ("User error: %m%p", &CurTok.SVal);
SkipUntilSep (); SkipUntilSep ();
} }
@@ -1010,9 +1011,7 @@ static void DoFarAddr (void)
static void DoFatal (void) static void DoFatal (void)
/* Fatal user error */ /* Fatal user error */
{ {
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
Fatal ("User error: %m%p", &CurTok.SVal); Fatal ("User error: %m%p", &CurTok.SVal);
SkipUntilSep (); SkipUntilSep ();
} }
@@ -1030,8 +1029,7 @@ static void DoFeature (void)
while (1) { while (1) {
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
@@ -1045,7 +1043,8 @@ static void DoFeature (void)
} }
if (Feature == FEAT_ADDRSIZE) { if (Feature == FEAT_ADDRSIZE) {
Warning (1, "Deprecated feature: '.feature addrsize'. Pseudo function .addrsize is always available."); Warning (1, "Deprecated feature: '.feature addrsize'. "
"Pseudo function .addrsize is always available.");
} }
NextTok (); NextTok ();
@@ -1087,19 +1086,17 @@ static void DoFileOpt (void)
OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
if (OptNum < 0) { if (OptNum < 0) {
/* Not found */ /* Not found */
ErrorSkip ("File option keyword expected"); ErrorExpect ("Expected a file option keyword");
SkipUntilSep ();
return; return;
} }
/* Skip the keyword */ /* Skip the keyword */
NextTok (); NextTok ();
/* Must be followed by a comma */ /* Must be followed by a comma and a string option */
ConsumeComma (); if (!ConsumeComma () || !Expect (TOK_STRCON, "Expected a string constant")) {
SkipUntilSep ();
/* We accept only string options for now */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return; return;
} }
@@ -1134,16 +1131,13 @@ static void DoFileOpt (void)
/* Option given as number */ /* Option given as number */
OptNum = ConstExpression (); OptNum = ConstExpression ();
if (!IsByteRange (OptNum)) { if (!IsByteRange (OptNum)) {
ErrorSkip ("Range error"); ErrorSkip ("Option number must be in byte range");
return; return;
} }
/* Must be followed by a comma */ /* Must be followed by a comma plus a string constant */
ConsumeComma (); if (!ConsumeComma () || !Expect (TOK_STRCON, "Expected a string constant")) {
SkipUntilSep ();
/* We accept only string options for now */
if (CurTok.Tok != TOK_STRCON) {
ErrorSkip ("String constant expected");
return; return;
} }
@@ -1248,8 +1242,7 @@ static void DoIncBin (void)
FILE* F; FILE* F;
/* Name must follow */ /* Name must follow */
if (CurTok.Tok != TOK_STRCON) { if (!ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
return; return;
} }
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
@@ -1314,13 +1307,16 @@ static void DoIncBin (void)
Count = Size - Start; Count = Size - Start;
if (Count < 0) { if (Count < 0) {
/* Nothing to read - flag this as a range error */ /* Nothing to read - flag this as a range error */
ErrorSkip ("Range error"); ErrorSkip ("Start offset is larger than file size");
goto Done; goto Done;
} }
} else { } else {
/* Count was given, check if it is valid */ /* Count was given, check if it is valid */
if (Start + Count > Size) { if (Start > Size) {
ErrorSkip ("Range error"); ErrorSkip ("Start offset is larger than file size");
goto Done;
} else if (Start + Count > Size) {
ErrorSkip ("Not enough bytes left in file at offset %ld", Start);
goto Done; goto Done;
} }
} }
@@ -1367,9 +1363,7 @@ static void DoInclude (void)
/* Include another file */ /* Include another file */
{ {
/* Name must follow */ /* Name must follow */
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
SB_Terminate (&CurTok.SVal); SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) { if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
/* Error opening the file, skip remainder of line */ /* Error opening the file, skip remainder of line */
@@ -1386,8 +1380,7 @@ static void DoInterruptor (void)
StrBuf Name = STATIC_STRBUF_INITIALIZER; StrBuf Name = STATIC_STRBUF_INITIALIZER;
/* Symbol name follows */ /* Symbol name follows */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
return; return;
} }
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
@@ -1474,9 +1467,7 @@ static void DoListBytes (void)
static void DoLocalChar (void) static void DoLocalChar (void)
/* Define the character that starts local labels */ /* Define the character that starts local labels */
{ {
if (CurTok.Tok != TOK_CHARCON) { if (ExpectSkip (TOK_CHARCON, "Expected a character constant")) {
ErrorSkip ("Character constant expected");
} else {
if (CurTok.IVal != '@' && CurTok.IVal != '?') { if (CurTok.IVal != '@' && CurTok.IVal != '?') {
Error ("Invalid start character for locals"); Error ("Invalid start character for locals");
} else { } else {
@@ -1492,9 +1483,9 @@ static void DoMacPack (void)
/* Insert a macro package */ /* Insert a macro package */
{ {
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) { if (!ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected"); return;
} else { }
SB_AppendStr (&CurTok.SVal, ".mac"); SB_AppendStr (&CurTok.SVal, ".mac");
SB_Terminate (&CurTok.SVal); SB_Terminate (&CurTok.SVal);
if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) { if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
@@ -1502,7 +1493,6 @@ static void DoMacPack (void)
SkipUntilSep (); SkipUntilSep ();
} }
} }
}
@@ -1538,11 +1528,9 @@ static void DoOrg (void)
static void DoOut (void) static void DoOut (void)
/* Output a string */ /* Output a string */
{ {
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
/* Output the string and be sure to flush the output to keep it in /* Output the string and be sure to flush the output to keep it in
** sync with any error messages if the output is redirected to a file. * sync with any error messages if the output is redirected to a file.
*/ */
printf ("%.*s\n", printf ("%.*s\n",
(int) SB_GetLen (&CurTok.SVal), (int) SB_GetLen (&CurTok.SVal),
@@ -1835,7 +1823,7 @@ static void DoRes (void)
Count = ConstExpression (); Count = ConstExpression ();
if (Count > 0xFFFF || Count < 0) { if (Count > 0xFFFF || Count < 0) {
ErrorSkip ("Range error"); ErrorSkip ("Invalid number of bytes specified");
return; return;
} }
if (CurTok.Tok == TOK_COMMA) { if (CurTok.Tok == TOK_COMMA) {
@@ -1843,7 +1831,7 @@ static void DoRes (void)
Val = ConstExpression (); Val = ConstExpression ();
/* We need a byte value here */ /* We need a byte value here */
if (!IsByteRange (Val)) { if (!IsByteRange (Val)) {
ErrorSkip ("Range error"); ErrorSkip ("Fill value is not in byte range");
return; return;
} }
@@ -1903,12 +1891,10 @@ static void DoScope (void)
static void DoSegment (void) static void DoSegment (void)
/* Switch to another segment */ /* Switch to another segment */
{ {
StrBuf Name = STATIC_STRBUF_INITIALIZER; if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
SegDef Def;
if (CurTok.Tok != TOK_STRCON) { SegDef Def;
ErrorSkip ("String constant expected"); StrBuf Name = AUTO_STRBUF_INITIALIZER;
} else {
/* Save the name of the segment and skip it */ /* Save the name of the segment and skip it */
SB_Copy (&Name, &CurTok.SVal); SB_Copy (&Name, &CurTok.SVal);
@@ -1923,11 +1909,11 @@ static void DoSegment (void)
/* Set the segment */ /* Set the segment */
UseSeg (&Def); UseSeg (&Def);
}
/* Free memory for Name */ /* Free memory for Name */
SB_Done (&Name); SB_Done (&Name);
} }
}
@@ -1935,9 +1921,7 @@ static void DoSetCPU (void)
/* Switch the CPU instruction set */ /* Switch the CPU instruction set */
{ {
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
cpu_t CPU; cpu_t CPU;
/* Try to find the CPU */ /* Try to find the CPU */
@@ -1948,7 +1932,7 @@ static void DoSetCPU (void)
SetCPU (CPU); SetCPU (CPU);
/* Skip the identifier. If the CPU switch was successful, the scanner /* Skip the identifier. If the CPU switch was successful, the scanner
** will treat the input now correctly for the new CPU. * will treat the input now correctly for the new CPU.
*/ */
NextTok (); NextTok ();
} }
@@ -2024,9 +2008,7 @@ static void DoUnDef (void)
EnableDefineStyleMacros (); EnableDefineStyleMacros ();
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) { if (ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
} else {
MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE); MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
NextTok (); NextTok ();
} }
@@ -2046,9 +2028,7 @@ static void DoUnexpected (void)
static void DoWarning (void) static void DoWarning (void)
/* User warning */ /* User warning */
{ {
if (CurTok.Tok != TOK_STRCON) { if (ExpectSkip (TOK_STRCON, "Expected a string constant")) {
ErrorSkip ("String constant expected");
} else {
Warning (0, "User warning: %m%p", &CurTok.SVal); Warning (0, "User warning: %m%p", &CurTok.SVal);
SkipUntilSep (); SkipUntilSep ();
} }
@@ -2255,7 +2235,7 @@ static CtrlDesc CtrlCmdTab [] = {
{ ccNone, DoUnexpected }, /* .REF, .REFERENCED */ { ccNone, DoUnexpected }, /* .REF, .REFERENCED */
{ ccNone, DoReferTo }, /* .REFTO, .REFERTO */ { ccNone, DoReferTo }, /* .REFTO, .REFERTO */
{ ccNone, DoReloc }, /* .RELOC */ { ccNone, DoReloc }, /* .RELOC */
{ ccNone, DoRepeat }, /* .REPEAT */ { ccKeepToken, DoRepeat }, /* .REPEAT */
{ ccNone, DoRes }, /* .RES */ { ccNone, DoRes }, /* .RES */
{ ccNone, DoInvalid }, /* .RIGHT */ { ccNone, DoInvalid }, /* .RIGHT */
{ ccNone, DoROData }, /* .RODATA */ { ccNone, DoROData }, /* .RODATA */

View File

@@ -53,7 +53,7 @@
static TokList* CollectRepeatTokens (void) static TokList* CollectRepeatTokens (const FilePos* StartPos)
/* Collect all tokens inside the .REPEAT body in a token list and return /* Collect all tokens inside the .REPEAT body in a token list and return
** this list. In case of errors, NULL is returned. ** this list. In case of errors, NULL is returned.
*/ */
@@ -67,7 +67,8 @@ static TokList* CollectRepeatTokens (void)
/* Check for end of input */ /* Check for end of input */
if (CurTok.Tok == TOK_EOF) { if (CurTok.Tok == TOK_EOF) {
Error ("Unexpected end of file"); ErrorExpect ("Expected '.ENDREPEAT'");
PNotification (StartPos, "For this '.REPEAT' command");
FreeTokList (List); FreeTokList (List);
return 0; return 0;
} }
@@ -117,10 +118,14 @@ void ParseRepeat (void)
char* Name; char* Name;
TokList* List; TokList* List;
/* Remember the position of the .REPEAT token, then skip it */
FilePos StartPos = CurTok.Pos;
NextTok ();
/* Repeat count follows */ /* Repeat count follows */
long RepCount = ConstExpression (); long RepCount = ConstExpression ();
if (RepCount < 0) { if (RepCount < 0) {
Error ("Range error"); Error ("Repeat count must be positive or zero");
RepCount = 0; RepCount = 0;
} }
@@ -132,9 +137,7 @@ void ParseRepeat (void)
NextTok (); NextTok ();
/* Check for an identifier */ /* Check for an identifier */
if (CurTok.Tok != TOK_IDENT) { if (ExpectSkip (TOK_IDENT, "Expected an identifier")) {
ErrorSkip ("Identifier expected");
} else {
/* Remember the name and skip it */ /* Remember the name and skip it */
SB_Terminate (&CurTok.SVal); SB_Terminate (&CurTok.SVal);
Name = xstrdup (SB_GetConstBuf (&CurTok.SVal)); Name = xstrdup (SB_GetConstBuf (&CurTok.SVal));
@@ -147,7 +150,7 @@ void ParseRepeat (void)
ConsumeSep (); ConsumeSep ();
/* Read the token list */ /* Read the token list */
List = CollectRepeatTokens (); List = CollectRepeatTokens (&StartPos);
/* If we had an error, bail out */ /* If we had an error, bail out */
if (List == 0) { if (List == 0) {

View File

@@ -105,14 +105,21 @@ static long DoStructInternal (long Offs, unsigned Type)
** union, the struct may be anonymous; in which case, no new lexical level ** union, the struct may be anonymous; in which case, no new lexical level
** is started. ** is started.
*/ */
int Anon = (CurTok.Tok != TOK_IDENT); int Anon = (CurTok.Tok == TOK_SEP);
if (!Anon) { if (!Anon) {
/* Non anonymous structs must have an identifier as name */
if (Expect (TOK_IDENT, "Expected a struct/union name")) {
/* Enter a new scope, then skip the name */ /* Enter a new scope, then skip the name */
SymEnterLevel (&CurTok.SVal, SCOPE_STRUCT, ADDR_SIZE_ABS, 0); SymEnterLevel (&CurTok.SVal, SCOPE_STRUCT, ADDR_SIZE_ABS, 0);
NextTok (); NextTok ();
/* Start at zero offset in the new scope */ /* Start at zero offset in the new scope */
Offs = 0; Offs = 0;
} else {
/* Skip the junk on the line before proceeding */
SkipUntilSep ();
Anon = 1;
}
} }
/* Test for end of line */ /* Test for end of line */
@@ -183,7 +190,7 @@ static long DoStructInternal (long Offs, unsigned Type)
case TOK_RES: case TOK_RES:
NextTok (); NextTok ();
if (CurTok.Tok == TOK_SEP) { if (CurTok.Tok == TOK_SEP) {
ErrorSkip ("Size is missing"); ErrorExpect ("Expected a byte count");
} else { } else {
MemberSize = Member (1); MemberSize = Member (1);
} }
@@ -192,7 +199,7 @@ static long DoStructInternal (long Offs, unsigned Type)
case TOK_ORG: case TOK_ORG:
NextTok (); NextTok ();
if (CurTok.Tok == TOK_SEP) { if (CurTok.Tok == TOK_SEP) {
ErrorSkip ("Address is missing"); ErrorExpect ("Expected an address");
} else { } else {
Offs = ConstExpression (); Offs = ConstExpression ();
@@ -233,7 +240,12 @@ static long DoStructInternal (long Offs, unsigned Type)
default: default:
if (!CheckConditionals ()) { if (!CheckConditionals ()) {
/* Not a conditional directive */ /* Not a conditional directive */
ErrorSkip ("Invalid storage allocator in struct/union"); if (Sym) {
ErrorExpect ("Expected a storage allocator after the field name");
} else {
ErrorExpect ("Expected a storage allocator");
}
SkipUntilSep ();
} }
} }
@@ -275,9 +287,9 @@ static long DoStructInternal (long Offs, unsigned Type)
/* End of struct/union definition */ /* End of struct/union definition */
if (Type == STRUCT) { if (Type == STRUCT) {
Consume (TOK_ENDSTRUCT, "'.ENDSTRUCT' expected"); Consume (TOK_ENDSTRUCT, "Expected '.ENDSTRUCT'");
} else { } else {
Consume (TOK_ENDUNION, "'.ENDUNION' expected"); Consume (TOK_ENDUNION, "Expected '.ENDUNION'");
} }
/* Return the size of the struct */ /* Return the size of the struct */

View File

@@ -1359,8 +1359,7 @@ static void StudyNearAddr (ExprNode* Expr, ExprDesc* D)
} }
/* Promote to absolute if smaller. */ /* Promote to absolute if smaller. */
if (D->AddrSize < ADDR_SIZE_ABS) if (D->AddrSize < ADDR_SIZE_ABS) {
{
D->AddrSize = ADDR_SIZE_ABS; D->AddrSize = ADDR_SIZE_ABS;
} }
} }

View File

@@ -73,7 +73,7 @@ SymTable* ParseScopedIdent (StrBuf* Name, StrBuf* FullName)
/* Start from the root scope */ /* Start from the root scope */
Scope = RootScope; Scope = RootScope;
} else if (CurTok.Tok == TOK_IDENT) { } else if (Expect (TOK_IDENT, "Expected an identifier")) {
/* Remember the name and skip it */ /* Remember the name and skip it */
SB_Copy (Name, &CurTok.SVal); SB_Copy (Name, &CurTok.SVal);
@@ -115,8 +115,7 @@ SymTable* ParseScopedIdent (StrBuf* Name, StrBuf* FullName)
while (1) { while (1) {
/* Next token must be an identifier. */ /* Next token must be an identifier. */
if (CurTok.Tok != TOK_IDENT) { if (!Expect (TOK_IDENT, "Expected an identifier")) {
Error ("Identifier expected");
return 0; return 0;
} }

View File

@@ -216,6 +216,10 @@ void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags
if (S->Flags & SF_IMPORT) { if (S->Flags & SF_IMPORT) {
/* Defined symbol is marked as imported external symbol */ /* Defined symbol is marked as imported external symbol */
Error ("Symbol '%m%p' is already an import", GetSymName (S)); Error ("Symbol '%m%p' is already an import", GetSymName (S));
if (CollCount (&S->DefLines) > 0) {
PNotification (GetSourcePos (CollAt(&S->DefLines, 0)),
"The symbol was previously imported here");
}
return; return;
} }
if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) { if ((Flags & SF_VAR) != 0 && (S->Flags & (SF_EXPORT | SF_GLOBAL))) {
@@ -227,6 +231,10 @@ void SymDef (SymEntry* S, ExprNode* Expr, unsigned char AddrSize, unsigned Flags
/* Multiple definition. In case of a variable, this is legal. */ /* Multiple definition. In case of a variable, this is legal. */
if ((S->Flags & SF_VAR) == 0) { if ((S->Flags & SF_VAR) == 0) {
Error ("Symbol '%m%p' is already defined", GetSymName (S)); Error ("Symbol '%m%p' is already defined", GetSymName (S));
if (CollCount (&S->DefLines) > 0) {
PNotification (GetSourcePos (CollAt(&S->DefLines, 0)),
"The symbol was previously defined here");
}
S->Flags |= SF_MULTDEF; S->Flags |= SF_MULTDEF;
return; return;
} else { } else {

View File

@@ -33,30 +33,261 @@
#include <stdio.h>
/* ca65 */ /* ca65 */
#include "token.h" #include "token.h"
/*****************************************************************************/ /*****************************************************************************/
/* Code */ /* Data */
/*****************************************************************************/ /*****************************************************************************/
int TokHasSVal (token_t Tok) /* Use a struct so we can extend it if required */
/* Return true if the given token has an attached SVal */ typedef struct TokDescEntry TokDescEntry;
{ struct TokDescEntry {
return (Tok == TOK_IDENT || Tok == TOK_LOCAL_IDENT || Tok == TOK_STRCON); const char* Desc;
} };
static const TokDescEntry TokDesc[] = {
{ "none", },
{ "end-of-file", },
{ "end-of-line", },
{ "{s}", },
{ "{s}", },
{ "{i}", },
{ "'{c}'", },
{ "\"{s}\"", },
{ "A", },
{ "Q", },
{ "S", },
{ "X", },
{ "Y", },
{ "Z", },
{ "R{i}", },
{ ":=", },
{ "unnamed label", },
{ "=", },
{ "<>", },
{ "<", },
{ ">", },
{ "<=", },
{ ">=", },
{ ".AND", },
{ ".OR", },
{ ".XOR", },
{ ".NOT", },
{ "+", },
{ "-", },
{ "*", },
{ "/", },
{ "!", },
{ "|", },
{ "^", },
{ "&", },
{ "<<", },
{ ">>", },
{ "~", },
{ "$", },
{ "::", },
{ ".", },
{ ",", },
{ "#", },
{ ":", },
{ "(", },
{ ")", },
{ "[", },
{ "]", },
{ "{", },
{ "}", },
{ "@", },
{ "z:", },
{ "a:", },
{ "f:", },
{ "macro parameter", },
{ "repeat counter", },
{ ".A16" },
{ ".A8" },
{ ".ADDR" },
{ ".ADDRSIZE" },
{ ".ALIGN" },
{ ".ASCIIZ" },
{ ".ASIZE" },
{ ".ASSERT" },
{ ".AUTOIMPORT" },
{ ".BANK" },
{ ".BANKBYTE" },
{ ".BANKBYTES" },
{ ".BLANK" },
{ ".BSS" },
{ ".BYTE" },
{ ".CAP" },
{ ".CASE" },
{ ".CHARMAP" },
{ ".CODE" },
{ ".CONCAT" },
{ ".CONDES" },
{ ".CONST" },
{ ".CONSTRUCTOR" },
{ ".CPU" },
{ ".DATA" },
{ ".DBG" },
{ ".DBYT" },
{ ".DEBUGINFO" },
{ ".DEFINE" },
{ ".DEFINED" },
{ ".DEFINEDMACRO" },
{ ".DELMAC" },
{ ".DESTRUCTOR" },
{ ".DWORD" },
{ ".ELSE" },
{ ".ELSEIF" },
{ ".END" },
{ ".ENDENUM" },
{ ".ENDIF" },
{ ".ENDMACRO" },
{ ".ENDPROC" },
{ ".ENDREP" },
{ ".ENDSCOPE" },
{ ".ENDSTRUCT" },
{ ".ENDUNION" },
{ ".ENUM" },
{ ".ERROR" },
{ ".EXITMACRO" },
{ ".EXPORT" },
{ ".EXPORTZP" },
{ ".FARADDR" },
{ ".FATAL" },
{ ".FEATURE" },
{ ".FILEOPT" },
{ ".FORCEIMPORT" },
{ ".FORCEWORD" },
{ ".GLOBAL" },
{ ".GLOBALZP" },
{ ".HIBYTE" },
{ ".HIBYTES" },
{ ".HIWORD" },
{ ".I16" },
{ ".I8" },
{ ".MAKEIDENT" },
{ ".IF" },
{ ".IFBLANK" },
{ ".IFCONST" },
{ ".IFDEF" },
{ ".IFNBLANK" },
{ ".IFNCONST" },
{ ".IFNDEF" },
{ ".IFNREF" },
{ ".IFP02" },
{ ".IFP02X" },
{ ".IFP4510" },
{ ".IFP45GS02" },
{ ".IFP6280" },
{ ".IFP816" },
{ ".IFPC02" },
{ ".IFPCE02" },
{ ".IFPDTV" },
{ ".IFPM740" },
{ ".IFPSC02" },
{ ".IFPSWEET16" },
{ ".IFPWC02" },
{ ".IFREF" },
{ ".IMPORT" },
{ ".IMPORTZP" },
{ ".INCBIN" },
{ ".INCLUDE" },
{ ".INTERRUPTOR" },
{ ".ISIZE" },
{ ".ISMNEMONIC" },
{ ".LEFT" },
{ ".LINECONT" },
{ ".LIST" },
{ ".LISTBYTES" },
{ ".LITERAL" },
{ ".LOBYTE" },
{ ".LOBYTES" },
{ ".LOCAL" },
{ ".LOCALCHAR" },
{ ".LOWORD" },
{ ".MACPACK" },
{ ".MACRO" },
{ ".MATCH" },
{ ".MAX" },
{ ".MID" },
{ ".MIN" },
{ ".NULL" },
{ ".ORG" },
{ ".OUT" },
{ ".P02" },
{ ".P02X" },
{ ".P4510" },
{ ".P45GS02" },
{ ".P6280" },
{ ".P816" },
{ ".PAGELENGTH" },
{ ".PARAMCOUNT" },
{ ".PC02" },
{ ".PCE02" },
{ ".PDTV" },
{ ".PM740" },
{ ".POPCHARMAP" },
{ ".POPCPU" },
{ ".POPSEG" },
{ ".PROC" },
{ ".PSC02" },
{ ".PSWEET16" },
{ ".PUSHCHARMAP" },
{ ".PUSHCPU" },
{ ".PUSHSEG" },
{ ".PWC02" },
{ ".REFERENCED" },
{ ".REFERTO" },
{ ".RELOC" },
{ ".REPEAT" },
{ ".RES" },
{ ".RIGHT" },
{ ".RODATA" },
{ ".SCOPE" },
{ ".SEGMENT" },
{ ".SET" },
{ ".SETCPU" },
{ ".SIZEOF" },
{ ".SMART" },
{ ".SPRINTF" },
{ ".STRAT" },
{ ".STRING" },
{ ".STRLEN" },
{ ".STRUCT" },
{ ".TAG" },
{ ".TCOUNT" },
{ ".TIME" },
{ ".UNDEF" },
{ ".UNION" },
{ ".VERSION" },
{ ".WARNING" },
{ ".WORD" },
{ ".XMATCH" },
{ ".ZEROPAGE" },
};
int TokHasIVal (token_t Tok) /*****************************************************************************/
/* Return true if the given token has an attached IVal */ /* Code */
{ /*****************************************************************************/
return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_REG);
}
@@ -72,3 +303,54 @@ void CopyToken (Token* Dst, const Token* Src)
SB_Copy (&Dst->SVal, &Src->SVal); SB_Copy (&Dst->SVal, &Src->SVal);
Dst->Pos = Src->Pos; Dst->Pos = Src->Pos;
} }
StrBuf* TokenDesc (const Token* T, StrBuf* S)
/* Place a textual description of the given token into S. */
{
PRECONDITION (sizeof (TokDesc) / sizeof (TokDesc[0]) == TOK_COUNT);
/* Clear the target buffer */
SB_Clear (S);
/* Get the description for the token */
const char* Desc = TokDesc[T->Tok].Desc;
/* Repeatedly replace {c}, {i} and {s} */
size_t Start = 0;
while (1) {
const char* P = strchr (Desc + Start, '{');
if (P) {
/* Check if this is really {c}, {i} or {s} */
if ((P[1] != 'c' &&
P[1] != 'i' &&
P[1] != 's') ||
P[2] != '}') {
++Start;
continue;
}
/* Append the text before the replacement token */
SB_AppendBuf (S, Desc + Start, P - (Desc + Start));
Start += P - (Desc + Start) + 3;
/* Append the replacement text */
if (P[1] == 'c') {
SB_AppendChar (S, (char)T->IVal);
} else if (P[1] == 'i') {
char Buf[64];
snprintf (Buf, sizeof (Buf), "%ld", T->IVal);
SB_AppendStr (S, Buf);
} else {
SB_Append (S, &T->SVal);
}
} else {
/* No more replacements found, append remainder */
SB_AppendStr (S, Desc + Start);
break;
}
}
/* Zero-terminate the buffer and return it */
SB_Terminate (S);
return S;
}

View File

@@ -62,18 +62,21 @@ typedef enum token_t {
TOK_CHARCON, /* Character constant */ TOK_CHARCON, /* Character constant */
TOK_STRCON, /* String constant */ TOK_STRCON, /* String constant */
TOK_A, /* A)ccumulator */ TOK_FIRSTREG, /* First register name token */
TOK_A = TOK_FIRSTREG, /* A)ccumulator */
TOK_Q, /* Q pseudo register */
TOK_S, /* S register */
TOK_X, /* X register */ TOK_X, /* X register */
TOK_Y, /* Y register */ TOK_Y, /* Y register */
TOK_Z, /* Z register */ TOK_Z, /* Z register */
TOK_S, /* S register */
TOK_Q, /* Q pseudo register */
TOK_REG, /* Sweet16 R.. register (in sweet16 mode) */ TOK_REG, /* Sweet16 R.. register (in sweet16 mode) */
TOK_LASTREG = TOK_REG, /* Last register name token */
TOK_ASSIGN, /* := */ TOK_ASSIGN, /* := */
TOK_ULABEL, /* An unnamed label */ TOK_ULABEL, /* An unnamed label */
TOK_EQ, /* = */ TOK_FIRSTOP, /* First operator token */
TOK_EQ = TOK_FIRSTOP, /* = */
TOK_NE, /* <> */ TOK_NE, /* <> */
TOK_LT, /* < */ TOK_LT, /* < */
TOK_GT, /* > */ TOK_GT, /* > */
@@ -97,6 +100,7 @@ typedef enum token_t {
TOK_SHL, /* << */ TOK_SHL, /* << */
TOK_SHR, /* >> */ TOK_SHR, /* >> */
TOK_NOT, /* ~ */ TOK_NOT, /* ~ */
TOK_LASTOP = TOK_NOT, /* Last operator token */
TOK_PC, /* $ if enabled */ TOK_PC, /* $ if enabled */
TOK_NAMESPACE, /* :: */ TOK_NAMESPACE, /* :: */
@@ -317,11 +321,27 @@ struct Token {
int TokHasSVal (token_t Tok); #if defined(HAVE_INLINE)
INLINE int TokHasSVal (token_t Tok)
/* Return true if the given token has an attached SVal */ /* Return true if the given token has an attached SVal */
{
return (Tok == TOK_IDENT || Tok == TOK_LOCAL_IDENT || Tok == TOK_STRCON);
}
#else
# define TokHasSVal(T) \
((T) == TOK_IDENT || (T) == TOK_LOCAL_IDENT || (T) == TOK_STRCON)
#endif
int TokHasIVal (token_t Tok); #if defined(HAVE_INLINE)
INLINE int TokHasIVal (token_t Tok)
/* Return true if the given token has an attached IVal */ /* Return true if the given token has an attached IVal */
{
return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_REG);
}
#else
# define TokHasIVal(T) \
((T) == TOK_INTCON || (T) == TOK_CHARCON || (T) == TOK_REG)
#endif
static inline int TokIsSep (enum token_t T) static inline int TokIsSep (enum token_t T)
/* Return true if this is a separator token */ /* Return true if this is a separator token */
@@ -334,6 +354,9 @@ void CopyToken (Token* Dst, const Token* Src);
** initialized. ** initialized.
*/ */
StrBuf* TokenDesc (const Token* T, StrBuf* S);
/* Place a textual description of the given token into S. */
/* End of token.h */ /* End of token.h */

View File

@@ -1,6 +1,4 @@
.macpack cpu
; step 1: try to assemble an instruction that's exclusive to this set ; step 1: try to assemble an instruction that's exclusive to this set
; (when possible) ; (when possible)

View File

@@ -0,0 +1 @@
0:

View File

@@ -0,0 +1 @@
foo

View File

@@ -0,0 +1,3 @@
.struct x
.word
.endstruct

View File

@@ -0,0 +1 @@
lda #$00 foo

View File

@@ -0,0 +1 @@
lda ($00),a

View File

@@ -0,0 +1 @@
lda ($00,a)

View File

@@ -0,0 +1,3 @@
.struct foo
bar
.endstruct

View File

@@ -0,0 +1 @@
127

View File

@@ -0,0 +1 @@
.fileopt author

View File

@@ -0,0 +1 @@
.assert 1

View File

@@ -0,0 +1,2 @@
lda |

View File

@@ -0,0 +1 @@
.byte 0 1

View File

@@ -0,0 +1 @@
.define debug(x) .out x

View File

@@ -0,0 +1,3 @@
.repeat 10
.byte $00

View File

@@ -0,0 +1,4 @@
.import foo
foo = 3

View File

@@ -0,0 +1,5 @@
foo = 3
foo = 2

View File

@@ -0,0 +1,5 @@
.macro mac
lda #$00
.byte 3

View File

@@ -0,0 +1,5 @@
.mac lda, val
ldx #val
txa

View File

@@ -0,0 +1,4 @@
.macro mac
.local .endmacro
.endmacro
mac

View File

@@ -0,0 +1,6 @@
.macro mac val
lda #val
.endmacro
.macro mac val
lda #val
.endmacro

View File

@@ -0,0 +1,5 @@
.macro foo a1, a2
.byte a1, a2
.endmacro
foo 1, 2, 3

View File

@@ -0,0 +1,7 @@
.macro mac1
.delmac mac2
.endmacro
.macro mac2
mac1
.endmacro
mac2

View File

@@ -0,0 +1,6 @@
.macro mac
.define endmac .endmacro
.endmacro
.macro mac
.define endmac .endmacro
.endmacro

View File

@@ -1,6 +1,6 @@
110-capabilities.s:3: Error: Arguments to .CAPABILITY must be identifiers 110-capabilities.s:3: Error: Expected a capability name but found ')'
110-capabilities.s:8: Error: Arguments to .CAPABILITY must be identifiers 110-capabilities.s:8: Error: Expected a capability name but found 'end-of-line'
110-capabilities.s:8: Error: ')' expected 110-capabilities.s:8: Error: Expected ')' but found 'end-of-line'
110-capabilities.s:12: Error: Not a valid capability name: CPU_HAS_BR 110-capabilities.s:12: Error: Not a valid capability name: CPU_HAS_BR
110-capabilities.s:17: Error: ')' expected 110-capabilities.s:17: Error: Expected ')' but found 'cpu_has_bra8'
110-capabilities.s:17: Error: Unexpected trailing garbage characters 110-capabilities.s:17: Error: Expected 'end-of-line' but found 'cpu_has_bra8'

View File

@@ -0,0 +1 @@
120-errormsg.s:1: Error: Expected a mnemonic but found '0'

View File

@@ -0,0 +1 @@
121-errormsg.s:1: Error: Expected ':' after identifier to form a label but found 'end-of-line'

View File

@@ -0,0 +1 @@
122-errormsg.s:1: Error: Expected a struct/union name but found 'X'

View File

@@ -0,0 +1 @@
123-errormsg.s:1: Error: Expected 'end-of-line' but found 'foo'

View File

@@ -0,0 +1 @@
124-errormsg.s:1: Error: Expected 'Y' but found 'A'

View File

@@ -0,0 +1,2 @@
125-errormsg.s:1: Error: Expected 'X' or 'S' but found 'A'
125-errormsg.s:1: Error: Illegal addressing mode

View File

@@ -0,0 +1 @@
126-errormsg.s:2: Error: Expected a storage allocator after the field name but found 'end-of-line'

View File

@@ -0,0 +1 @@
127-errormsg.s:1: Error: Expected a mnemonic but found '127'

View File

@@ -0,0 +1 @@
128-errormsg.s:1: Error: Expected ',' but found 'end-of-line'

View File

@@ -0,0 +1 @@
129-errormsg.s:1: Error: Expected ',' but found 'end-of-line'

View File

@@ -0,0 +1,2 @@
130-errormsg.s:1: Error: Expected an expression but found '|'
130-errormsg.s:1: Error: Expected an expression but found 'end-of-line'

View File

@@ -0,0 +1 @@
131-errormsg.s:1: Error: Expected 'end-of-line' but found '1'

View File

@@ -0,0 +1,2 @@
132-errormsg.s:1: Error: Expected a parameter name but found 'X'
132-errormsg.s:1: Error: Expected ')' but found 'X'

View File

@@ -0,0 +1,2 @@
133-errormsg.s:3: Error: Expected '.ENDREPEAT' but found 'end-of-file'
133-errormsg.s:1: Note: For this '.REPEAT' command

View File

@@ -0,0 +1,2 @@
134-errormsg.s:3: Error: Symbol 'foo' is already an import
134-errormsg.s:1: Note: The symbol was previously imported here

View File

@@ -0,0 +1,2 @@
135-errormsg.s:4: Error: Symbol 'foo' is already defined
135-errormsg.s:1: Note: The symbol was previously defined here

View File

@@ -0,0 +1,2 @@
136-errormsg.s:5: Error: Missing '.ENDMACRO' for definition of macro 'mac'
136-errormsg.s:1: Note: Macro definition started here

View File

@@ -0,0 +1,3 @@
137-errormsg.s:1: Error: Cannot use an instruction as macro name
137-errormsg.s:5: Error: Expected '.ENDMACRO' but found 'end-of-file'
137-errormsg.s:1: Note: Macro definition started here

View File

@@ -0,0 +1,3 @@
138-errormsg.s:2: Error: Expected an identifier but found '.ENDMACRO'
138-errormsg.s:4: Error: Macro 'mac' contains errors and cannot be expanded
138-errormsg.s:1: Note: Definition of macro 'mac' was here

View File

@@ -0,0 +1,2 @@
139-errormsg.s:4: Error: A macro named 'mac' is already defined
139-errormsg.s:1: Note: Previous definition of macro 'mac' was here

View File

@@ -0,0 +1,2 @@
140-errormsg.s:5: Error: Too many parameters for macro 'foo'
140-errormsg.s:1: Note: See definition of macro 'foo' which was here

View File

@@ -0,0 +1,3 @@
141-errormsg.s:7: Error: Cannot delete macro 'mac2' which is currently expanded
141-errormsg.s:5: Note: Expanded from macro here
141-errormsg.s:2: Note: Expanded from macro here

View File

@@ -0,0 +1,2 @@
142-errormsg.s:4: Error: A macro named 'mac' is already defined
142-errormsg.s:1: Note: Previous definition of macro 'mac' was here