add INVISIBLE operator for writing to stderr

Printing warnings and the like to the standard error stream is common
courtesy, not least because it avoids cluttering the program's output
with diagnostic messages.

Neither the 1.2 specification nor any of the proposals for 1.3 mention
using `INVISIBLE` for this purpose. Nevertheless, the operator and the
behavior described herein have seen sufficiently wide adoption in other
LOLCODE interpreters that I believe `lci` would do well to follow suit.

This patch attempts to be minimally invasive. Rather than adding a new
node type and all that that entails, the `PrintStmtNode` structure now
has a `FILE *` field containing the file to which the node should print
during interpretation. This saves a branch in `interpretPrintStmtNode()`
at the cost of making the structure negligibly larger on most systems.

Adding tests for `INVISIBLE` would require rethinking the test driver,
which presently uses the existence of output on `stderr` to detect that
an "expected error" has occurred. I'll happily try my hand at making the
necessary modifications if this patch comes to be accepted.
This commit is contained in:
D.E. Akers 2015-08-13 18:24:26 -04:00 committed by Justin Meza
parent afdd225208
commit f1881ad63f
4 changed files with 29 additions and 8 deletions

View File

@ -3229,12 +3229,12 @@ ReturnObject *interpretPrintStmtNode(StmtNode *node,
deleteValueObject(use);
return NULL;
}
printf("%s", getString(use));
fprintf(stmt->file, "%s", getString(use));
deleteValueObject(val);
deleteValueObject(use);
}
if (!stmt->nonl)
printf("\n");
putc('\n', stmt->file);
return createReturnObject(RT_DEFAULT, NULL);
}

View File

@ -639,6 +639,8 @@ void deleteCastStmtNode(CastStmtNode *node)
*
* \param [in] args The expressions to print.
*
* \param [in] file Where to print (\c stdout or \c stderr).
*
* \param [in] nonl Whether an ending newline should be surpressed.
*
* \return A pointer to the print statement with the desired properties.
@ -646,6 +648,7 @@ void deleteCastStmtNode(CastStmtNode *node)
* \retval NULL Memory allocation failed.
*/
PrintStmtNode *createPrintStmtNode(ExprNodeList *args,
FILE *file,
int nonl)
{
PrintStmtNode *p = malloc(sizeof(PrintStmtNode));
@ -654,6 +657,7 @@ PrintStmtNode *createPrintStmtNode(ExprNodeList *args,
return NULL;
}
p->args = args;
p->file = file;
p->nonl = nonl;
return p;
}
@ -2649,6 +2653,7 @@ StmtNode *parsePrintStmtNode(Token ***tokenp)
{
ExprNode *arg = NULL;
ExprNodeList *args = NULL;
FILE *file = stdout;
int nonl = 0;
PrintStmtNode *stmt = NULL;
StmtNode *ret = NULL;
@ -2661,13 +2666,21 @@ StmtNode *parsePrintStmtNode(Token ***tokenp)
debug("ST_PRINT");
#endif
/* Remove the print keyword from the token stream */
/**
* Remove the print keyword from the token stream
* A "quad-bool" is used to indicate whether we succeeded or failed to
* accept either a \c TT_VISIBLE or \c TT_INVISIBLE token.
*/
status = acceptToken(&tokens, TT_VISIBLE);
if (!status) {
parser_error_expected_token(TT_VISIBLE, tokens);
if (!status) status = acceptToken(&tokens, TT_INVISIBLE) ? 2 : -1;
if (status < 1) {
parser_error_expected_token(TT_VISIBLE - status, tokens);
goto parsePrintStmtNodeAbort;
}
/* Use standard error if we accepted a \c TT_INVISIBLE above */
if (status == 2) file = stderr;
/* Parse the arguments to the print statement */
args = createExprNodeList();
if (!args) goto parsePrintStmtNodeAbort;
@ -2687,7 +2700,7 @@ StmtNode *parsePrintStmtNode(Token ***tokenp)
&& !peekToken(&tokens, TT_BANG));
/* Check for the no-newline token */
if(acceptToken(&tokens, TT_BANG)) nonl = 1;
if (acceptToken(&tokens, TT_BANG)) nonl = 1;
/* Make sure the statement ends with a newline */
status = acceptToken(&tokens, TT_NEWLINE);
@ -2697,7 +2710,7 @@ StmtNode *parsePrintStmtNode(Token ***tokenp)
}
/* Create the new PrintStmtNode structure */
stmt = createPrintStmtNode(args, nonl);
stmt = createPrintStmtNode(args, file, nonl);
if (!stmt) goto parsePrintStmtNodeAbort;
/* Create the new StmtNode structure */
@ -4219,6 +4232,10 @@ StmtNode *parseStmtNode(Token ***tokenp)
else if (peekToken(&tokens, TT_VISIBLE)) {
ret = parsePrintStmtNode(tokenp);
}
/* Warn */
else if (peekToken(&tokens, TT_INVISIBLE)) {
ret = parsePrintStmtNode(tokenp);
}
/* Input */
else if (peekToken(&tokens, TT_GIMMEH)) {
ret = parseInputStmtNode(tokenp);

View File

@ -70,6 +70,7 @@
*
* \par
* PrintStmtNode ::= \c TT_VISIBLE ExprNodeList \c TT_BANG ? \c TT_NEWLINE
* | \c TT_INVISIBLE ExprNodeList \c TT_BANG ? \c TT_NEWLINE
*
* \par
* InputStmtNode ::= \c TT_GIMMEH IdentifierNode \c TT_NEWLINE
@ -417,6 +418,7 @@ typedef struct {
*/
typedef struct {
ExprNodeList *args; /**< The expressions to print. */
FILE *file; /**< Where to print (\c stdout or \c stderr). */
int nonl; /**< Whether to print an ending newline. */
} PrintStmtNode;
@ -643,7 +645,7 @@ void deleteCastStmtNode(CastStmtNode *);
* Functions for creating and deleting PrintStmtNodes.
*/
/**@{*/
PrintStmtNode *createPrintStmtNode(ExprNodeList *, int);
PrintStmtNode *createPrintStmtNode(ExprNodeList *, FILE *, int);
void deletePrintStmtNode(PrintStmtNode *);
/**@}*/

View File

@ -76,6 +76,7 @@ typedef enum {
TT_A, /**< Cast target separator. */
TT_ISNOWA, /**< In-place cast. */
TT_VISIBLE, /**< Print. */
TT_INVISIBLE, /**< Print to standard error. */
TT_SMOOSH, /**< String concatenation. */
TT_BANG, /**< Exclamation point (!) */
TT_GIMMEH, /**< Input. */
@ -156,6 +157,7 @@ static const char *keywords[] = {
"A", /* TT_A */
"IS NOW A", /* TT_ISNOWA */
"VISIBLE", /* TT_VISIBLE */
"INVISIBLE", /* TT_INVISIBLE */
"SMOOSH", /* TT_SMOOSH */
"!", /* TT_BANG */
"GIMMEH", /* TT_GIMMEH */