mirror of
https://github.com/cc65/cc65.git
synced 2024-12-24 11:31:31 +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>
|
#include <string.h>
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
|
#include "chartype.h"
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
/* ca65 */
|
/* ca65 */
|
||||||
#include "error.h"
|
#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 */
|
/* Code */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -135,9 +160,7 @@ static void FuncConcat (void)
|
|||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
/* Next token must be a string */
|
/* Next token must be a string */
|
||||||
if (Tok != TOK_STRCON) {
|
if (!LookAtStrCon ()) {
|
||||||
Error ("String constant expected");
|
|
||||||
SkipUntilSep ();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +271,7 @@ static void FuncIdent (void)
|
|||||||
ConsumeLParen ();
|
ConsumeLParen ();
|
||||||
|
|
||||||
/* The function expects a string argument */
|
/* The function expects a string argument */
|
||||||
if (Tok != TOK_STRCON) {
|
if (!LookAtStrCon ()) {
|
||||||
Error ("String constant expected");
|
|
||||||
SkipUntilSep ();
|
|
||||||
return;
|
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)
|
static void FuncString (void)
|
||||||
/* Handle the .STRING function */
|
/* Handle the .STRING function */
|
||||||
{
|
{
|
||||||
@ -477,6 +708,10 @@ void NextTok (void)
|
|||||||
FuncRight ();
|
FuncRight ();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOK_SPRINTF:
|
||||||
|
FuncSPrintF ();
|
||||||
|
break;
|
||||||
|
|
||||||
case TOK_STRING:
|
case TOK_STRING:
|
||||||
FuncString ();
|
FuncString ();
|
||||||
break;
|
break;
|
||||||
|
@ -1774,6 +1774,7 @@ static CtrlDesc CtrlCmdTab [] = {
|
|||||||
{ ccNone, DoSetCPU },
|
{ ccNone, DoSetCPU },
|
||||||
{ ccNone, DoUnexpected }, /* .SIZEOF */
|
{ ccNone, DoUnexpected }, /* .SIZEOF */
|
||||||
{ ccNone, DoSmart },
|
{ ccNone, DoSmart },
|
||||||
|
{ ccNone, DoUnexpected }, /* .SPRINTF */
|
||||||
{ ccNone, DoUnexpected }, /* .STRAT */
|
{ ccNone, DoUnexpected }, /* .STRAT */
|
||||||
{ ccNone, DoUnexpected }, /* .STRING */
|
{ ccNone, DoUnexpected }, /* .STRING */
|
||||||
{ ccNone, DoUnexpected }, /* .STRLEN */
|
{ ccNone, DoUnexpected }, /* .STRLEN */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
/* */
|
/* */
|
||||||
/* */
|
/* */
|
||||||
/* */
|
/* */
|
||||||
/* (C) 1998-2004 Ullrich von Bassewitz */
|
/* (C) 1998-2005 Ullrich von Bassewitz */
|
||||||
/* Römerstraße 52 */
|
/* Römerstraße 52 */
|
||||||
/* D-70794 Filderstadt */
|
/* D-70794 Filderstadt */
|
||||||
/* EMail: uz@cc65.org */
|
/* EMail: uz@cc65.org */
|
||||||
@ -241,6 +241,7 @@ struct DotKeyword {
|
|||||||
{ ".SHR", TOK_SHR },
|
{ ".SHR", TOK_SHR },
|
||||||
{ ".SIZEOF", TOK_SIZEOF },
|
{ ".SIZEOF", TOK_SIZEOF },
|
||||||
{ ".SMART", TOK_SMART },
|
{ ".SMART", TOK_SMART },
|
||||||
|
{ ".SPRINTF", TOK_SPRINTF },
|
||||||
{ ".STRAT", TOK_STRAT },
|
{ ".STRAT", TOK_STRAT },
|
||||||
{ ".STRING", TOK_STRING },
|
{ ".STRING", TOK_STRING },
|
||||||
{ ".STRLEN", TOK_STRLEN },
|
{ ".STRLEN", TOK_STRLEN },
|
||||||
|
@ -228,6 +228,7 @@ enum Token {
|
|||||||
TOK_SETCPU,
|
TOK_SETCPU,
|
||||||
TOK_SIZEOF,
|
TOK_SIZEOF,
|
||||||
TOK_SMART,
|
TOK_SMART,
|
||||||
|
TOK_SPRINTF,
|
||||||
TOK_STRAT,
|
TOK_STRAT,
|
||||||
TOK_STRING,
|
TOK_STRING,
|
||||||
TOK_STRLEN,
|
TOK_STRLEN,
|
||||||
|
Loading…
Reference in New Issue
Block a user