diff --git a/src/ca65/macro.c b/src/ca65/macro.c index 67c6b7b9b..d85fbcc90 100644 --- a/src/ca65/macro.c +++ b/src/ca65/macro.c @@ -290,6 +290,9 @@ void MacDef (unsigned Style) /* Define the macro */ M = NewMacro (SVal, HashVal, Style); + + /* Switch to raw token mode and skip the macro name */ + EnterRawTokenMode (); NextTok (); /* If we have a DEFINE style macro, we may have parameters in braces, @@ -371,7 +374,7 @@ void MacDef (unsigned Style) /* May not have end of file in a macro definition */ if (Tok == TOK_EOF) { Error (ERR_ENDMACRO_EXPECTED); - return; + goto Done; } } else { /* Accept a newline or end of file for new style macros */ @@ -454,6 +457,10 @@ void MacDef (unsigned Style) if (Style == MAC_STYLE_CLASSIC) { NextTok (); } + +Done: + /* Switch out of raw token mode */ + LeaveRawTokenMode (); } @@ -650,7 +657,7 @@ static void StartExpDefine (Macro* M) /* A define style macro must be called with as many actual parameters * as there are formal ones. Get the parameter count. - */ + */ unsigned Count = M->ParamCount; /* Skip the current token */ diff --git a/src/ca65/main.c b/src/ca65/main.c index 46f7eba39..21d6a613e 100644 --- a/src/ca65/main.c +++ b/src/ca65/main.c @@ -328,33 +328,31 @@ static void OneLine (void) * is no colon, it's an assignment. */ if (Tok == TOK_EQ) { - /* Skip the '=' */ - NextTok (); - /* Define the symbol with the expression following the - * '=' - */ - SymDef (Ident, Expression (), 0); - /* Don't allow anything after a symbol definition */ - Done = 1; + /* Skip the '=' */ + NextTok (); + /* Define the symbol with the expression following the '=' */ + SymDef (Ident, Expression (), 0); + /* Don't allow anything after a symbol definition */ + Done = 1; } else { - /* Define a label */ - SymDef (Ident, CurrentPC (), IsZPSeg ()); - /* Skip the colon. If NoColonLabels is enabled, allow labels - * without a colon if there is no whitespace before the - * identifier. - */ - if (Tok != TOK_COLON) { - if (HadWS || !NoColonLabels) { - Error (ERR_COLON_EXPECTED); - } - if (Tok == TOK_NAMESPACE) { - /* Smart :: handling */ - NextTok (); - } - } else { + /* Define a label */ + SymDef (Ident, CurrentPC (), IsZPSeg ()); + /* Skip the colon. If NoColonLabels is enabled, allow labels + * without a colon if there is no whitespace before the + * identifier. + */ + if (Tok != TOK_COLON) { + if (HadWS || !NoColonLabels) { + Error (ERR_COLON_EXPECTED); + } + if (Tok == TOK_NAMESPACE) { + /* Smart :: handling */ + NextTok (); + } + } else { /* Skip the colon */ - NextTok (); - } + NextTok (); + } } } } diff --git a/src/ca65/nexttok.c b/src/ca65/nexttok.c index 72c80708a..9504e2928 100644 --- a/src/ca65/nexttok.c +++ b/src/ca65/nexttok.c @@ -43,6 +43,16 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +static unsigned RawMode = 0; /* Raw token mode flag/counter */ + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -63,28 +73,28 @@ static TokList* CollectTokens (unsigned Start, unsigned Count) unsigned Parens = 0; while (Parens != 0 || Tok != TOK_RPAREN) { - /* Check for end of line or end of input */ - if (Tok == TOK_SEP || Tok == TOK_EOF) { - Error (ERR_UNEXPECTED_EOL); - return List; - } + /* Check for end of line or end of input */ + if (Tok == TOK_SEP || Tok == TOK_EOF) { + Error (ERR_UNEXPECTED_EOL); + return List; + } - /* Collect tokens in the given range */ - if (Current >= Start && Current < Start+Count) { - /* Add the current token to the list */ - AddCurTok (List); - } + /* Collect tokens in the given range */ + if (Current >= Start && Current < Start+Count) { + /* Add the current token to the list */ + AddCurTok (List); + } - /* Check for and count parenthesii */ - if (Tok == TOK_LPAREN) { - ++Parens; - } else if (Tok == TOK_RPAREN) { - --Parens; - } + /* Check for and count parenthesii */ + if (Tok == TOK_LPAREN) { + ++Parens; + } else if (Tok == TOK_RPAREN) { + --Parens; + } - /* Get the next token */ - ++Current; - NextTok (); + /* Get the next token */ + ++Current; + NextTok (); } /* Eat the closing paren */ @@ -165,6 +175,47 @@ static void FuncConcat (void) +static void FuncLeft (void) +/* Handle the .LEFT function */ +{ + long Count; + TokList* List; + + /* Skip it */ + NextTok (); + + /* Left paren expected */ + ConsumeLParen (); + + /* Count argument */ + Count = ConstExpression (); + if (Count < 0 || Count > 100) { + Error (ERR_RANGE); + Count = 1; + } + ConsumeComma (); + + /* Read the token list */ + List = CollectTokens (0, (unsigned) Count); + + /* Since we want to insert the list before the now current token, we have + * to save the current token in some way and then skip it. To do this, we + * will add the current token at the end of the token list (so the list + * will never be empty), push the token list, and then skip the current + * token. This will replace the current token by the first token from the + * list (which will be the old current token in case the list was empty). + */ + AddCurTok (List); + + /* Insert it into the scanner feed */ + PushTokList (List, ".LEFT"); + + /* Skip the current token */ + NextTok (); +} + + + static void FuncMid (void) /* Handle the .MID function */ { @@ -181,7 +232,7 @@ static void FuncMid (void) /* Start argument */ Start = ConstExpression (); if (Start < 0 || Start > 100) { - Error (ERR_RANGE); + Error (ERR_RANGE); Start = 0; } ConsumeComma (); @@ -189,16 +240,84 @@ static void FuncMid (void) /* Count argument */ Count = ConstExpression (); if (Count < 0 || Count > 100) { - Error (ERR_RANGE); - Count = 1; + Error (ERR_RANGE); + Count = 1; } ConsumeComma (); /* Read the token list */ List = CollectTokens ((unsigned) Start, (unsigned) Count); + /* Since we want to insert the list before the now current token, we have + * to save the current token in some way and then skip it. To do this, we + * will add the current token at the end of the token list (so the list + * will never be empty), push the token list, and then skip the current + * token. This will replace the current token by the first token from the + * list (which will be the old current token in case the list was empty). + */ + AddCurTok (List); + /* Insert it into the scanner feed */ PushTokList (List, ".MID"); + + /* Skip the current token */ + NextTok (); +} + + + +static void FuncRight (void) +/* Handle the .RIGHT function */ +{ + long Count; + TokList* List; + + /* Skip it */ + NextTok (); + + /* Left paren expected */ + ConsumeLParen (); + + /* Count argument */ + Count = ConstExpression (); + if (Count < 0 || Count > 100) { + Error (ERR_RANGE); + Count = 1; + } + ConsumeComma (); + + /* Read the complete token list */ + List = CollectTokens (0, 9999); + + /* Delete tokens from the list until Count tokens are remaining */ + while (List->Count > Count) { + /* Get the first node */ + TokNode* T = List->Root; + + /* Remove it from the list */ + List->Root = List->Root->Next; + + /* Free the node */ + FreeTokNode (T); + + /* Corrent the token counter */ + List->Count--; + } + + /* Since we want to insert the list before the now current token, we have + * to save the current token in some way and then skip it. To do this, we + * will add the current token at the end of the token list (so the list + * will never be empty), push the token list, and then skip the current + * token. This will replace the current token by the first token from the + * list (which will be the old current token in case the list was empty). + */ + AddCurTok (List); + + /* Insert it into the scanner feed */ + PushTokList (List, ".RIGHT"); + + /* Skip the current token */ + NextTok (); } @@ -244,25 +363,37 @@ void NextTok (void) /* Get the next raw token */ NextRawTok (); - /* Check for token handling functions */ - switch (Tok) { + /* In raw mode, pass the token unchanged */ + if (RawMode == 0) { - case TOK_CONCAT: - FuncConcat (); - break; + /* Execute token handling functions */ + switch (Tok) { - case TOK_MID: - FuncMid (); - break; + case TOK_CONCAT: + FuncConcat (); + break; - case TOK_STRING: - FuncString (); - break; + case TOK_LEFT: + FuncLeft (); + break; - default: - /* Quiet down gcc */ - break; + case TOK_MID: + FuncMid (); + break; + case TOK_RIGHT: + FuncRight (); + break; + + case TOK_STRING: + FuncString (); + break; + + default: + /* Quiet down gcc */ + break; + + } } } @@ -321,7 +452,7 @@ void ConsumeComma (void) void SkipUntilSep (void) -/* Skip tokens until we reach a line separator */ +/* Skip tokens until we reach a line separator or end of file */ { while (Tok != TOK_SEP && Tok != TOK_EOF) { NextTok (); @@ -330,3 +461,25 @@ void SkipUntilSep (void) +void EnterRawTokenMode (void) +/* Enter raw token mode. In raw mode, token handling functions are not + * executed, but the function tokens are passed untouched to the upper + * layer. Raw token mode is used when storing macro tokens for later + * use. + * Calls to EnterRawTokenMode and LeaveRawTokenMode may be nested. + */ +{ + ++RawMode; +} + + + +void LeaveRawTokenMode (void) +/* Leave raw token mode. */ +{ + PRECONDITION (RawMode > 0); + --RawMode; +} + + + diff --git a/src/ca65/nexttok.h b/src/ca65/nexttok.h index bc63b699e..cb38da7bc 100644 --- a/src/ca65/nexttok.h +++ b/src/ca65/nexttok.h @@ -67,7 +67,18 @@ void ConsumeComma (void); /* Consume a comma */ void SkipUntilSep (void); -/* Skip tokens until we reach a line separator */ +/* Skip tokens until we reach a line separator or end of file */ + +void EnterRawTokenMode (void); +/* Enter raw token mode. In raw mode, token handling functions are not + * executed, but the function tokens are passed untouched to the upper + * layer. Raw token mode is used when storing macro tokens for later + * use. + * Calls to EnterRawTokenMode and LeaveRawTokenMode may be nested. + */ + +void LeaveRawTokenMode (void); +/* Leave raw token mode. */ diff --git a/src/ca65/pseudo.c b/src/ca65/pseudo.c index c00d75a9c..ea222968f 100644 --- a/src/ca65/pseudo.c +++ b/src/ca65/pseudo.c @@ -75,6 +75,16 @@ static char Keyword [sizeof (SVal)+1] = "."; static void DoUnexpected (void); +/* Got an unexpected keyword */ + +static void DoInvalid (void); +/* Handle a token that is invalid here, since it should have been handled on + * a much lower level of the expression hierarchy. Getting this sort of token + * means that the lower level code has bugs. + * This function differs to DoUnexpected in that the latter may be triggered + * by the user by using keywords in the wrong location. DoUnexpected is not + * an error in the assembler itself, while DoInvalid is. + */ @@ -696,6 +706,20 @@ static void DoInclude (void) +static void DoInvalid (void) +/* Handle a token that is invalid here, since it should have been handled on + * a much lower level of the expression hierarchy. Getting this sort of token + * means that the lower level code has bugs. + * This function differs to DoUnexpected in that the latter may be triggered + * by the user by using keywords in the wrong location. DoUnexpected is not + * an error in the assembler itself, while DoInvalid is. + */ +{ + Internal ("Unexpected token: %s", Keyword); +} + + + static void DoLineCont (void) /* Switch the use of line continuations */ { @@ -788,16 +812,6 @@ static void DoMacro (void) -static void DoMid (void) -/* Handle .MID - this should never happen, since the keyword is actually - * handled on a much lower level of the expression hierarchy. - */ -{ - Internal ("Unexpected token: .MID"); -} - - - static void DoNull (void) /* Switch to the NULL segment */ { @@ -1121,6 +1135,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoImportZP }, { ccNone, DoIncBin }, { ccNone, DoInclude }, + { ccNone, DoInvalid }, /* .LEFT */ { ccNone, DoLineCont }, { ccNone, DoList }, { ccNone, DoListBytes }, @@ -1129,7 +1144,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoMacPack }, { ccNone, DoMacro }, { ccNone, DoUnexpected }, /* .MATCH */ - { ccNone, DoMid }, + { ccNone, DoInvalid }, /* .MID */ { ccNone, DoNull }, { ccNone, DoOrg }, { ccNone, DoOut }, @@ -1143,6 +1158,7 @@ static CtrlDesc CtrlCmdTab [] = { { ccNone, DoReloc }, { ccNone, DoRepeat }, { ccNone, DoRes }, + { ccNone, DoInvalid }, /* .RIGHT */ { ccNone, DoROData }, { ccNone, DoSegment }, { ccNone, DoSmart }, diff --git a/src/ca65/scanner.c b/src/ca65/scanner.c index 357144aa1..c737f6c6f 100644 --- a/src/ca65/scanner.c +++ b/src/ca65/scanner.c @@ -184,6 +184,7 @@ struct DotKeyword { { "IMPORTZP", TOK_IMPORTZP }, { "INCBIN", TOK_INCBIN }, { "INCLUDE", TOK_INCLUDE }, + { "LEFT", TOK_LEFT }, { "LINECONT", TOK_LINECONT }, { "LIST", TOK_LIST }, { "LISTBYTES", TOK_LISTBYTES }, @@ -212,6 +213,7 @@ struct DotKeyword { { "RELOC", TOK_RELOC }, { "REPEAT", TOK_REPEAT }, { "RES", TOK_RES }, + { "RIGHT", TOK_RIGHT }, { "RODATA", TOK_RODATA }, { "SEGMENT", TOK_SEGMENT }, { "SHL", TOK_SHL }, diff --git a/src/ca65/scanner.h b/src/ca65/scanner.h index f28e30d47..dea582994 100644 --- a/src/ca65/scanner.h +++ b/src/ca65/scanner.h @@ -161,6 +161,7 @@ enum Token { TOK_IMPORTZP, TOK_INCBIN, TOK_INCLUDE, + TOK_LEFT, TOK_LINECONT, TOK_LIST, TOK_LISTBYTES, @@ -182,7 +183,8 @@ enum Token { TOK_REFERENCED, TOK_RELOC, TOK_REPEAT, - TOK_RES, + TOK_RES, + TOK_RIGHT, TOK_RODATA, TOK_SEGMENT, TOK_SMART, diff --git a/src/ca65/toklist.c b/src/ca65/toklist.c index fc4eec608..ab93a6766 100644 --- a/src/ca65/toklist.c +++ b/src/ca65/toklist.c @@ -37,6 +37,7 @@ #include "../common/xmalloc.h" +#include "error.h" #include "istack.h" #include "scanner.h" #include "toklist.h" @@ -191,9 +192,15 @@ static int ReplayTokList (void* List) /* Cast the generic pointer to an actual list */ TokList* L = List; + /* Last may never be a NULL pointer, otherwise there's a bug in the code */ + CHECK (L->Last != 0); + /* Set the next token from the list */ TokSet (L->Last); + /* Set the pointer to the next token */ + L->Last = L->Last->Next; + /* If this was the last token, decrement the repeat counter. If it goes * zero, delete the list and remove the function from the stack. */