diff --git a/interpreter/cling/lib/MetaProcessor/InputValidator.cpp b/interpreter/cling/lib/MetaProcessor/InputValidator.cpp index c151704617591ed705bd0027b274c3305369d91b..2ea9899a287ee549ef89bb3fc790420771b2b59b 100644 --- a/interpreter/cling/lib/MetaProcessor/InputValidator.cpp +++ b/interpreter/cling/lib/MetaProcessor/InputValidator.cpp @@ -8,20 +8,79 @@ //------------------------------------------------------------------------------ #include "InputValidator.h" - #include "MetaLexer.h" +#include <algorithm> namespace cling { + bool InputValidator::inBlockComment() const { + return std::find(m_ParenStack.begin(), m_ParenStack.end(), tok::slash) + != m_ParenStack.end(); + } + + static int findNestedBlockComments(const char* startPos, const char* endPos) { + // While probably not standard compliant, it should work fine for the indent + // Let the real parser error if the balancing is incorrect + + // search forward for //, then backward for block comments + // */ last, comment has ended, doesn't matter how many /* before + // /* last, comment has begun, doesn't matter if priors ended or not + char commentTok = 0; + while (startPos < endPos) { + if (*startPos == '/') { + if (++commentTok == 2) { + while (endPos > startPos) { + switch (*endPos) { + case '*': + if (commentTok == '*') + return -1; + else + commentTok = '/'; + break; + case '/': + if (commentTok == '/') + return 1; + else + commentTok = '*'; + break; + default: + commentTok = 0; + break; + } + --endPos; + } + return 0; + } + } else if (commentTok) + commentTok = 0; // need a new start to double slash + ++startPos; + } + return 0; + } + + static void unwindTokens(std::deque<int>& queue, int tok) { + assert(!queue.empty() && "Token stack is empty!"); + while (queue.back() != tok) { + queue.pop_back(); + assert(!queue.empty() && "Token stack is empty!"); + } + queue.pop_back(); + } + InputValidator::ValidationResult InputValidator::validate(llvm::StringRef line) { ValidationResult Res = kComplete; Token Tok; const char* curPos = line.data(); + bool multilineComment = inBlockComment(); + int commentTok = multilineComment ? tok::asterik : tok::slash; + do { + const char* prevStart = curPos; MetaLexer::LexPunctuatorAndAdvance(curPos, Tok); int kind = (int)Tok.getKind(); + // if (kind == tok::hash) { // // Handle #ifdef ... // // ... @@ -40,28 +99,78 @@ namespace cling { // } // } - if (kind >= (int)tok::stringlit && kind <= (int)tok::charlit) { - MetaLexer::LexQuotedStringAndAdvance(curPos, Tok); - } else - - // In case when we need closing brace. - if (kind >= (int)tok::l_square && kind <= (int)tok::r_brace) { - // The closing paren kind is open paren kind + 1 (i.e odd number) - if (kind % 2) { - // closing the right one? - if (m_ParenStack.empty()) { - Res = kMismatch; - break; + if (kind == commentTok) { + if (kind == tok::slash) { + if (multilineComment) { + // exiting a comment, unwind the stack + multilineComment = false; + commentTok = tok::slash; + unwindTokens(m_ParenStack, tok::slash); } - int prev = m_ParenStack.back(); - if (prev != kind - 1) { - Res = kMismatch; + // If we have a closing comment without a start it will be transformed + // to */; and clang reports an error for both the */ and the ; + // If we return kIncomplete, then just one error is printed, but too + // late: after the user has another expression which will always fail. + // So just deal with two errors for now + // else if (prevKind == tok::asterik) { + // Res = kIncomplete; + // break; + // } + else // wait for an asterik + commentTok = tok::asterik; + } + else { + assert(commentTok == tok::asterik && "Comment token not / or *"); + if (!multilineComment) { + // entering a new comment + multilineComment = true; + m_ParenStack.push_back(tok::slash); + } + else // wait for closing / (must be next token) + commentTok = tok::slash; + } + } + else { + // If we're in a multiline, and waiting for the closing slash + // we gonna have to wait for another asterik first + if (multilineComment) { + if (kind == tok::eof) { + switch (findNestedBlockComments(prevStart, curPos)) { + case -1: unwindTokens(m_ParenStack, tok::slash); + case 1: + case 0: break; + default: assert(0 && "Nested block comment count"); break; + } + // eof, were done anyway break; } - m_ParenStack.pop_back(); + else if (commentTok == tok::slash) { + // Cancel the wait for a slash, but only if current token isn't + // also an asterik. + if (kind != tok::asterik) + commentTok = tok::asterik; + } + } + + if (kind >= (int)tok::l_square && kind <= (int)tok::r_brace) { + // The closing paren kind is open paren kind + 1 (i.e odd number) + if (kind % 2) { + int prev = m_ParenStack.empty() ? -1: m_ParenStack.back(); + // closing the right one? + if (prev != kind - 1) { + if (multilineComment) + continue; + Res = kMismatch; + break; + } + m_ParenStack.pop_back(); + } + else + m_ParenStack.push_back(kind); + } + else if (kind >= (int)tok::stringlit && kind <= (int)tok::charlit) { + MetaLexer::LexQuotedStringAndAdvance(curPos, Tok); } - else - m_ParenStack.push_back(kind); } } while (Tok.isNot(tok::eof)); diff --git a/interpreter/cling/lib/MetaProcessor/InputValidator.h b/interpreter/cling/lib/MetaProcessor/InputValidator.h index f8d783e796969d6ce2eb79b826bb7e4948d4f5a0..91c359dcd96342ab1dd27e72f8584d5f13a808e3 100644 --- a/interpreter/cling/lib/MetaProcessor/InputValidator.h +++ b/interpreter/cling/lib/MetaProcessor/InputValidator.h @@ -68,6 +68,12 @@ namespace cling { ///\brief Resets the collected input and its corresponding brace stack. /// void reset(); + + ///\brief Return whether we are inside a mult-line comment + /// + ///\returns true if currently inside a multi-line comment block + /// + bool inBlockComment() const; }; } #endif // CLING_INPUT_VALIDATOR_H diff --git a/interpreter/cling/lib/MetaProcessor/MetaLexer.cpp b/interpreter/cling/lib/MetaProcessor/MetaLexer.cpp index 3a321f0b932dc868ab595fe25915dc8777647342..60db7d66e82894b1c65cd85f65f4a8613b108c58 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaLexer.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaLexer.cpp @@ -56,19 +56,18 @@ namespace cling { return LexQuotedStringAndAdvance(curPos, Tok); case '[': case ']': case '(': case ')': case '{': case '}': case '\\': case ',': case '.': case '!': case '?': case '>': - case '&': case '#': case '@': + case '&': case '#': case '@': case '*': // INTENTIONAL FALL THROUGHs return LexPunctuator(curPos - 1, Tok); case '/': - if (*curPos != '/') - return LexPunctuator(curPos - 1, Tok); - else { + if (*curPos == '/') { ++curPos; Tok.setKind(tok::comment); Tok.setLength(2); return; } + return LexPunctuator(curPos - 1, Tok); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -127,6 +126,7 @@ namespace cling { case '@' : Tok.setKind(tok::at); break; case '&' : Tok.setKind(tok::ampersand); break; case '#' : Tok.setKind(tok::hash); break; + case '*' : Tok.setKind(tok::asterik); break; case '\0' : Tok.setKind(tok::eof); Tok.setLength(0); break;// if static call default: Tok.setLength(0); break; } diff --git a/interpreter/cling/lib/MetaProcessor/MetaLexer.h b/interpreter/cling/lib/MetaProcessor/MetaLexer.h index 6a0aaa2bc030a30ae5f5954f820b65b07edef57e..a7ac2623a34788ebd33d2cff2213774c53ffedec 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaLexer.h +++ b/interpreter/cling/lib/MetaProcessor/MetaLexer.h @@ -39,6 +39,7 @@ namespace cling { space, // (' ' | '\t')* constant, // {0-9} at, // @ + asterik, // * eof, unknown }; diff --git a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp index fa905e4fff2c106f5af56d9b66dce5facea38922..ccce89a825463df64745799b8ed2ee7d73b0c6ad 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp @@ -149,7 +149,8 @@ namespace cling { // Check for and handle meta commands. m_MetaParser->enterNewInputLine(input_line); MetaSema::ActionResult actionResult = MetaSema::AR_Success; - if (m_MetaParser->isMetaCommand(actionResult, result)) { + if (!m_InputValidator->inBlockComment() && + m_MetaParser->isMetaCommand(actionResult, result)) { if (m_MetaParser->isQuitRequested()) return -1; diff --git a/interpreter/cling/test/Prompt/BlockComments.C b/interpreter/cling/test/Prompt/BlockComments.C new file mode 100644 index 0000000000000000000000000000000000000000..374d579c2a44e7a71f4580aefc4c0a597a87cd6d --- /dev/null +++ b/interpreter/cling/test/Prompt/BlockComments.C @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ + +// RUN: cat %s | %cling -Xclang -verify 2>&1 | FileCheck %s +// Test blockComments + +(/* + 1 + 2 + 3 */ + 8 + // single line 1 + /* single line 2*/ +) +// CHECK: (int) 8 + +// Check nested indentation works +/* + { + ( + [ + ] + ) + } +*/ + +// Check nested indentation doesn't error on mismatched closures +/* + { + [ + ( + } + ) + ] +*/ + +( 5 + /* + + 1 + + 2 + + 3 */ + + 4 + // single line 1 + /* single line 2*/ +) +// CHECK-NEXT: (int) 9 + +/* + This should work + // As should this // */ + +/* + This will warn + // /* */ // expected-warning {{within block comment}} + +.rawInput 1 +*/ // expected-error {{expected unqualified-id}} +.rawInput 0 + + +// This is a side effect of wrapping, expression is compiled as */; so 2 errors +*/ // expected-error@2 {{expected expression}} expected-error@3 {{expected expression}} + + +// Check meta-commands are blocked out +/* + .L SomeStuff + .x some more + .q +*/ + +( 5 + /* + + 10 + + 20 */ + /* + + 30 + */ + + 4 + // single line 1 + + 10 + /* single line 2*/ + /* ) */ +) +// CHECK-NEXT: (int) 19 + +/* 8 + */ 9 /* = 20 */ +// CHECK-NEXT: (int) 9 + +/* +// Check inline asteriks +******************************************************* +* Row * Instance * fEvents.fEventNo * fShowers * +******************************************************* +* 0 * 0 * 0 * 10 * +* 0 * 1 * 0 * 20 * +* 0 * 2 * 2 * 30 * +******************************************************* +*/ + +32 +// CHECK-NEXT: (int) 32 + +/* Check inline asteriks ****/ +62 +// CHECK-NEXT: (int) 62 + +.q