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"
# include "InputValidator.h"
# 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"
using namespace clang ;
namespace cling {
2012-09-05 19:27:17 +04:00
namespace tok {
enum TokenKind {
commandstart ,
ident ,
l_paren ,
r_paren ,
anystring ,
comma ,
booltrue ,
boolfalse ,
eof ,
unknown
} ;
}
class Token {
private :
tok : : TokenKind kind ;
const char * bufStart ;
const char * bufEnd ;
void startToken ( ) {
bufStart = 0 ;
bufEnd = 0 ;
kind = tok : : unknown ;
}
public :
tok : : TokenKind getKind ( ) const { return kind ; }
size_t getLength ( ) const { return bufEnd - bufStart ; }
const char * getBufStart ( ) const { return bufStart ; }
friend class CommandLexer ;
} ;
class CommandLexer {
private :
const char * bufferStart ;
const char * bufferEnd ;
const char * curPos ;
const InvocationOptions & m_Opts ;
public :
CommandLexer ( const char * bufStart , const char * bufEnd ,
const InvocationOptions & Opts )
: bufferStart ( bufStart ) , bufferEnd ( bufEnd ) , curPos ( bufStart ) , m_Opts ( Opts )
{ }
void LexBlankSpace ( ) {
// TODO: React on EOF.
while ( * curPos = = ' ' | | * curPos = = ' \t ' )
+ + curPos ;
}
bool LexCommandSymbol ( Token & Result ) {
Result . startToken ( ) ;
LexBlankSpace ( ) ;
Result . bufStart = curPos ;
for ( size_t i = 0 ; i < m_Opts . MetaString . size ( ) ; + + i , + + curPos )
if ( * curPos ! = m_Opts . MetaString [ i ] ) {
Result . bufEnd = Result . bufStart ;
return false ;
}
Result . bufEnd = curPos ;
return true ;
}
bool LexIdent ( Token & Result ) {
Result . startToken ( ) ;
Result . bufStart = curPos ;
unsigned char C = * curPos ;
while ( ( C > = ' A ' & & C < = ' Z ' ) | | ( C > = ' a ' & & C < = ' z ' ) )
C = * + + curPos ;
Result . bufEnd = curPos ;
if ( Result . getLength ( ) > 0 ) {
Result . kind = tok : : ident ;
return true ;
}
return false ;
}
bool LexAnyString ( Token & Result ) {
Result . startToken ( ) ;
LexBlankSpace ( ) ;
Result . bufStart = curPos ;
unsigned char C = * curPos ;
while ( C ! = ' ' & & C ! = ' \n ' & & C ! = ' \t ' & &
C ! = ' \0 ' & & C ! = ' ( ' & & C ! = ' ) ' )
C = * + + curPos ;
Result . bufEnd = curPos ;
if ( Result . getLength ( ) > 0 ) {
Result . kind = tok : : anystring ;
return true ;
}
return false ;
}
bool LexSpecialSymbol ( Token & Result ) {
Result . startToken ( ) ;
LexBlankSpace ( ) ;
Result . bufStart = curPos ;
if ( * curPos = = ' ( ' ) {
Result . kind = tok : : l_paren ;
+ + curPos ;
}
else if ( * curPos = = ' ) ' ) {
Result . kind = tok : : r_paren ;
+ + curPos ;
}
else if ( * curPos = = ' , ' ) {
Result . kind = tok : : comma ;
+ + curPos ;
}
Result . bufEnd = curPos ;
return Result . kind ! = tok : : unknown ;
}
bool LexBool ( Token & Result ) {
Result . startToken ( ) ;
LexBlankSpace ( ) ;
Result . bufStart = curPos ;
if ( * curPos = = ' 0 ' ) {
Result . kind = tok : : boolfalse ;
+ + curPos ;
}
else if ( * curPos = = ' 1 ' ) {
Result . kind = tok : : booltrue ;
+ + curPos ;
}
Result . bufEnd = curPos ;
return Result . kind ! = tok : : unknown ;
}
} ;
2012-09-05 13:37:39 +04:00
MetaProcessor : : MetaProcessor ( Interpreter & interp ) : m_Interp ( interp ) {
m_InputValidator . reset ( new InputValidator ( ) ) ;
}
MetaProcessor : : ~ MetaProcessor ( ) { }
int MetaProcessor : : process ( const char * input_text , Value * result /*=0*/ ) {
if ( ! input_text ) { // null pointer, nothing to do.
return 0 ;
}
if ( ! input_text [ 0 ] ) { // empty string, nothing to do.
return m_InputValidator - > getExpectedIndent ( ) ;
}
std : : string input_line ( input_text ) ;
if ( input_line = = " \n " ) { // just a blank line, nothing to do.
return 0 ;
}
// Check for and handle meta commands.
2012-09-05 19:27:17 +04:00
if ( ProcessMeta ( input_line , result ) ) {
2012-09-05 13:37:39 +04:00
return 0 ;
}
// Check if the current statement is now complete. If not, return to
// prompt for more.
if ( m_InputValidator - > validate ( input_line , m_Interp . getCI ( ) - > getLangOpts ( ) )
= = InputValidator : : kIncomplete ) {
return m_InputValidator - > getExpectedIndent ( ) ;
}
// We have a complete statement, compile and execute it.
std : : string input = m_InputValidator - > getInput ( ) ;
m_InputValidator - > reset ( ) ;
if ( m_Options . RawInput )
m_Interp . declare ( input ) ;
else
m_Interp . process ( input , result ) ;
return 0 ;
}
MetaProcessorOpts & MetaProcessor : : getMetaProcessorOpts ( ) {
// Take interpreter's state
m_Options . PrintingAST = m_Interp . isPrintingAST ( ) ;
return m_Options ;
}
2012-09-05 19:27:17 +04:00
// Command syntax: meta_command := <command_symbol><command>[arg_list]
// command_symbol := '.' | '//.'
// command := ident
// arg_list := any_string[(extra_arg_list)] [' ' arg_list]
// extra_arg_list := any_string [, extra_arg_list]
//
2012-09-05 13:37:39 +04:00
bool MetaProcessor : : ProcessMeta ( const std : : string & input_line , Value * result ) {
llvm : : MemoryBuffer * MB = llvm : : MemoryBuffer : : getMemBuffer ( input_line ) ;
Token Tok ;
2012-09-05 19:27:17 +04:00
CommandLexer CmdLexer ( MB - > getBufferStart ( ) , MB - > getBufferEnd ( ) ,
m_Interp . getOptions ( ) ) ;
2012-09-05 13:37:39 +04:00
2012-09-05 19:27:17 +04:00
if ( ! CmdLexer . LexCommandSymbol ( Tok ) ) {
// No error because it may be line containing code
return false ;
2012-09-05 13:37:39 +04:00
}
2012-09-05 19:27:17 +04:00
if ( ! CmdLexer . LexIdent ( Tok ) ) {
llvm : : errs ( ) < < " Command name token expected. Try .help \n " ;
return false ;
2012-09-05 13:37:39 +04:00
}
2012-09-05 19:27:17 +04:00
llvm : : StringRef Command ( Tok . getBufStart ( ) , Tok . getLength ( ) ) ;
// Should be used for faster comparison if the command is only one char long.
unsigned char CmdStartChar = * Tok . getBufStart ( ) ;
2012-09-05 13:37:39 +04:00
2012-09-05 19:27:17 +04:00
// .q Exits the process.
if ( CmdStartChar = = ' q ' ) {
m_Options . Quitting = true ;
2012-09-05 13:37:39 +04:00
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( CmdStartChar = = ' L ' ) {
if ( ! CmdLexer . LexAnyString ( Tok ) ) {
llvm : : errs ( ) < < " Filename expected. \n " ;
2012-09-05 13:37:39 +04:00
return false ;
}
2012-09-05 19:27:17 +04:00
//TODO: Check if the file exists and is readable.
if ( ! m_Interp . loadFile ( llvm : : StringRef ( Tok . getBufStart ( ) , Tok . getLength ( ) ) ) )
llvm : : errs ( ) < < " Load file failed. \n " ;
2012-09-05 13:37:39 +04:00
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( CmdStartChar = = ' x ' | | CmdStartChar = = ' X ' ) {
if ( ! CmdLexer . LexAnyString ( Tok ) ) {
llvm : : errs ( ) < < " Filename expected. \n " ;
2012-09-05 13:37:39 +04:00
return false ;
}
2012-09-05 19:27:17 +04:00
llvm : : sys : : Path file ( llvm : : StringRef ( Tok . getBufStart ( ) , Tok . getLength ( ) ) ) ;
llvm : : StringRef args ;
// TODO: Check whether the file exists using the compilers header search.
// if (!file.canRead()) {
// llvm::errs() << "File doesn't exist or not readable.\n";
// return false;
// }
CmdLexer . LexSpecialSymbol ( Tok ) ;
if ( Tok . getKind ( ) = = tok : : l_paren ) {
// Good enough for now.
if ( ! CmdLexer . LexAnyString ( Tok ) ) {
llvm : : errs ( ) < < " Argument list expected. \n " ;
return false ;
}
args = llvm : : StringRef ( Tok . getBufStart ( ) , Tok . getLength ( ) ) ;
if ( ! CmdLexer . LexSpecialSymbol ( Tok ) & & Tok . getKind ( ) = = tok : : r_paren ) {
llvm : : errs ( ) < < " Closing parenthesis expected. \n " ;
return false ;
}
}
if ( ! executeFile ( file . str ( ) , args , result ) )
llvm : : errs ( ) < < " Execute file failed. \n " ;
return true ;
2012-09-05 13:37:39 +04:00
}
2012-09-05 19:27:17 +04:00
else if ( CmdStartChar = = ' U ' ) {
// if (!CmdLexer.LexAnyString(Tok)) {
// llvm::errs() << "Filename expected.\n";
// return false;
2012-09-05 13:37:39 +04:00
// }
2012-09-05 19:27:17 +04:00
// llvm::sys::Path file(llvm::StringRef(Tok.bufStart, Tok.getLength()));
// TODO: Check whether the file exists using the compilers header search.
// if (!file.canRead()) {
// llvm::errs() << "File doesn't exist or not readable.\n";
// return false;
// } else
// if (file.isDynamicLibrary()) {
// llvm::errs() << "cannot unload shared libraries yet!.\n";
// return false;
// }
// TODO: Later comes more fine-grained unloading. For now just:
2012-09-05 13:37:39 +04:00
m_Interp . unload ( ) ;
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( CmdStartChar = = ' @ ' ) {
m_InputValidator - > reset ( ) ;
return true ;
}
else if ( CmdStartChar = = ' I ' ) {
if ( CmdLexer . LexAnyString ( Tok ) ) {
llvm : : sys : : Path path ( llvm : : StringRef ( Tok . getBufStart ( ) , Tok . getLength ( ) ) ) ;
// TODO: Check whether the file exists using the compilers header search.
// if (!path.canRead()) {
// llvm::errs() << "Path doesn't exist or not readable.\n";
// return false;
// }
m_Interp . AddIncludePath ( path . str ( ) ) ;
}
else {
2012-09-05 13:37:39 +04:00
m_Interp . DumpIncludePath ( ) ;
2012-09-05 19:27:17 +04:00
}
return true ;
}
else if ( Command . equals ( " printAST " ) ) {
if ( ! CmdLexer . LexBool ( Tok ) ) {
bool flag = ! m_Interp . isPrintingAST ( ) ;
m_Interp . enablePrintAST ( flag ) ;
llvm : : outs ( ) < < ( flag ? " P " : " Not p " ) < < " rinting AST \n " ;
}
2012-09-05 13:37:39 +04:00
else {
2012-09-05 19:27:17 +04:00
if ( Tok . getKind ( ) = = tok : : boolfalse )
m_Interp . enablePrintAST ( false ) ;
else if ( Tok . getKind ( ) = = tok : : booltrue )
m_Interp . enablePrintAST ( true ) ;
else {
llvm : : errs ( ) < < " Boolean value expected. \n " ;
2012-09-05 13:37:39 +04:00
return false ;
2012-09-05 19:27:17 +04:00
}
2012-09-05 13:37:39 +04:00
}
2012-09-05 19:27:17 +04:00
//m_Options.PrintingAST = m_Interp.isPrintingAST(); ????
2012-09-05 13:37:39 +04:00
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( Command . equals ( " rawInput " ) ) {
if ( ! CmdLexer . LexBool ( Tok ) ) {
m_Options . RawInput = ! m_Options . RawInput ;
llvm : : outs ( ) < < ( m_Options . RawInput ? " U " : " Not u " ) < < " sing raw input \n " ;
}
else {
if ( Tok . getKind ( ) = = tok : : boolfalse )
m_Options . RawInput = false ;
else if ( Tok . getKind ( ) = = tok : : booltrue )
m_Options . RawInput = true ;
else {
llvm : : errs ( ) < < " Boolean value expected. \n " ;
return false ;
}
}
2012-09-05 13:37:39 +04:00
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( Command . equals ( " dynamicExtensions " ) ) {
if ( ! CmdLexer . LexBool ( Tok ) ) {
bool flag = ! m_Interp . isDynamicLookupEnabled ( ) ;
m_Interp . enableDynamicLookup ( flag ) ;
llvm : : outs ( ) < < ( flag ? " U " : " Not u " ) < < " sing dynamic extensions \n " ;
}
else {
if ( Tok . getKind ( ) = = tok : : boolfalse )
2012-09-05 13:37:39 +04:00
m_Interp . enableDynamicLookup ( false ) ;
2012-09-05 19:27:17 +04:00
else if ( Tok . getKind ( ) = = tok : : booltrue )
2012-09-05 13:37:39 +04:00
m_Interp . enableDynamicLookup ( true ) ;
2012-09-05 19:27:17 +04:00
else {
llvm : : errs ( ) < < " Boolean value expected. \n " ;
return false ;
}
2012-09-05 13:37:39 +04:00
}
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( Command . equals ( " help " ) ) {
2012-09-05 13:37:39 +04:00
PrintCommandHelp ( ) ;
return true ;
}
2012-09-05 19:27:17 +04:00
else if ( Command . equals ( " file " ) ) {
2012-09-05 13:37:39 +04:00
PrintFileStats ( ) ;
return true ;
}
return false ;
}
void MetaProcessor : : PrintCommandHelp ( ) {
std : : string & metaString = m_Interp . getOptions ( ) . MetaString ;
llvm : : outs ( ) < < " Cling meta commands usage \n " ;
llvm : : outs ( ) < < " Syntax: .Command [arg0 arg1 ... argN] \n " ;
llvm : : outs ( ) < < " \n " ;
llvm : : outs ( ) < < metaString < < " q \t \t \t \t - Exit the program \n " ;
llvm : : outs ( ) < < metaString < < " L <filename> \t \t \t - Load file or library \n " ;
llvm : : outs ( ) < < metaString < < " (x|X) <filename>[args] \t \t - Same as .L and runs a " ;
llvm : : outs ( ) < < " function with signature " ;
llvm : : outs ( ) < < " \t \t \t \t ret_type filename(args) \n " ;
llvm : : outs ( ) < < metaString < < " I [path] \t \t \t - Shows the include path. If a path is " ;
llvm : : outs ( ) < < " given - \n \t \t \t \t adds the path to the include paths \n " ;
llvm : : outs ( ) < < metaString < < " @ \t \t \t \t - Cancels and ignores the multiline input \n " ;
llvm : : outs ( ) < < metaString < < " rawInput [0|1] \t \t \t - Toggle wrapping and printing " ;
llvm : : outs ( ) < < " the execution \n \t \t \t \t results of the input \n " ;
llvm : : outs ( ) < < metaString < < " dynamicExtensions [0|1] \t - Toggles the use of the " ;
llvm : : outs ( ) < < " dynamic scopes and the \t \t \t \t late binding \n " ;
llvm : : outs ( ) < < metaString < < " printAST [0|1] \t \t \t - Toggles the printing of input's " ;
llvm : : outs ( ) < < " corresponding \t \t \t \t AST nodes \n " ;
llvm : : outs ( ) < < metaString < < " help \t \t \t \t - Shows this information \n " ;
}
void MetaProcessor : : PrintFileStats ( ) {
const SourceManager & SM = m_Interp . getCI ( ) - > getSourceManager ( ) ;
SM . getFileManager ( ) . PrintStats ( ) ;
llvm : : outs ( ) < < " \n *** \n \n " ;
for ( SourceManager : : fileinfo_iterator I = SM . fileinfo_begin ( ) ,
E = SM . fileinfo_end ( ) ; I ! = E ; + + I ) {
llvm : : outs ( ) < < ( * I ) . first - > getName ( ) ;
llvm : : outs ( ) < < " \n " ;
}
}
// Run a file: .x file[(args)]
2012-09-05 19:27:17 +04:00
bool MetaProcessor : : executeFile ( llvm : : StringRef file , llvm : : StringRef args ,
2012-09-05 13:37:39 +04:00
Value * result ) {
// Look for start of parameters:
typedef std : : pair < llvm : : StringRef , llvm : : StringRef > StringRefPair ;
2012-09-05 19:27:17 +04:00
// StringRefPair pairFileArgs = llvm::StringRef(fileWithArgs).split('(');
// if (pairFileArgs.second.empty()) {
// pairFileArgs.second = ")";
// }
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-09-07 10:56:06 +04:00
Interpreter : : CompilationResult interpRes = Interpreter : : kSuccess ;
2012-09-05 19:27:17 +04:00
if ( m_Interp . loadFile ( file ) ) {
std : : string expression = pairFuncExt . first . str ( ) + " ( " + args . str ( ) + " ) " ;
interpRes = m_Interp . evaluate ( expression , result ) ;
2012-09-05 13:37:39 +04:00
}
return ( interpRes ! = Interpreter : : kFailure ) ;
}
} // end namespace cling