From 23fbf3ff2a8956483bffaf1943967e04074cf443 Mon Sep 17 00:00:00 2001 From: cuz Date: Sun, 9 Sep 2001 20:49:20 +0000 Subject: [PATCH] Adding functionality to StrBuf git-svn-id: svn://svn.cc65.org/cc65/trunk@885 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/cc65/asmstmt.c | 355 ++++++++++++++++++++++++++++++++++++++++---- src/cc65/codegen.c | 13 +- src/cc65/codegen.h | 9 +- src/cc65/expr.c | 2 +- src/cc65/expr.h | 9 +- src/cc65/litpool.c | 12 +- src/cc65/litpool.h | 10 +- src/cc65/scanner.c | 24 +++ src/cc65/scanner.h | 7 +- src/common/strbuf.c | 71 +++++---- src/common/strbuf.h | 66 +++++++- 11 files changed, 491 insertions(+), 87 deletions(-) diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 76f5596d9..b34f78591 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -35,21 +35,334 @@ #include -/* cc65 */ +/* common */ +#include "xsprintf.h" + +/* cc65 */ #include "codegen.h" +#include "datatype.h" #include "error.h" +#include "expr.h" +#include "function.h" #include "litpool.h" #include "scanner.h" +#include "symtab.h" #include "asmstmt.h" /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ +static void AsmRangeError (unsigned Arg) +/* Print a diagnostic about a range error in the argument with the given number */ +{ + Error ("Range error in argument %u", Arg); +} + + + +static void AsmErrorSkip (void) +/* Called in case of an error, skips tokens until the closing paren or a + * semicolon is reached. + */ +{ + static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI }; + SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); +} + + + +static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) +/* Find the symbol with the name currently in NextTok. The symbol must be of + * the given type. On errors, NULL is returned. + */ +{ + SymEntry* Sym; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Argument must be an identifier */ + if (CurTok.Tok != TOK_IDENT) { + Error ("Identifier expected for argument %u", Arg); + AsmErrorSkip (); + return 0; + } + + /* Get a pointer to the symbol table entry */ + Sym = FindSym (CurTok.Ident); + + /* Did we find a symbol with this name? */ + if (Sym == 0) { + Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg); + AsmErrorSkip (); + return 0; + } + + /* We found the symbol - skip the name token */ + NextToken (); + + /* Check if we have a global symbol */ + if ((Sym->Flags & Type) != Type) { + Error ("Type of argument %u differs from format specifier", Arg); + AsmErrorSkip (); + return 0; + } + + /* Mark the symbol as referenced */ + Sym->Flags |= SC_REF; + + /* Return it */ + return Sym; +} + + + +static void ParseByteArg (StrBuf* T, unsigned Arg) +/* Parse the %b format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Check the range but allow negative values if the type is signed */ + if (IsSignUnsigned (Expr.Type)) { + if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFF) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } else { + if (Expr.ConstVal < -128 || Expr.ConstVal > 127) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.ConstVal & 0xFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseWordArg (StrBuf* T, unsigned Arg) +/* Parse the %w format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Check the range but allow negative values if the type is signed */ + if (IsSignUnsigned (Expr.Type)) { + if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFFFF) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } else { + if (Expr.ConstVal < -32768 || Expr.ConstVal > 32767) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.ConstVal & 0xFFFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseLongArg (StrBuf* T, unsigned Arg) +/* Parse the %l format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.ConstVal & 0xFFFFFFFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseGVarArg (StrBuf* T, unsigned Arg) +/* Parse the %v format specifier */ +{ + /* Parse the symbol name parameter and check the type */ + SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); + if (Sym == 0) { + /* Some sort of error */ + return; + } + + /* Check for external linkage */ + if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { + /* External linkage */ + /* ### FIXME: Asm name should be generated by codegen */ + SB_AppendChar (T, '_'); + SB_AppendStr (T, Sym->Name); + } else { + /* Static variable */ + char Buf [16]; + xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.Label); + SB_AppendStr (T, Buf); + } +} + + + +static void ParseLVarArg (StrBuf* T, unsigned Arg) +/* Parse the %o format specifier */ +{ + unsigned Offs; + char Buf [16]; + + /* Parse the symbol name parameter and check the type */ + SymEntry* Sym = AsmGetSym (Arg, SC_AUTO); + if (Sym == 0) { + /* Some sort of error */ + return; + } + + /* The symbol may be a parameter to a variadic function. In this case, we + * don't have a fixed stack offset, so check it and bail out with an error + * if this is the case. + */ + if ((Sym->Flags & SC_PARAM) == SC_PARAM && IsVariadic (CurrentFunc)) { + Error ("Argument %u has no fixed stack offset", Arg); + AsmErrorSkip (); + return; + } + + /* Calculate the current offset from SP */ + Offs = Sym->V.Offs - oursp; + + /* Output the offset */ + xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs); + SB_AppendStr (T, Buf); +} + + + +static void ParseAsm (void) +/* Parse the contents of the ASM statement */ +{ + unsigned I; + unsigned Arg; + + /* Create a target string buffer */ + StrBuf T = AUTO_STRBUF_INITIALIZER; + + /* Create a string buffer from the string literal */ + StrBuf S = AUTO_STRBUF_INITIALIZER; + GetLiteralStrBuf (&S, CurTok.IVal); + + /* Reset the string pointer, effectivly clearing the string from the + * string table. Since we're working with one token lookahead, this + * will fail if the next token is also a string token, but that's a + * syntax error anyway, because we expect a right paren. + */ + ResetLiteralPoolOffs (CurTok.IVal); + + /* Skip the string token */ + NextToken (); + + /* Parse the statement. It may contain several lines and one or more + * of the following place holders: + * %b - Numerical 8 bit value + * %w - Numerical 16 bit value + * %l - Numerical 32 bit value + * %v - Assembler name of a (global) variable + * %o - Stack offset of a (local) variable + * %% - The % sign + */ + I = 0; + Arg = 0; + while (I < SB_GetLen (&S)) { + + /* Get the next character */ + char C = SB_AtUnchecked (&S, I++); + + /* If it is a newline, the current line is ready to go */ + if (C == '\n') { + + /* Pass it to the backend and start over */ + g_asmcode (&T); + SB_Clear (&T); + + } else if (C == '%') { + + /* Format specifier */ + ++Arg; + + /* Check if we have characters left */ + if (I >= SB_GetLen (&S)) { + Error ("Error in __asm__ format specifier %u", Arg); + AsmErrorSkip (); + goto Done; + } else { + C = SB_AtUnchecked (&S, I++); + switch (C) { + case 'b': ParseByteArg (&T, Arg); break; + case 'w': ParseWordArg (&T, Arg); break; + case 'l': ParseLongArg (&T, Arg); break; + case 'v': ParseGVarArg (&T, Arg); break; + case 'o': ParseLVarArg (&T, Arg); break; + case '%': SB_AppendChar (&T, '%'); break; + default: + Error ("Error in __asm__ format specifier %u", Arg); + AsmErrorSkip (); + goto Done; + } + } + + } else { + + /* A normal character, just copy it */ + SB_AppendChar (&T, C); + + } + } + + /* If the target buffer is not empty, we have a last line in there */ + if (!SB_IsEmpty (&T)) { + g_asmcode (&T); + } + +Done: + /* Call the string buf destructors */ + DoneStrBuf (&S); + DoneStrBuf (&T); +} + + + void AsmStatement (void) /* This function parses ASM statements. The syntax of the ASM directive * looks like the one defined for C++ (C has no ASM directive), that is, @@ -64,39 +377,21 @@ void AsmStatement (void) /* String literal */ if (CurTok.Tok != TOK_SCONST) { + + /* Print a diagnostic */ Error ("String literal expected"); + + /* Try some smart error recovery: Skip tokens until we reach the + * enclosing paren, or a semicolon. + */ + AsmErrorSkip (); + } else { - /* The string literal may consist of more than one line of assembler - * code. Separate the single lines and output the code. - */ - const char* S = GetLiteral (CurTok.IVal); - while (*S) { - - /* Separate the lines */ - const char* E = strchr (S, '\n'); - if (E) { - /* Found a newline */ - g_asmcode (S, E-S); - S = E+1; - } else { - int Len = strlen (S); - g_asmcode (S, Len); - S += Len; - } - } - - /* Reset the string pointer, effectivly clearing the string from the - * string table. Since we're working with one token lookahead, this - * will fail if the next token is also a string token, but that's a - * syntax error anyway, because we expect a right paren. - */ - ResetLiteralPoolOffs (CurTok.IVal); + /* Parse the ASM statement */ + ParseAsm (); } - /* Skip the string token */ - NextToken (); - /* Closing paren needed */ ConsumeRParen (); } diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 7732f7f34..1d537111b 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -39,6 +39,7 @@ /* common */ #include "check.h" +#include "strbuf.h" #include "version.h" #include "xmalloc.h" #include "xsprintf.h" @@ -3877,16 +3878,10 @@ void g_zerobytes (unsigned n) -void g_asmcode (const char* Line, int Len) -/* Output one line of assembler code. If Len is greater than zero, it is used - * as the maximum number of characters to use from Line. - */ +void g_asmcode (struct StrBuf* B) +/* Output one line of assembler code. */ { - if (Len >= 0) { - AddCodeLine ("%.*s", Len, Line); - } else { - AddCodeLine ("%s", Line); - } + AddCodeLine ("%.*s", SB_GetLen (B), SB_GetConstBuf (B)); } diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index 9d1420fca..91274ec42 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -84,6 +84,9 @@ /* Compiler relative stackpointer */ extern int oursp; +/* Forward */ +struct StrBuf; + /*****************************************************************************/ @@ -437,10 +440,8 @@ void g_zerobytes (unsigned n); -void g_asmcode (const char* Line, int Len); -/* Output one line of assembler code. If Len is greater than zero, it is used - * as the maximum number of characters to use from Line. - */ +void g_asmcode (struct StrBuf* B); +/* Output one line of assembler code. */ diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 46484528e..d059da243 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -468,7 +468,7 @@ static void MakeConstIntExpr (ExprDesc* Expr, long Value) -static void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr) +void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr) /* Will evaluate an expression via the given function. If the result is not * a constant, a diagnostic will be printed, and the value is replaced by * a constant one to make sure there are no internal errors that result diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 00f18f3b3..1a6d18a8f 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -60,10 +60,11 @@ struct ExprDesc { -void doasm (void); -/* This function parses ASM statements. The syntax of the ASM directive - * looks like the one defined for C++ (C has no ASM directive), that is, - * a string literal in parenthesis. +void ConstSubExpr (int (*F) (ExprDesc*), ExprDesc* Expr); +/* Will evaluate an expression via the given function. If the result is not + * a constant, a diagnostic will be printed, and the value is replaced by + * a constant one to make sure there are no internal errors that result + * from this input error. */ unsigned assignadjust (type* lhst, ExprDesc* rhs); diff --git a/src/cc65/litpool.c b/src/cc65/litpool.c index b1675ad87..e5bc9b737 100644 --- a/src/cc65/litpool.c +++ b/src/cc65/litpool.c @@ -37,7 +37,6 @@ /* common */ #include "check.h" -#include "strbuf.h" #include "tgttrans.h" /* cc65 */ @@ -165,6 +164,17 @@ const char* GetLiteral (unsigned Offs) +void GetLiteralStrBuf (StrBuf* Target, unsigned Offs) +/* Copy the string starting at Offs and lasting to the end of the buffer + * into Target. + */ +{ + CHECK (Offs <= SB_GetLen (&LiteralPool)); + SB_Slice (Target, &LiteralPool, Offs, SB_GetLen (&LiteralPool) - Offs); +} + + + void PrintLiteralPoolStats (FILE* F) /* Print statistics about the literal space used */ { diff --git a/src/cc65/litpool.h b/src/cc65/litpool.h index e9ad3c5cb..99fd27bfb 100644 --- a/src/cc65/litpool.h +++ b/src/cc65/litpool.h @@ -40,10 +40,13 @@ #include +/* common */ +#include "strbuf.h" + /*****************************************************************************/ -/* Data */ +/* Data */ /*****************************************************************************/ @@ -88,6 +91,11 @@ unsigned AddLiteral (const char* S); const char* GetLiteral (unsigned Offs); /* Get a pointer to the literal with the given offset in the pool */ +void GetLiteralStrBuf (StrBuf* Target, unsigned Offs); +/* Copy the string starting at Offs and lasting to the end of the buffer + * into Target. + */ + void PrintLiteralPoolStats (FILE* F); /* Print statistics about the literal space used */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 2009826ea..3584a56a1 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -801,6 +801,30 @@ void NextToken (void) +void SkipTokens (const token_t* TokenList, unsigned TokenCount) +/* Skip tokens until we reach TOK_CEOF or a token in the given token list. + * This routine is used for error recovery. + */ +{ + while (CurTok.Tok != TOK_CEOF) { + + /* Check if the current token is in the token list */ + unsigned I; + for (I = 0; I < TokenCount; ++I) { + if (CurTok.Tok == TokenList[I]) { + /* Found a token in the list */ + return; + } + } + + /* Not in the list: Skip it */ + NextToken (); + + } +} + + + void Consume (token_t Token, const char* ErrorMsg) /* Eat token if it is the next in the input stream, otherwise print an error * message. diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 97614b62e..e78446efb 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -191,7 +191,7 @@ extern Token NextTok; /* The next token */ /*****************************************************************************/ -/* code */ +/* code */ /*****************************************************************************/ @@ -205,6 +205,11 @@ int IsSym (char* s); void NextToken (void); /* Get next token from input stream */ +void SkipTokens (const token_t* TokenList, unsigned TokenCount); +/* Skip tokens until we reach TOK_CEOF or a token in the given token list. + * This routine is used for error recovery. + */ + void Consume (token_t Token, const char* ErrorMsg); /* Eat token if it is the next in the input stream, otherwise print an error * message. diff --git a/src/common/strbuf.c b/src/common/strbuf.c index f428037fb..f20b4cdc6 100644 --- a/src/common/strbuf.c +++ b/src/common/strbuf.c @@ -42,31 +42,13 @@ /*****************************************************************************/ -/* Helpers */ +/* Data */ /*****************************************************************************/ -void SB_Realloc (StrBuf* B, unsigned NewSize) -/* Reallocate the string buffer space, make sure at least NewSize bytes are - * available. - */ -{ - /* Get the current size, use a minimum of 8 bytes */ - unsigned NewAllocated = B->Allocated; - if (NewAllocated == 0) { - NewAllocated = 8; - } - - /* Round up to the next power of two */ - while (NewAllocated < NewSize) { - NewAllocated *= 2; - } - - /* Reallocate the buffer */ - B->Buf = xrealloc (B->Buf, NewAllocated); - B->Allocated = NewAllocated; -} +/* An empty string buf */ +const StrBuf EmptyStrBuf = STATIC_STRBUF_INITIALIZER; @@ -119,6 +101,29 @@ void FreeStrBuf (StrBuf* B) +void SB_Realloc (StrBuf* B, unsigned NewSize) +/* Reallocate the string buffer space, make sure at least NewSize bytes are + * available. + */ +{ + /* Get the current size, use a minimum of 8 bytes */ + unsigned NewAllocated = B->Allocated; + if (NewAllocated == 0) { + NewAllocated = 8; + } + + /* Round up to the next power of two */ + while (NewAllocated < NewSize) { + NewAllocated *= 2; + } + + /* Reallocate the buffer */ + B->Buf = xrealloc (B->Buf, NewAllocated); + B->Allocated = NewAllocated; +} + + + void SB_Terminate (StrBuf* B) /* Zero terminate the given string buffer. NOTE: The terminating zero is not * accounted for in B->Len, if you want that, you have to use AppendChar! @@ -133,6 +138,18 @@ void SB_Terminate (StrBuf* B) +void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size) +/* Copy Buf to Target, discarding the old contents of Target */ +{ + if (Target->Allocated < Size) { + SB_Realloc (Target, Size); + } + memcpy (Target->Buf, Buf, Size); + Target->Len = Size; +} + + + void SB_AppendChar (StrBuf* B, char C) /* Append a character to a string buffer */ { @@ -159,18 +176,6 @@ void SB_AppendBuf (StrBuf* B, const char* S, unsigned Size) -void SB_Copy (StrBuf* Target, const StrBuf* Source) -/* Copy Source to Target, discarding the old contents of Target */ -{ - if (Target->Allocated < Source->Allocated) { - SB_Realloc (Target, Source->Allocated); - } - memcpy (Target->Buf, Source->Buf, Source->Len); - Target->Len = Source->Len; -} - - - void SB_Slice (StrBuf* Target, const StrBuf* Source, unsigned Start, unsigned Len) /* Copy a slice from Source into Target. The current contents of Target are * destroyed. If Start is greater than the length of Source, or if Len diff --git a/src/common/strbuf.h b/src/common/strbuf.h index 6a819e71f..1c92e0fee 100644 --- a/src/common/strbuf.h +++ b/src/common/strbuf.h @@ -42,6 +42,7 @@ /* common */ #include "attrib.h" +#include "check.h" #include "inline.h" @@ -59,9 +60,15 @@ struct StrBuf { char* Buf; }; +/* An empty string buf */ +extern const StrBuf EmptyStrBuf; + /* Initializer for static string bufs */ #define STATIC_STRBUF_INITIALIZER { 0, 0, 0 } +/* Initializer for auto string bufs */ +#define AUTO_STRBUF_INITIALIZER EmptyStrBuf + /*****************************************************************************/ @@ -117,6 +124,39 @@ INLINE char* SB_GetBuf (StrBuf* B) # define SB_GetBuf(B) (B)->Buf #endif +#if defined(HAVE_INLINE) +INLINE char SB_At (const StrBuf* B, unsigned Index) +/* Get a character from the buffer */ +{ + PRECONDITION (Index < B->Len); + return B->Buf[Index]; +} +#else +# define SB_At(B, Index) \ + (PRECONDITION ((Index) < (B)->Len), \ + (B)->Buf[Index]) +#endif + +#if defined(HAVE_INLINE) +INLINE char SB_AtUnchecked (const StrBuf* B, unsigned Index) +/* Get a character from the buffer */ +{ + return B->Buf[Index]; +} +#else +# define SB_AtUnchecked(B, Index) ((B)->Buf[Index]) +#endif + +#if defined(HAVE_INLINE) +INLINE int SB_IsEmpty (const StrBuf* B) +/* Return true if the string buffer is empty */ +{ + return (B->Len == 0); +} +#else +# define SB_IsEmpty(B) ((B)->Len == 0) +#endif + #if defined(HAVE_INLINE) INLINE void SB_Clear (StrBuf* B) /* Clear the string buffer (make it empty) */ @@ -132,6 +172,29 @@ void SB_Terminate (StrBuf* B); * accounted for in B->Len, if you want that, you have to use AppendChar! */ +void SB_CopyBuf (StrBuf* Target, const char* Buf, unsigned Size); +/* Copy Buf to Target, discarding the old contents of Target */ + +#if defined(HAVE_INLINE) +INLINE void SB_CopyStr (StrBuf* Target, const char* S) +/* Copy S to Target, discarding the old contents of Target */ +{ + SB_CopyBuf (Target, S, strlen (S)); +} +#else +# define SB_CopyStr(Target, S) SB_CopyBuf (Target, S, strlen (S)) +#endif + +#if defined(HAVE_INLINE) +INLINE void SB_Copy (StrBuf* Target, const StrBuf* Source) +/* Copy Source to Target, discarding the old contents of Target */ +{ + SB_CopyBuf (Target, Source->Buf, Source->Len); +} +#else +# define SB_Copy(Target, Source) SB_CopyBuf (Target, (Source)->Buf, (Source)->Len) +#endif + void SB_AppendChar (StrBuf* B, char C); /* Append a character to a string buffer */ @@ -148,9 +211,6 @@ INLINE void SB_AppendStr (StrBuf* B, const char* S) # define SB_AppendStr(B, S) SB_AppendBuf (B, S, strlen (S)) #endif -void SB_Copy (StrBuf* Target, const StrBuf* Source); -/* Copy Source to Target, discarding the old contents of Target */ - #if defined(HAVE_INLINE) INLINE void SB_Append (StrBuf* Target, const StrBuf* Source) /* Append the contents of Source to Target */ -- 2.39.5