mirror of
https://github.com/cc65/cc65.git
synced 2024-12-23 19:29:37 +00:00
New .sprintf function
git-svn-id: svn://svn.cc65.org/cc65/trunk@3508 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
22e204d1c4
commit
765dc3442b
@ -37,7 +37,9 @@
|
||||
#include <string.h>
|
||||
|
||||
/* common */
|
||||
#include "chartype.h"
|
||||
#include "check.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
/* ca65 */
|
||||
#include "error.h"
|
||||
@ -59,6 +61,29 @@ static unsigned RawMode = 0; /* Raw token mode flag/counter */
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Error handling */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
static int LookAtStrCon (void)
|
||||
/* Make sure the next token is a string constant. If not, print an error
|
||||
* messages skip the remainder of the line and return false. Otherwise return
|
||||
* true.
|
||||
*/
|
||||
{
|
||||
if (Tok != TOK_STRCON) {
|
||||
Error ("String constant expected");
|
||||
SkipUntilSep ();
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
@ -135,9 +160,7 @@ static void FuncConcat (void)
|
||||
while (1) {
|
||||
|
||||
/* Next token must be a string */
|
||||
if (Tok != TOK_STRCON) {
|
||||
Error ("String constant expected");
|
||||
SkipUntilSep ();
|
||||
if (!LookAtStrCon ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -248,9 +271,7 @@ static void FuncIdent (void)
|
||||
ConsumeLParen ();
|
||||
|
||||
/* The function expects a string argument */
|
||||
if (Tok != TOK_STRCON) {
|
||||
Error ("String constant expected");
|
||||
SkipUntilSep ();
|
||||
if (!LookAtStrCon ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -410,6 +431,216 @@ static void FuncRight (void)
|
||||
|
||||
|
||||
|
||||
static void InvalidFormatString (void)
|
||||
/* Print an error message and skip the remainder of the line */
|
||||
{
|
||||
Error ("Invalid format string");
|
||||
SkipUntilSep ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void FuncSPrintF (void)
|
||||
/* Handle the .SPRINTF function */
|
||||
{
|
||||
char Format[sizeof (SVal)]; /* User given format */
|
||||
const char* F = Format; /* User format pointer */
|
||||
StrBuf R = AUTO_STRBUF_INITIALIZER; /* Result string */
|
||||
StrBuf F1 = AUTO_STRBUF_INITIALIZER; /* One format spec from F */
|
||||
StrBuf R1 = AUTO_STRBUF_INITIALIZER; /* One result */
|
||||
int Done;
|
||||
long IVal; /* Integer value */
|
||||
|
||||
|
||||
|
||||
/* Skip the .SPRINTF token */
|
||||
NextTok ();
|
||||
|
||||
/* Left paren expected */
|
||||
ConsumeLParen ();
|
||||
|
||||
/* First argument is a format string. Remember and skip it */
|
||||
if (!LookAtStrCon ()) {
|
||||
return;
|
||||
}
|
||||
strcpy (Format, SVal);
|
||||
NextTok ();
|
||||
|
||||
/* Walk over the format string, generating the function result in R */
|
||||
while (1) {
|
||||
|
||||
/* Get the next char from the format string and check for EOS */
|
||||
if (*F == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a format specifier */
|
||||
if (*F != '%') {
|
||||
/* No format specifier, just copy */
|
||||
SB_AppendChar (&R, *F++);
|
||||
continue;
|
||||
}
|
||||
if (*++F == '%') {
|
||||
/* %% */
|
||||
SB_AppendChar (&R, '%');
|
||||
++F;
|
||||
continue;
|
||||
}
|
||||
if (*F == '\0') {
|
||||
InvalidFormatString ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Since a format specifier follows, we do expect anotehr argument for
|
||||
* the .sprintf function.
|
||||
*/
|
||||
ConsumeComma ();
|
||||
|
||||
/* We will copy the format spec into F1 checking for the things we
|
||||
* support, and later use xsprintf to do the actual formatting. This
|
||||
* is easier than adding another printf implementation...
|
||||
*/
|
||||
SB_Clear (&F1);
|
||||
SB_AppendChar (&F1, '%');
|
||||
|
||||
/* Check for flags */
|
||||
Done = 0;
|
||||
while (*F != '\0' && !Done) {
|
||||
switch (*F) {
|
||||
case '-': /* FALLTHROUGH */
|
||||
case '+': /* FALLTHROUGH */
|
||||
case ' ': /* FALLTHROUGH */
|
||||
case '#': /* FALLTHROUGH */
|
||||
case '0': SB_AppendChar (&F1, *F++); break;
|
||||
default: Done = 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We do only support a numerical width field */
|
||||
while (IsDigit (*F)) {
|
||||
SB_AppendChar (&F1, *F++);
|
||||
}
|
||||
|
||||
/* Precision - only positive numerical fields supported */
|
||||
if (*F == '.') {
|
||||
SB_AppendChar (&F1, *F++);
|
||||
while (IsDigit (*F)) {
|
||||
SB_AppendChar (&F1, *F++);
|
||||
}
|
||||
}
|
||||
|
||||
/* Length modifiers aren't supported, so read the conversion specs */
|
||||
switch (*F) {
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'X':
|
||||
case 'x':
|
||||
/* Our ints are actually longs, so we use the 'l' modifier when
|
||||
* calling xsprintf later. Terminate the format string.
|
||||
*/
|
||||
SB_AppendChar (&F1, 'l');
|
||||
SB_AppendChar (&F1, *F++);
|
||||
SB_Terminate (&F1);
|
||||
|
||||
/* The argument must be a constant expression */
|
||||
IVal = ConstExpression ();
|
||||
|
||||
/* Format this argument according to the spec */
|
||||
SB_Printf (&R1, SB_GetConstBuf (&F1), IVal);
|
||||
|
||||
/* Append the formatted argument to the result */
|
||||
SB_Append (&R, &R1);
|
||||
|
||||
break;
|
||||
|
||||
case 's':
|
||||
/* Add the format spec and terminate the format */
|
||||
SB_AppendChar (&F1, *F++);
|
||||
SB_Terminate (&F1);
|
||||
|
||||
/* The argument must be a string constant */
|
||||
if (!LookAtStrCon ()) {
|
||||
/* Make it one */
|
||||
strcpy (SVal, "**undefined**");
|
||||
}
|
||||
|
||||
/* Format this argument according to the spec */
|
||||
SB_Printf (&R1, SB_GetConstBuf (&F1), SVal);
|
||||
|
||||
/* Skip the string constant */
|
||||
NextTok ();
|
||||
|
||||
/* Append the formatted argument to the result */
|
||||
SB_Append (&R, &R1);
|
||||
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
/* Add the format spec and terminate the format */
|
||||
SB_AppendChar (&F1, *F++);
|
||||
SB_Terminate (&F1);
|
||||
|
||||
/* The argument must be a constant expression */
|
||||
IVal = ConstExpression ();
|
||||
|
||||
/* Check for a valid character range */
|
||||
if (IVal <= 0 || IVal > 255) {
|
||||
Error ("Char argument out of range");
|
||||
IVal = ' ';
|
||||
}
|
||||
|
||||
/* Format this argument according to the spec. Be sure to pass
|
||||
* an int as the char value.
|
||||
*/
|
||||
SB_Printf (&R1, SB_GetConstBuf (&F1), (int) IVal);
|
||||
|
||||
/* Append the formatted argument to the result */
|
||||
SB_Append (&R, &R1);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
Error ("Invalid format string");
|
||||
if (*F) {
|
||||
/* Don't skip beyond end of string */
|
||||
++F;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The length of the final result may not exceed the size of a string */
|
||||
if (SB_GetLen (&R) >= sizeof (SVal)) {
|
||||
Error ("Resulting string is too long");
|
||||
SB_Cut (&R, sizeof (SVal) - 1);
|
||||
}
|
||||
|
||||
/* Terminate the result string */
|
||||
SB_Terminate (&R);
|
||||
|
||||
/* We expect a closing parenthesis, but will not skip it but replace it
|
||||
* by the string token just created.
|
||||
*/
|
||||
if (Tok != TOK_RPAREN) {
|
||||
Error ("`)' expected");
|
||||
} else {
|
||||
Tok = TOK_STRCON;
|
||||
memcpy (SVal, SB_GetConstBuf (&R), SB_GetLen (&R) + 1);
|
||||
}
|
||||
|
||||
|
||||
/* Delete the string buffers */
|
||||
DoneStrBuf (&R);
|
||||
DoneStrBuf (&F1);
|
||||
DoneStrBuf (&R1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void FuncString (void)
|
||||
/* Handle the .STRING function */
|
||||
{
|
||||
@ -477,6 +708,10 @@ void NextTok (void)
|
||||
FuncRight ();
|
||||
break;
|
||||
|
||||
case TOK_SPRINTF:
|
||||
FuncSPrintF ();
|
||||
break;
|
||||
|
||||
case TOK_STRING:
|
||||
FuncString ();
|
||||
break;
|
||||
|
@ -1773,7 +1773,8 @@ static CtrlDesc CtrlCmdTab [] = {
|
||||
{ ccNone, DoSegment },
|
||||
{ ccNone, DoSetCPU },
|
||||
{ ccNone, DoUnexpected }, /* .SIZEOF */
|
||||
{ ccNone, DoSmart },
|
||||
{ ccNone, DoSmart },
|
||||
{ ccNone, DoUnexpected }, /* .SPRINTF */
|
||||
{ ccNone, DoUnexpected }, /* .STRAT */
|
||||
{ ccNone, DoUnexpected }, /* .STRING */
|
||||
{ ccNone, DoUnexpected }, /* .STRLEN */
|
||||
|
@ -6,7 +6,7 @@
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 1998-2004 Ullrich von Bassewitz */
|
||||
/* (C) 1998-2005 Ullrich von Bassewitz */
|
||||
/* Römerstraße 52 */
|
||||
/* D-70794 Filderstadt */
|
||||
/* EMail: uz@cc65.org */
|
||||
@ -241,6 +241,7 @@ struct DotKeyword {
|
||||
{ ".SHR", TOK_SHR },
|
||||
{ ".SIZEOF", TOK_SIZEOF },
|
||||
{ ".SMART", TOK_SMART },
|
||||
{ ".SPRINTF", TOK_SPRINTF },
|
||||
{ ".STRAT", TOK_STRAT },
|
||||
{ ".STRING", TOK_STRING },
|
||||
{ ".STRLEN", TOK_STRLEN },
|
||||
|
@ -228,6 +228,7 @@ enum Token {
|
||||
TOK_SETCPU,
|
||||
TOK_SIZEOF,
|
||||
TOK_SMART,
|
||||
TOK_SPRINTF,
|
||||
TOK_STRAT,
|
||||
TOK_STRING,
|
||||
TOK_STRLEN,
|
||||
|
Loading…
Reference in New Issue
Block a user