2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Axel Naumann <axel@cern.ch>
2014-01-07 14:08:37 +04:00
//
// 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.
2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
# include "cling/MetaProcessor/MetaProcessor.h"
2012-11-28 20:50:13 +04:00
# include "Display.h"
2012-09-05 13:37:39 +04:00
# include "InputValidator.h"
2012-12-01 04:45:43 +04:00
# include "MetaParser.h"
# include "MetaSema.h"
2012-09-05 13:37:39 +04:00
# include "cling/Interpreter/Interpreter.h"
2013-06-10 17:14:36 +04:00
# include "cling/Interpreter/StoredValueRef.h"
2012-09-05 13:37:39 +04:00
# include "clang/Basic/FileManager.h"
# include "clang/Basic/TargetInfo.h"
# include "clang/Frontend/CompilerInstance.h"
# include "clang/Lex/Preprocessor.h"
2013-09-19 19:59:58 +04:00
# include "llvm/Support/Path.h"
2012-09-05 13:37:39 +04:00
2012-10-30 17:40:25 +04:00
# include <fstream>
2012-11-28 20:38:02 +04:00
# include <cstdlib>
2012-11-21 00:21:29 +04:00
# include <cctype>
2013-12-18 18:51:27 +04:00
# include <unistd.h>
# include <stdio.h>
2012-10-30 17:40:25 +04:00
2012-11-28 20:38:02 +04:00
2012-09-05 13:37:39 +04:00
using namespace clang ;
namespace cling {
2013-12-18 18:51:27 +04:00
MetaProcessor : : MaybeRedirectOutputRAII : : MaybeRedirectOutputRAII ( MetaProcessor * p )
: m_MetaProcessor ( p ) , terminalOut ( 0 ) , terminalErr ( 0 ) {
//Empty file acts as a flag.
if ( ! m_MetaProcessor - > m_FileOut . empty ( ) ) {
terminalOut = ttyname ( STDOUT_FILENO ) ;
stdout = freopen ( m_MetaProcessor - > m_FileOut . c_str ( ) , " a " , stdout ) ;
}
//Empty file acts as a flag.
if ( ! m_MetaProcessor - > m_FileErr . empty ( ) ) {
terminalErr = ttyname ( STDERR_FILENO ) ;
stderr = freopen ( m_MetaProcessor - > m_FileErr . c_str ( ) , " a " , stderr ) ;
}
}
void MetaProcessor : : MaybeRedirectOutputRAII : : pop ( ) {
//Switch back to standard output after line is processed.
if ( terminalOut ) {
stdout = freopen ( terminalOut , " w " , stdout ) ;
}
if ( terminalErr ) {
stderr = freopen ( terminalErr , " w " , stderr ) ;
}
}
2013-01-17 19:27:14 +04:00
MetaProcessor : : MetaProcessor ( Interpreter & interp , raw_ostream & outs )
2013-12-18 18:51:27 +04:00
: m_Interp ( interp ) , m_Outs ( outs ) , m_FileOut ( " " ) , m_FileErr ( " " ) {
2012-09-05 13:37:39 +04:00
m_InputValidator . reset ( new InputValidator ( ) ) ;
2012-12-01 04:45:43 +04:00
m_MetaParser . reset ( new MetaParser ( new MetaSema ( interp , * this ) ) ) ;
2012-09-05 13:37:39 +04:00
}
MetaProcessor : : ~ MetaProcessor ( ) { }
2012-10-05 16:09:51 +04:00
int MetaProcessor : : process ( const char * input_text ,
2013-06-10 17:14:36 +04:00
Interpreter : : CompilationResult & compRes ,
StoredValueRef * result ) {
if ( result )
* result = StoredValueRef : : invalidValue ( ) ;
2013-05-24 19:50:13 +04:00
compRes = Interpreter : : kSuccess ;
2012-10-29 18:23:53 +04:00
int expectedIndent = m_InputValidator - > getExpectedIndent ( ) ;
2013-05-24 19:50:13 +04:00
if ( expectedIndent )
compRes = Interpreter : : kMoreInputExpected ;
2012-10-29 18:23:53 +04:00
if ( ! input_text | | ! input_text [ 0 ] ) {
// nullptr / empty string, nothing to do.
return expectedIndent ;
2012-09-05 13:37:39 +04:00
}
std : : string input_line ( input_text ) ;
if ( input_line = = " \n " ) { // just a blank line, nothing to do.
2012-10-29 18:23:53 +04:00
return expectedIndent ;
2012-09-05 13:37:39 +04:00
}
// Check for and handle meta commands.
2012-12-01 04:45:43 +04:00
m_MetaParser - > enterNewInputLine ( input_line ) ;
2013-05-24 19:50:13 +04:00
MetaSema : : ActionResult actionResult = MetaSema : : AR_Success ;
2013-06-10 17:14:36 +04:00
if ( m_MetaParser - > isMetaCommand ( actionResult , result ) ) {
2012-12-06 15:47:24 +04:00
if ( m_MetaParser - > isQuitRequested ( ) )
return - 1 ;
2013-05-24 19:50:13 +04:00
if ( actionResult ! = MetaSema : : AR_Success )
compRes = Interpreter : : kFailure ;
2012-10-29 18:23:53 +04:00
return expectedIndent ;
2012-09-05 13:37:39 +04:00
}
// Check if the current statement is now complete. If not, return to
// prompt for more.
2012-12-01 04:45:43 +04:00
if ( m_InputValidator - > validate ( input_line ) = = InputValidator : : kIncomplete ) {
2013-05-24 19:50:13 +04:00
compRes = Interpreter : : kMoreInputExpected ;
2012-09-05 13:37:39 +04:00
return m_InputValidator - > getExpectedIndent ( ) ;
}
// We have a complete statement, compile and execute it.
std : : string input = m_InputValidator - > getInput ( ) ;
m_InputValidator - > reset ( ) ;
2012-12-06 14:40:20 +04:00
// if (m_Options.RawInput)
// compResLocal = m_Interp.declare(input);
// else
2013-06-10 17:14:36 +04:00
compRes = m_Interp . process ( input , result ) ;
2012-09-05 13:37:39 +04:00
return 0 ;
}
2012-12-06 15:47:24 +04:00
void MetaProcessor : : cancelContinuation ( ) const {
2012-12-04 12:05:33 +04:00
m_InputValidator - > reset ( ) ;
}
2012-12-06 15:47:24 +04:00
int MetaProcessor : : getExpectedIndent ( ) const {
return m_InputValidator - > getExpectedIndent ( ) ;
2012-09-05 13:37:39 +04:00
}
// Run a file: .x file[(args)]
2012-09-05 19:27:17 +04:00
bool MetaProcessor : : executeFile ( llvm : : StringRef file , llvm : : StringRef args ,
2013-06-10 17:14:36 +04:00
Interpreter : : CompilationResult & compRes ,
StoredValueRef * result ) {
2012-09-05 13:37:39 +04:00
// Look for start of parameters:
typedef std : : pair < llvm : : StringRef , llvm : : StringRef > StringRefPair ;
2012-09-05 19:27:17 +04:00
StringRefPair pairPathFile = file . rsplit ( ' / ' ) ;
2012-09-05 13:37:39 +04:00
if ( pairPathFile . second . empty ( ) ) {
pairPathFile . second = pairPathFile . first ;
}
StringRefPair pairFuncExt = pairPathFile . second . rsplit ( ' . ' ) ;
2012-10-29 18:23:53 +04:00
Interpreter : : CompilationResult interpRes = m_Interp . loadFile ( file ) ;
if ( interpRes = = Interpreter : : kSuccess ) {
2012-09-05 19:27:17 +04:00
std : : string expression = pairFuncExt . first . str ( ) + " ( " + args . str ( ) + " ) " ;
2012-11-19 04:00:53 +04:00
m_CurrentlyExecutingFile = file ;
bool topmost = ! m_TopExecutingFile . data ( ) ;
if ( topmost )
2012-11-21 00:21:29 +04:00
m_TopExecutingFile = m_CurrentlyExecutingFile ;
2012-12-07 17:04:19 +04:00
2013-06-10 17:14:36 +04:00
interpRes = m_Interp . process ( expression , result ) ;
2012-12-07 17:04:19 +04:00
2012-11-19 04:00:53 +04:00
m_CurrentlyExecutingFile = llvm : : StringRef ( ) ;
if ( topmost )
m_TopExecutingFile = llvm : : StringRef ( ) ;
2012-09-05 13:37:39 +04:00
}
2013-05-24 19:50:13 +04:00
compRes = interpRes ;
2012-09-05 13:37:39 +04:00
return ( interpRes ! = Interpreter : : kFailure ) ;
}
2012-10-30 17:40:25 +04:00
Interpreter : : CompilationResult
MetaProcessor : : readInputFromFile ( llvm : : StringRef filename ,
2013-06-10 17:14:36 +04:00
StoredValueRef * result ,
2012-10-30 17:40:25 +04:00
bool ignoreOutmostBlock /*=false*/ ) {
2012-11-09 14:54:07 +04:00
2012-11-21 00:21:29 +04:00
{
// check that it's not binary:
std : : ifstream in ( filename . str ( ) . c_str ( ) , std : : ios : : in | std : : ios : : binary ) ;
char magic [ 1024 ] = { 0 } ;
in . read ( magic , sizeof ( magic ) ) ;
size_t readMagic = in . gcount ( ) ;
if ( readMagic > = 4 ) {
2013-11-21 20:52:57 +04:00
llvm : : StringRef magicStr ( magic , in . gcount ( ) ) ;
2013-09-19 19:05:28 +04:00
llvm : : sys : : fs : : file_magic fileType
2013-11-21 20:52:57 +04:00
= llvm : : sys : : fs : : identify_magic ( magicStr ) ;
2013-09-19 19:05:28 +04:00
if ( fileType ! = llvm : : sys : : fs : : file_magic : : unknown ) {
2012-11-21 00:21:29 +04:00
llvm : : errs ( ) < < " Error in cling::MetaProcessor: "
" cannot read input from a binary file! \n " ;
return Interpreter : : kFailure ;
}
unsigned printable = 0 ;
2012-11-21 14:51:41 +04:00
for ( size_t i = 0 ; i < readMagic ; + + i )
2012-11-21 00:21:29 +04:00
if ( isprint ( magic [ i ] ) )
+ + printable ;
if ( 10 * printable < 5 * readMagic ) {
// 50% printable for ASCII files should be a safe guess.
llvm : : errs ( ) < < " Error in cling::MetaProcessor: "
" cannot read input from a (likely) binary file! \n " < < printable ;
return Interpreter : : kFailure ;
}
}
}
2012-10-30 17:40:25 +04:00
std : : ifstream in ( filename . str ( ) . c_str ( ) ) ;
2012-11-09 14:54:07 +04:00
in . seekg ( 0 , std : : ios : : end ) ;
size_t size = in . tellg ( ) ;
std : : string content ( size , ' ' ) ;
in . seekg ( 0 ) ;
in . read ( & content [ 0 ] , size ) ;
if ( ignoreOutmostBlock & & ! content . empty ( ) ) {
static const char whitespace [ ] = " \t \r \n " ;
std : : string : : size_type posNonWS = content . find_first_not_of ( whitespace ) ;
2012-12-09 14:07:41 +04:00
std : : string : : size_type replaced = posNonWS ;
2012-10-30 17:40:25 +04:00
if ( posNonWS ! = std : : string : : npos ) {
2012-11-09 14:54:07 +04:00
if ( content [ posNonWS ] = = ' { ' ) {
2012-10-30 17:40:25 +04:00
// hide the curly brace:
2012-11-09 14:54:07 +04:00
content [ posNonWS ] = ' ' ;
2012-10-30 17:40:25 +04:00
// and the matching closing '}'
2012-11-09 14:54:07 +04:00
posNonWS = content . find_last_not_of ( whitespace ) ;
2012-10-30 17:40:25 +04:00
if ( posNonWS ! = std : : string : : npos ) {
2012-11-09 14:54:07 +04:00
if ( content [ posNonWS ] = = ' ; ' & & content [ posNonWS - 1 ] = = ' } ' ) {
content [ posNonWS - - ] = ' ' ; // replace ';' and enter next if
}
if ( content [ posNonWS ] = = ' } ' ) {
content [ posNonWS ] = ' ' ; // replace '}'
} else {
2012-12-09 14:07:41 +04:00
// More text (comments) are okay after the last '}', but
// we can not easily find it to remove it (so we need to upgrade
// this code to better handle the case with comments or
// preprocessor code before and after the leading { and
// trailing })
content [ replaced ] = ' { ' ;
// By putting the '{' back, we keep the code as consistent as
// the user wrote it ... but we should still warn that we not
// goint to treat this file an unamed macro.
llvm : : errs ( )
< < " Warning in cling::MetaProcessor: can not find the closing '}', "
< < llvm : : sys : : path : : filename ( filename )
< < " is not handled as an unamed script! \n " ;
2012-11-09 14:54:07 +04:00
}
} // find '}'
} // have '{'
} // have non-whitespace
} // ignore outmost block
2012-11-21 00:21:29 +04:00
std : : string strFilename ( filename . str ( ) ) ;
m_CurrentlyExecutingFile = strFilename ;
bool topmost = ! m_TopExecutingFile . data ( ) ;
if ( topmost )
m_TopExecutingFile = m_CurrentlyExecutingFile ;
2013-06-10 17:14:36 +04:00
Interpreter : : CompilationResult ret ;
if ( process ( content . c_str ( ) , ret , result ) ) {
2013-05-17 13:17:08 +04:00
// Input file has to be complete.
llvm : : errs ( )
< < " Error in cling::MetaProcessor: file "
< < llvm : : sys : : path : : filename ( filename )
< < " is incomplete (missing parenthesis or similar)! \n " ;
ret = Interpreter : : kFailure ;
}
2012-11-21 00:21:29 +04:00
m_CurrentlyExecutingFile = llvm : : StringRef ( ) ;
if ( topmost )
m_TopExecutingFile = llvm : : StringRef ( ) ;
2012-10-30 17:40:25 +04:00
return ret ;
}
2013-12-18 18:51:27 +04:00
void MetaProcessor : : setStdStream ( llvm : : StringRef file ,
2014-01-13 11:57:12 +04:00
RedirectionScope stream , bool append ) {
if ( stream & kSTDOUT | | stream & kSTDBOTH ) {
2013-12-18 18:51:27 +04:00
m_FileOut = file ;
if ( ! append & & ! m_FileOut . empty ( ) ) {
FILE * f = fopen ( m_FileOut . c_str ( ) , " w " ) ;
}
}
2014-01-13 11:57:12 +04:00
if ( stream & kSTDERR | | stream & kSTDBOTH ) {
2013-12-18 18:51:27 +04:00
m_FileErr = file ;
if ( ! append & & ! m_FileErr . empty ( ) ) {
FILE * f = fopen ( m_FileErr . c_str ( ) , " w " ) ;
}
}
}
2012-10-30 17:40:25 +04:00
} // end namespace cling