2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
# 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"
# include "clang/Basic/FileManager.h"
# include "clang/Basic/TargetInfo.h"
# include "clang/Frontend/CompilerInstance.h"
# include "clang/Lex/Preprocessor.h"
# include "llvm/Support/Path.h"
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>
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 {
MetaProcessor : : MetaProcessor ( Interpreter & interp ) : m_Interp ( interp ) {
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 ,
2012-10-29 18:23:53 +04:00
StoredValueRef * result /*=0*/ ,
Interpreter : : CompilationResult * compRes /*=0*/ ) {
int expectedIndent = m_InputValidator - > getExpectedIndent ( ) ;
if ( compRes ) {
if ( expectedIndent ) {
* compRes = Interpreter : : kMoreInputExpected ;
} else {
* compRes = Interpreter : : kSuccess ;
}
2012-09-05 13:37:39 +04:00
}
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 ) ;
if ( m_MetaParser - > isMetaCommand ( ) ) {
2012-12-06 15:47:24 +04:00
if ( m_MetaParser - > isQuitRequested ( ) )
return - 1 ;
2012-12-01 04:45:43 +04:00
//TODO: set the compilation result if there was error in the meta commands
2012-12-07 17:04:19 +04:00
if ( result )
* result = m_MetaParser - > getLastResultedValue ( ) ;
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 ) {
if ( compRes )
* 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-10-29 18:23:53 +04:00
Interpreter : : CompilationResult compResLocal ;
2012-12-06 14:40:20 +04:00
// if (m_Options.RawInput)
// compResLocal = m_Interp.declare(input);
// else
compResLocal = m_Interp . process ( input , result ) ;
2012-10-29 18:23:53 +04:00
if ( compRes ) * compRes = compResLocal ;
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 ,
2012-10-29 18:23:53 +04:00
StoredValueRef * result ,
Interpreter : : CompilationResult * compRes /*=0*/ ) {
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
interpRes = m_Interp . process ( expression , result ) ;
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
}
2012-10-29 18:23:53 +04:00
if ( compRes ) * 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 ,
StoredValueRef * result /* = 0 */ ,
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 ) {
llvm : : sys : : LLVMFileType fileType
= llvm : : sys : : IdentifyFileType ( magic , 4 ) ;
if ( fileType ! = llvm : : sys : : Unknown_FileType ) {
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 ;
2012-11-09 14:54:07 +04:00
Interpreter : : CompilationResult ret = Interpreter : : kSuccess ;
process ( content . c_str ( ) , result , & ret ) ;
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 ;
}
} // end namespace cling