From ffbee16a2d223a6254759f668afda4893fcc268a Mon Sep 17 00:00:00 2001 From: "D.E. Akers" <0x0dea@gmail.com> Date: Thu, 13 Aug 2015 18:24:26 -0400 Subject: [PATCH] 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. --- interpreter.c | 4 ++-- parser.c | 27 ++++++++++++++++++++++----- parser.h | 4 +++- tokenizer.h | 2 ++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/interpreter.c b/interpreter.c index 20987b9..2425280 100644 --- a/interpreter.c +++ b/interpreter.c @@ -3087,12 +3087,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); } diff --git a/parser.c b/parser.c index 2b6a037..1df2606 100644 --- a/parser.c +++ b/parser.c @@ -629,6 +629,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. @@ -636,6 +638,7 @@ void deleteCastStmtNode(CastStmtNode *node) * \retval NULL Memory allocation failed. */ PrintStmtNode *createPrintStmtNode(ExprNodeList *args, + FILE *file, int nonl) { PrintStmtNode *p = malloc(sizeof(PrintStmtNode)); @@ -644,6 +647,7 @@ PrintStmtNode *createPrintStmtNode(ExprNodeList *args, return NULL; } p->args = args; + p->file = file; p->nonl = nonl; return p; } @@ -2477,6 +2481,7 @@ StmtNode *parsePrintStmtNode(Token ***tokenp) { ExprNode *arg = NULL; ExprNodeList *args = NULL; + FILE *file = stdout; int nonl = 0; PrintStmtNode *stmt = NULL; StmtNode *ret = NULL; @@ -2489,13 +2494,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; @@ -2515,7 +2528,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); @@ -2525,7 +2538,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 */ @@ -3963,6 +3976,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); diff --git a/parser.h b/parser.h index a09b249..85a3076 100644 --- a/parser.h +++ b/parser.h @@ -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 @@ -395,6 +396,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; @@ -612,7 +614,7 @@ void deleteCastStmtNode(CastStmtNode *); * Functions for creating and deleting PrintStmtNodes. */ /**@{*/ -PrintStmtNode *createPrintStmtNode(ExprNodeList *, int); +PrintStmtNode *createPrintStmtNode(ExprNodeList *, FILE *, int); void deletePrintStmtNode(PrintStmtNode *); /**@}*/ diff --git a/tokenizer.h b/tokenizer.h index 81f9fd2..cab948d 100644 --- a/tokenizer.h +++ b/tokenizer.h @@ -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. */ @@ -153,6 +154,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 */