|
From: <net...@us...> - 2004-01-13 22:18:30
|
Update of /cvsroot/cpptool/rfta/src/rftaparser
In directory sc8-pr-cvs1:/tmp/cvs-serv8283/rftaparser
Modified Files:
PreProcessorTest.cpp PreProcessorTest.h PreProcessor.cpp
Added Files:
FileSourceManager.cpp PositionTranslator.cpp
PositionTranslatorTest.cpp PositionTranslatorTest.h
PreProcessorContext.cpp Source.cpp SourceTest.cpp SourceTest.h
Log Message:
preprocessor implementation
--- NEW FILE: FileSourceManager.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Source file FileSourceManager.cpp for class FileSourceManager
// (c)Copyright 2003, Andre Baresel.
// Created: 2003/12/28
// //////////////////////////////////////////////////////////////////////////
#include <iosfwd>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <rfta/parser/FileSourceManager.h>
#include <rfta/parser/Source.h>
#include <rfta/parser/PlainTextDocument.h>
#include <boost/format.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem//operations.hpp>
static std::string readSourceFile( const boost::filesystem::path &path )
{
boost::filesystem::ifstream stream( path );
if (!stream.is_open()) return "";
std::string source;
while ( !stream.eof() )
{
std::string line;
std::getline( stream, line );
source += line;
source += '\n';
}
return source;
}
namespace Refactoring
{
SourcePtr
FileSourceManager::open(std::string sourceName)
{
if (sourceName=="stdexcept")
int i = 0;
// try original name
boost::filesystem::path path( sourceName,
boost::filesystem::native );
// try relative path to root source
if (!boost::filesystem::exists(path))
path = sourcePath_ / path;
// try include paths:
if (!boost::filesystem::exists(path))
{
PathList::iterator i;
boost::filesystem::path sourcepath( sourceName,
boost::filesystem::native );
for (i= includeDirList_.begin(); i!=includeDirList_.end(); i++)
{
if (boost::filesystem::exists(*i / sourcepath))
{
path = *i / sourcepath;
break;
}
}
}
std::string source;
if (boost::filesystem::exists(path))
source = readSourceFile( path );
TextDocumentPtr doc(new PlainTextDocument(source));
SourcePtr result(new Source(doc, path.native_file_string()));
return result;
}
} // namespace Refactoring
--- NEW FILE: PositionTranslator.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Source file PositionTranslator.cpp for class PositionTranslator
// (c)Copyright 2003, Andre Baresel.
// Created: 2003/12/31
// //////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include <rfta/parser/PositionTranslator.h>
namespace Refactoring
{
void
PositionTranslator::addReplacement(const SourceRange& range, int replacementLen)
{
TranslationMap::const_iterator before = getLower(range.getStartIndex());
SourceRange tmp_preprocessed = range;
int diffBefore = 0;
if ( before != translationMap_.end() && before->first < range.getStartIndex() )
{
diffBefore = before->second.offsetDifference_;
tmp_preprocessed.moveStartIndexBy(-diffBefore);
}
tmp_preprocessed.setLength(replacementLen);
Transposition entry;
entry.origin_ = range;
entry.offsetDifference_ = range.getLength()-replacementLen+diffBefore;
entry.preprocessed_ = tmp_preprocessed;
translationMap_[tmp_preprocessed.getStartIndex()] = entry;
int moveBy = range.getLength()-replacementLen;
changeFollowingTranslation(translationMap_.find(tmp_preprocessed.getStartIndex()),moveBy);
}
void
PositionTranslator::changeFollowingTranslation(TranslationMap::iterator first, int moveBy)
{
if (moveBy == 0) return; // nothing to do.
if (moveBy < 0)
{
TranslationMap::iterator it = translationMap_.end();
it--;
while (it != first)
{
Transposition entry = it->second;
entry.preprocessed_.moveStartIndexBy(-moveBy);
entry.offsetDifference_ += moveBy;
TranslationMap::iterator todel = it;it--;
translationMap_.erase(todel);
translationMap_[entry.preprocessed_.getStartIndex()] = entry;
}
} else
{
TranslationMap::iterator it = first;
it++;
while (it != translationMap_.end())
{
Transposition entry = it->second;
entry.preprocessed_.moveStartIndexBy(-moveBy);
entry.offsetDifference_ += moveBy;
TranslationMap::iterator todel = it;it++;
translationMap_.erase(todel);
translationMap_[entry.preprocessed_.getStartIndex()] = entry;
}
}
}
/**
* translates the position of preprocessed source code into
* the original range.
*/
SourceRange
PositionTranslator::translate(const SourceRange& range) const
{
TranslationMap::const_iterator before = getLower(range.getStartIndex());
SourceRange result = range;
if (before != translationMap_.end() && before->first < range.getStartIndex())
{
if (range.getStartIndex() < before->second.preprocessed_.getEndIndex())
{
if (before == translationMap_.begin())
return result;
before--;
}
result.moveStartIndexBy(before->second.offsetDifference_);
}
return result;
}
/**
* translates the position of original source code into
* the range in preprocessed.
*/
SourceRange
PositionTranslator::reverseTranslate(const SourceRange& range) const
{
SourceRange result=range;
TranslationMap::const_iterator it = translationMap_.begin();
int offset = 0;
while (it!=translationMap_.end() && it->second.origin_.getStartIndex() <= range.getStartIndex())
{
it++;
}
// no translation before:
if (it == translationMap_.begin())
return result;
it--;
// check if inside translation range:
if (range.getStartIndex() < it->second.origin_.getEndIndex() )
{
if (it == translationMap_.begin()) // inside first translation--> leave range as it is
return result;
it--;
}
result.moveStartIndexBy(-it->second.offsetDifference_);
return result;
}
PositionTranslator::TranslationMap::const_iterator
PositionTranslator::getLower(int index) const
{
TranslationMap::const_iterator it = translationMap_.begin();
while (it!=translationMap_.end() && it->first < index)
it++;
if (it != translationMap_.begin()) it--;
return it;
}
PositionTranslator::TranslationMap::const_iterator
PositionTranslator::getUpper(int index) const
{
TranslationMap::const_iterator it = translationMap_.begin();
while (it!=translationMap_.end() && it->first <= index)
it++;
if (it!=translationMap_.end()) it++;
return it;
}
} // namespace Refactoring
--- NEW FILE: PositionTranslatorTest.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Source file PositionTranslatorTest.cpp for class PositionTranslatorTest
// (c)Copyright 2003, Andre Baresel
// Created: 2003/12/13
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "PositionTranslatorTest.h"
#include <rfta/parser/PositionTranslator.h>
namespace Refactoring
{
RFTAPARSER_TEST_SUITE_REGISTRATION( PositionTranslatorTest );
PositionTranslatorTest::PositionTranslatorTest()
{
}
PositionTranslatorTest::~PositionTranslatorTest()
{
}
void
PositionTranslatorTest::setUp()
{
}
void
PositionTranslatorTest::tearDown()
{
}
void
PositionTranslatorTest::testExtensions()
{
PositionTranslator trans;
/**
* test shows position translation for three ranges enlarged by different
* replacements:
*/
SourceRange r1(0,10);
trans.addReplacement(r1,20); // increase size by 10 = (20-10)
SourceRange r2(20,10);
trans.addReplacement(r2,25); // increase size by 15 = (25-10)
SourceRange r3(15,5);
trans.addReplacement(r3,10); // increase size by 5 = (10-5)
// original source ranges:
// |x10x| 5 |x 5x|x10x| (the 'x' mark enlarged ranges)
// equivalent source ranges after replacements:
// |+20+| 5 |+10+|+25+| (the '+' mark the new size)
// now translate positions of 'source-after-replacment' into original positions.
CPPUNIT_ASSERT_EQUAL(SourceRange(5,2),trans.translate(SourceRange(5,2))); /* within first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(11,3),trans.translate(SourceRange(21,3))); /* after first replc. -> move by 10 */
CPPUNIT_ASSERT_EQUAL(SourceRange(15,3),trans.translate(SourceRange(25,3))); /* within second replc. -> move by 10 */
CPPUNIT_ASSERT_EQUAL(SourceRange(20,3),trans.translate(SourceRange(35,3))); /* after second replc. -> move by 15 */
CPPUNIT_ASSERT_EQUAL(SourceRange(30,1),trans.translate(SourceRange(60,1))); /* after third replc. -> move by 30 */
}
void
PositionTranslatorTest::testShorten()
{
PositionTranslator trans;
// source ranges of original code and their change by replacements
SourceRange r1(30,10);
trans.addReplacement(r1,5);
SourceRange r2(20,5);
trans.addReplacement(r2,1);
SourceRange r3(5,15);
trans.addReplacement(r3,5);
// original source:
// 5 |x15x|x5x| 5 |x10x| (the 'x' mark reduced ranges)
// source after replacements:
// 5 |-5-|-1-| 5 |-5-| (the '-' mark the new size)
// now translate positions of 'source-after-replacment' into original positions.
CPPUNIT_ASSERT_EQUAL(SourceRange(0,2),trans.translate(SourceRange(0,2))); /* before first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(7,1),trans.translate(SourceRange(7,1))); /* within first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(20,3),trans.translate(SourceRange(10,3))); /* within second replc. -> move by -10 */
CPPUNIT_ASSERT_EQUAL(SourceRange(26,3),trans.translate(SourceRange(12,3))); /* after second replc. -> move by -14 */
CPPUNIT_ASSERT_EQUAL(SourceRange(31,3),trans.translate(SourceRange(17,3))); /* within third replc. -> move by -14 */
CPPUNIT_ASSERT_EQUAL(SourceRange(41,5),trans.translate(SourceRange(22,5))); /* after third replc. -> move by -19 */
}
void
PositionTranslatorTest::testMixture()
{
PositionTranslator trans;
SourceRange r1(5,15);
trans.addReplacement(r1,5);
SourceRange r2(30,10);
trans.addReplacement(r2,14);
SourceRange r3(20,5);
trans.addReplacement(r3,10);
SourceRange r4(25,5);
trans.addReplacement(r4,1);
// original source:
// 5 |x15x|x5x|x5x|x10x| (the 'x' mark reduced ranges)
// source after replacements:
// 5 |-5-|+10+|-1-|+14+| (the '-/+' mark the new size)
// now translate positions of 'source-after-replacment' into original positions.
CPPUNIT_ASSERT_EQUAL(SourceRange(0,2),trans.translate( SourceRange(0,2))); /* before first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(7,1),trans.translate( SourceRange(7,1))); /* within first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(20,3),trans.translate(SourceRange(10,3))); /* within second replc. -> move by -10 */
CPPUNIT_ASSERT_EQUAL(SourceRange(25,3),trans.translate(SourceRange(20,3))); /* within third replc. -> move by -15 */
CPPUNIT_ASSERT_EQUAL(SourceRange(31,5),trans.translate(SourceRange(22,5))); /* within fourth replc. -> move by -9 */
CPPUNIT_ASSERT_EQUAL(SourceRange(40,5),trans.translate(SourceRange(35,5))); /* after fourth replc. -> move by -5 */
}
void
PositionTranslatorTest::testReverseTranslate()
{
PositionTranslator trans;
SourceRange r1(5,15);
trans.addReplacement(r1,5);
SourceRange r2(30,10);
trans.addReplacement(r2,14);
SourceRange r3(20,5);
trans.addReplacement(r3,10);
SourceRange r4(25,5);
trans.addReplacement(r4,1);
// original source:
// 5 |x15x|x5x|x5x|x10x| (the 'x' mark reduced ranges)
// source after replacements:
// 5 |-5-|+10+|-1-|+14+| (the '-/+' mark the new size)
// now translate positions of 'source-after-replacment' into original positions.
CPPUNIT_ASSERT_EQUAL(SourceRange(0,2),trans.reverseTranslate( SourceRange(0,2))); /* before first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(7,1),trans.reverseTranslate( SourceRange(7,1))); /* within first replacement -> no change */
CPPUNIT_ASSERT_EQUAL(SourceRange(10,3),trans.reverseTranslate(SourceRange(20,3))); /* within second replc. -> move by -10 */
CPPUNIT_ASSERT_EQUAL(SourceRange(20,3),trans.reverseTranslate(SourceRange(25,3))); /* within third replc. -> move by -15 */
CPPUNIT_ASSERT_EQUAL(SourceRange(22,5),trans.reverseTranslate(SourceRange(31,5))); /* within fourth replc. -> move by -9 */
CPPUNIT_ASSERT_EQUAL(SourceRange(35,5),trans.reverseTranslate(SourceRange(40,5))); /* after fourth replc. -> move by -5 */
}
} // namespace Refactoring
--- NEW FILE: PositionTranslatorTest.h ---
// //////////////////////////////////////////////////////////////////////////
// Header file PositionTranslatorTest.h for class PositionTranslatorTest
// (c)Copyright 2003, Andre Baresel
// Created: 2003/12/31
// //////////////////////////////////////////////////////////////////////////
#ifndef RFTA_POSITIONTRANSLATORTEST_H
#define RFTA_POSITIONTRANSLATORTEST_H
#include "ParserTesting.h"
namespace Refactoring
{
/// Unit tests for PreProcessor
class PositionTranslatorTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( PositionTranslatorTest );
CPPUNIT_TEST( testExtensions );
CPPUNIT_TEST( testShorten );
CPPUNIT_TEST( testMixture );
CPPUNIT_TEST( testReverseTranslate );
CPPUNIT_TEST_SUITE_END();
public:
/*! Constructs a PositionTranslatorTest object.
*/
PositionTranslatorTest();
/// Destructor.
virtual ~PositionTranslatorTest();
void setUp();
void tearDown();
void testExtensions();
void testShorten();
void testMixture();
void testReverseTranslate();
private:
/// Prevents the use of the copy constructor.
PositionTranslatorTest( const PositionTranslatorTest &other );
/// Prevents the use of the copy operator.
void operator =( const PositionTranslatorTest &other );
private:
};
// Inlines methods for PositionTranslatorTest:
// ----------------------------------------
} // namespace Refactoring
#endif // RFTA_POSITIONTRANSLATORTEST_H
--- NEW FILE: PreProcessorContext.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Source file PreProcessorContext.h for class PreProcessorContext
// (c)Copyright 2003, Andre Baresel
// Created: 2003/12/13
// //////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include <rfta/parser/PreProcessorContext.h>
#include <rfta/parser/PreProcessorError.h>
#include <xtl/Type.h>
#include <xtl/CStringView.h>
#include <rfta/parser/ParserTools.h>
#include <rfta/parser/SourceManager.h>
#include <rfta/parser/PreProcessor.h>
#include <rfta/parser/Source.h>
namespace Refactoring
{
bool
PreProcessorContext::isMacroIdentifier( std::string identifier )
{
MacroDefMap::iterator i = macroDef_.find(identifier);
return i != macroDef_.end();
}
std::string
PreProcessorContext::getMacroReplacement( std::string macroident )
{
MacroDefMap::iterator i = macroDef_.find(macroident);
if (i == macroDef_.end())
ThrowPreProcessError(std::string("Macro '")+macroident+"'is not defined.");
return i->second;
}
void
PreProcessorContext::addMacro(std::string ident, std::string replacement)
{
macroDef_[ident] = replacement;
}
/**
* set 'source' as active source and store
* the former source-ptr.
*/
void
PreProcessorContext::enterSource(SourcePtr source)
{
sources_.push(source_);
source_ = source;
}
/**
* leave current source and return to parent.
* (restore former source-ptr)
*/
void
PreProcessorContext::leaveSource()
{
source_ = sources_.top();
sources_.pop();
}
} // namespace Refactoring
--- NEW FILE: Source.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Implementation file Source.cpp for class Source
// (c)Copyright 2003, Andre Baresel.
// Created: 2003/12/25
// //////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include <rfta/parser/Source.h>
namespace Refactoring
{
Source::Source(TextDocumentPtr document)
: document_(document)
{
}
Source::Source(TextDocumentPtr document, std::string identifier)
: document_(document)
, identifier_(identifier)
{
}
Source::~Source()
{
}
/**
* returns the source that holds the range.
* (does care about includes)
*/
Source&
Source::getSourceOrigin(const SourceRange& range)
{
IncludeList::const_iterator i = getSourceOriginInternal(range);
if (i!=includeList_.end())
{
return *(i->include_);
}
else
{
return *this;
}
}
/**
* return the source text of the range in original file.
* (does translate the position and uses the original source)
*/
std::string
Source::getOriginalText(const SourceRange& range)
{
IncludeList::const_iterator where = getSourceOriginInternal(range);
// check if the range is located in include source:
if (where != includeList_.end())
{
SourceRange r = translator_.translate(range);
r.moveStartIndexBy(-where->range_.getStartIndex());
// get it from included source
return where->include_->getOriginalText(r);
}
// translate position in current source:
SourceRange original = translator_.translate(range);
std::string completeTxt = getOriginalText();
int len = original.getLength();
// TODO: throw exception:
if (completeTxt.length() < original.getStartIndex() + len)
len = completeTxt.length() - original.getStartIndex();
return std::string( completeTxt.c_str() + original.getStartIndex(),
len );
}
/**
* returns the range in original source
*/
SourceRange
Source::getRangeOrigin(const SourceRange& range) const
{
IncludeList::const_iterator where = getSourceOriginInternal(range);
// check if the range is located in include source:
if (where != includeList_.end())
{
SourceRange r = translator_.translate(range);
r.moveStartIndexBy(-where->range_.getStartIndex());
// get it from included source
return where->include_->getRangeOrigin(r);
}
return translator_.translate(range);
}
/**
* returns the range in preprocessed source
*/
SourceRange
Source::getRangePreProcessed(const SourceRange& range)
{
return translator_.reverseTranslate(range);
}
/**
* returns the includelist iterator position that holds the range.
* --> end() in case of no include
*/
Source::IncludeList::const_iterator
Source::getSourceOriginInternal(const SourceRange& range) const
{
IncludeList::const_iterator i;
SourceRange first = range;
first.setLength(1); // check if first character falls into the include-range
for (i=includeList_.begin(); i!=includeList_.end(); i++)
{
if (i->preprocessedRange_.contains(first))
{
break;
}
}
return i;
}
/**
* store range of an include
*/
void
Source::registerInclude(const SourceRange& range, int includeLength, const SourcePtr include)
{
IncludeEntry entry;
entry.include_ = include;
entry.range_ = range;
entry.preprocessedRange_ = translator_.reverseTranslate( range );
entry.preprocessedRange_.setLength( includeLength );
includeList_.push_back(entry);
translator_.addReplacement(range,includeLength);
}
/**
* store range of a text replacement (e.g. Macro)
*/
void
Source::registerReplacement(const SourceRange& range, int replacementLen)
{
translator_.addReplacement(range,replacementLen);
}
/**
* @param index position in preprocessed source text.
*
* @return line number
*/
int
Source::getLineNumberOf(int index) const
{
SourceRange range(index,1);
IncludeList::const_iterator where = getSourceOriginInternal(range);
if (where != includeList_.end())
{
SourceRange r = translator_.translate(range);
r.moveStartIndexBy(-where->range_.getStartIndex());
// get it from included source
return where->include_->getLineNumberOf(r.getStartIndex());
}
SourceRange r = getRangeOrigin(range);
std::string completeTxt = document_->getAllText();
return std::count( completeTxt.c_str(), completeTxt.c_str()+r.getStartIndex(), '\n' ) +1;
}
} // namespace Refactoring
--- NEW FILE: SourceTest.cpp ---
// //////////////////////////////////////////////////////////////////////////
// Source file SourceTest.cpp for class SourceTest
// (c)Copyright 2004, Andre Baresel
// Created: 2004/1/1
// //////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include "SourceTest.h"
#include "KeyedString.h"
#include <rfta/parser/Source.h>
#include "TextDocumentMock.h"
namespace Refactoring
{
RFTAPARSER_TEST_SUITE_REGISTRATION( SourceTest );
SourceTest::SourceTest()
{
}
SourceTest::~SourceTest()
{
}
void
SourceTest::setUp()
{
}
void
SourceTest::tearDown()
{
}
void
SourceTest::testSimpleInclude()
{
/**
* 'source' object is storing original source text and
* links to includes. It can restore offset from preprocessed source to
* original source text. This is tested here.
*/
// original source:
Testing::KeyedString str;
str.addKeyed("BEFORE", "int var_before;\n");
str.addKeyed("INCLUDE","#include <header.h>\n");
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// source of include:
std::string doc2_txt = "int inside_header;";
TextDocumentPtr doc2 = TextDocumentPtr(new Testing::TextDocumentMock(doc2_txt));
SourcePtr include = SourcePtr(new Source(doc2));
// preprocessor operation that registers an include:
SourceRange directive_range(str.getKeyedIndex("INCLUDE",0),str.getKeyedLen("INCLUDE",0));
src.registerInclude(directive_range,doc2_txt.length(),SourcePtr(include));
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("BEFORE", "int var_before;") << " ";
preprocessed.addKeyed("INCLUDE","int inside_header;");
preprocessed.addKeyed("AFTER", "int var_after;");
// this are the offsets which will be tested:
SourceRange before_range(SourceRange(preprocessed.getKeyedIndex("BEFORE",0),preprocessed.getKeyedLen("BEFORE",0)));
SourceRange include_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE",0),preprocessed.getKeyedLen("INCLUDE",0)));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(std::string("int var_before;"), src.getOriginalText(before_range));
CPPUNIT_ASSERT_EQUAL(std::string("int inside_header;"), src.getOriginalText(include_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
void
SourceTest::testNestedInclude()
{
/**
* 'source' object is storing original source text and
* links to includes. It can restore offset from preprocessed source to
* original source text. This is tested here.
*/
// original source:
Testing::KeyedString str;
str.addKeyed("BEFORE", "int var_before;\n");
str.addKeyed("INCLUDE","#include <header.h>\n");
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// source of includes:
std::string doc2_txt = "int inside_header;\n#include <header2.h>\n";
TextDocumentPtr doc2 = TextDocumentPtr(new Testing::TextDocumentMock(doc2_txt));
SourcePtr include = SourcePtr(new Source(doc2));
std::string doc3_txt = "int inside_header2;";
TextDocumentPtr doc3 = TextDocumentPtr(new Testing::TextDocumentMock(doc3_txt));
SourcePtr include2 = SourcePtr(new Source(doc3));
// preprocessor operation that registers an include:
SourceRange directive_range(str.getKeyedIndex("INCLUDE",0),str.getKeyedLen("INCLUDE",0));
src.registerInclude(directive_range,doc2_txt.length()-2,SourcePtr(include)); // '-2' is correction of nested include
// preprocessing nested header
SourceRange directive2_range(19,21); // position of '#include <header2.h>' in doc2_txt
include->registerInclude(directive2_range,doc3_txt.length(),SourcePtr(include2));
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("BEFORE", "int var_before;") << " ";
preprocessed.addKeyed("INCLUDE", "int inside_header;") << " ";
preprocessed.addKeyed("INCLUDE2","int inside_header2;");
preprocessed.addKeyed("AFTER", "int var_after;");
// this are the offsets which will be tested:
SourceRange before_range(SourceRange(preprocessed.getKeyedIndex("BEFORE",0),preprocessed.getKeyedLen("BEFORE",0)));
SourceRange include_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE",0),preprocessed.getKeyedLen("INCLUDE",0)));
SourceRange include2_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE2",0),preprocessed.getKeyedLen("INCLUDE2",0)));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(std::string("int var_before;"), src.getOriginalText(before_range));
CPPUNIT_ASSERT_EQUAL(std::string("int inside_header;"), src.getOriginalText(include_range));
CPPUNIT_ASSERT_EQUAL(std::string("int inside_header2;"), src.getOriginalText(include2_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
void
SourceTest::testIncludeSequence()
{
// original source:
Testing::KeyedString str;
str.addKeyed("BEFORE", "int var_before;\n");
str.addKeyed("INCLUDE", "#include <header.h>\n");
str.addKeyed("INCLUDE2","#include <header2.h>\n");
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// source of includes:
std::string doc2_txt = "int h1;";
TextDocumentPtr doc2 = TextDocumentPtr(new Testing::TextDocumentMock(doc2_txt));
SourcePtr include = SourcePtr(new Source(doc2));
std::string doc3_txt = "int h2;";
TextDocumentPtr doc3 = TextDocumentPtr(new Testing::TextDocumentMock(doc3_txt));
SourcePtr include2 = SourcePtr(new Source(doc3));
// preprocessor operation that registers an include:
SourceRange directive_range(str.getKeyedIndex("INCLUDE",0),str.getKeyedLen("INCLUDE",0));
src.registerInclude(directive_range,doc2_txt.length(),SourcePtr(include));
// preprocessor operation that registers an include:
SourceRange directive2_range(str.getKeyedIndex("INCLUDE2",0),str.getKeyedLen("INCLUDE2",0));
src.registerInclude(directive2_range,doc3_txt.length(),SourcePtr(include2));
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("BEFORE", "int var_before;") << " ";
preprocessed.addKeyed("INCLUDE", "int h1;");
preprocessed.addKeyed("INCLUDE2","int h2;");
preprocessed.addKeyed("AFTER", "int var_after;");
// this are the offsets which will be tested:
SourceRange before_range(SourceRange(preprocessed.getKeyedIndex("BEFORE",0),preprocessed.getKeyedLen("BEFORE",0)));
SourceRange include_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE",0),preprocessed.getKeyedLen("INCLUDE",0)));
SourceRange include2_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE2",0),preprocessed.getKeyedLen("INCLUDE2",0)));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(std::string("int var_before;"), src.getOriginalText(before_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h1;"), src.getOriginalText(include_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h2;"), src.getOriginalText(include2_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
void
SourceTest::testSimpleMacro()
{
// original source:
Testing::KeyedString str;
std::string macro_txt = "int macrorepl;";
std::string macro_def = std::string("#define DEF_VAR ")+macro_txt+"\n";
str.addKeyed("DEF", macro_def);
str << "{\n";
str.addKeyed("USE", "DEF_VAR") << "\n";
str << "}\n";
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// preprocessing macro definition replacement:
SourceRange directive1_range(str.getKeyedIndex("DEF",0),str.getKeyedLen("DEF",0));
src.registerReplacement(directive1_range,1);
// preprocessing macro use replacement:
SourceRange directive2_range(str.getKeyedIndex("USE",0),str.getKeyedLen("USE",0));
src.registerReplacement(directive2_range,macro_txt.length());
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("DEF", " ") << "{ ";
preprocessed.addKeyed("USE", macro_txt.c_str() ) << " } ";
preprocessed.addKeyed("AFTER", "int var_after;") << " ";
// this are the offsets which will be tested:
SourceRange def_range(SourceRange(preprocessed.getKeyedIndex("DEF",0),macro_def.length()));
SourceRange use_range(SourceRange(preprocessed.getKeyedIndex("USE",0),7));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(macro_def , src.getOriginalText(def_range));
CPPUNIT_ASSERT_EQUAL(std::string("DEF_VAR"), src.getOriginalText(use_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
void
SourceTest::testMixedIncludeMacro()
{
// original source:
Testing::KeyedString str;
str.addKeyed("BEFORE", "int var_before;\n");
str.addKeyed("INCLUDE", "#include <header.h>\n");
str.addKeyed("MACRO", "DEF_VAR") << "\n";
str.addKeyed("INCLUDE2","#include <header2.h>\n");
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// source of includes:
std::string doc2_txt = "int h1;";
TextDocumentPtr doc2 = TextDocumentPtr(new Testing::TextDocumentMock(doc2_txt));
SourcePtr include = SourcePtr(new Source(doc2));
std::string doc3_txt = "int h2;";
TextDocumentPtr doc3 = TextDocumentPtr(new Testing::TextDocumentMock(doc3_txt));
SourcePtr include2 = SourcePtr(new Source(doc3));
std::string macro_txt = "int macrorepl;";
// preprocessor operation that registers an include:
SourceRange directive_range(str.getKeyedIndex("INCLUDE",0),str.getKeyedLen("INCLUDE",0));
src.registerInclude(directive_range,doc2_txt.length(),SourcePtr(include));
// preprocessing macro replacement:
SourceRange directive2_range(str.getKeyedIndex("MACRO",0),str.getKeyedLen("MACRO",0));
src.registerReplacement(directive2_range,macro_txt.length());
// preprocessing nested header
SourceRange directive3_range(str.getKeyedIndex("INCLUDE2",0),str.getKeyedLen("INCLUDE2",0));
src.registerInclude(directive3_range,doc3_txt.length(),SourcePtr(include2));
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("BEFORE", "int var_before;") << " ";
preprocessed.addKeyed("INCLUDE", "int h1;");
preprocessed.addKeyed("MACRO", macro_txt.c_str() ) << " ";
preprocessed.addKeyed("INCLUDE2","int h2;");
preprocessed.addKeyed("AFTER", "int var_after;");
// this are the offsets which will be tested:
SourceRange before_range(SourceRange(preprocessed.getKeyedIndex("BEFORE",0),preprocessed.getKeyedLen("BEFORE",0)));
SourceRange include_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE",0),preprocessed.getKeyedLen("INCLUDE",0)));
SourceRange macro_range(SourceRange(preprocessed.getKeyedIndex("MACRO",0),7));
SourceRange include2_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE2",0),preprocessed.getKeyedLen("INCLUDE2",0)));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(std::string("int var_before;"), src.getOriginalText(before_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h1;"), src.getOriginalText(include_range));
CPPUNIT_ASSERT_EQUAL(std::string("DEF_VAR"), src.getOriginalText(macro_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h2;"), src.getOriginalText(include2_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
void
SourceTest::testMacroInsideInclude()
{
// original source:
Testing::KeyedString str;
str.addKeyed("BEFORE", "int var_before;\n");
str.addKeyed("INCLUDE", "#include <header.h>\n");
str.addKeyed("INCLUDE2","#include <header2.h>\n");
str.addKeyed("AFTER", "int var_after;\n");
TextDocumentPtr doc = TextDocumentPtr(new Testing::TextDocumentMock(str));
Source src(doc);
// source of includes:
std::string doc2_txt = "int h1;DEF_VAR\n";
TextDocumentPtr doc2 = TextDocumentPtr(new Testing::TextDocumentMock(doc2_txt));
SourcePtr include = SourcePtr(new Source(doc2));
std::string doc3_txt = "int h2;";
TextDocumentPtr doc3 = TextDocumentPtr(new Testing::TextDocumentMock(doc3_txt));
SourcePtr include2 = SourcePtr(new Source(doc3));
std::string macro_txt = "int m;";
// preprocessor operation that registers an include:
SourceRange directive_range(str.getKeyedIndex("INCLUDE",0),str.getKeyedLen("INCLUDE",0));
src.registerInclude(directive_range,doc2_txt.length()-1,SourcePtr(include)); // '-1' is correction of macro replacement
// preprocessing macro replacement:
SourceRange directive2_range(7,7);
include->registerReplacement(directive2_range,macro_txt.length());
// preprocessing nested header
SourceRange directive3_range(str.getKeyedIndex("INCLUDE2",0),str.getKeyedLen("INCLUDE2",0));
src.registerInclude(directive3_range,doc3_txt.length(),SourcePtr(include2));
// for testing we create the corresponding preprocessed source
// just to have offset to be restored:
Testing::KeyedString preprocessed;
preprocessed.addKeyed("BEFORE", "int var_before;") << " ";
preprocessed.setKeyStart("INCLUDE");
preprocessed << "int h1;";
preprocessed.addKeyed("MACRO", macro_txt.c_str() ) << " ";
preprocessed.setKeyEnd("INCLUDE");
preprocessed.addKeyed("INCLUDE2","int h2;");
preprocessed.addKeyed("AFTER", "int var_after;");
// this are the offsets which will be tested:
SourceRange before_range(SourceRange(preprocessed.getKeyedIndex("BEFORE",0),preprocessed.getKeyedLen("BEFORE",0)));
SourceRange include_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE",0),15));
SourceRange macro_range(SourceRange(preprocessed.getKeyedIndex("MACRO",0),7));
SourceRange include2_range(SourceRange(preprocessed.getKeyedIndex("INCLUDE2",0),preprocessed.getKeyedLen("INCLUDE2",0)));
SourceRange after_range(SourceRange(preprocessed.getKeyedIndex("AFTER",0),preprocessed.getKeyedLen("AFTER",0)));
// use the offsets of preprocessed text to get the original source text:
// imagine that 'source' does only know about registered 'include' and
// does the offset translation with this information)
CPPUNIT_ASSERT_EQUAL(str.asString(), src.getOriginalText());
CPPUNIT_ASSERT_EQUAL(std::string("int var_before;"), src.getOriginalText(before_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h1;DEF_VAR\n"), src.getOriginalText(include_range));
CPPUNIT_ASSERT_EQUAL(std::string("DEF_VAR"), src.getOriginalText(macro_range));
CPPUNIT_ASSERT_EQUAL(std::string("int h2;"), src.getOriginalText(include2_range));
CPPUNIT_ASSERT_EQUAL(std::string("int var_after;"), src.getOriginalText(after_range));
}
} // namespace Refactoring
--- NEW FILE: SourceTest.h ---
// //////////////////////////////////////////////////////////////////////////
// Header file SourceTest.h for class SourceTest
// (c)Copyright 2003, Andre Baresel
// Created: 2003/12/13
// //////////////////////////////////////////////////////////////////////////
#ifndef RFTA_SOURCETEST_H
#define RFTA_SOURCETEST_H
#include "ParserTesting.h"
namespace Refactoring
{
/// Unit tests for Source
class SourceTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( SourceTest );
CPPUNIT_TEST( testSimpleInclude );
CPPUNIT_TEST( testNestedInclude );
CPPUNIT_TEST( testIncludeSequence );
CPPUNIT_TEST( testSimpleMacro );
CPPUNIT_TEST( testMixedIncludeMacro );
CPPUNIT_TEST( testMacroInsideInclude );
CPPUNIT_TEST_SUITE_END();
public:
/*! Constructs a SourceTest object.
*/
SourceTest();
/// Destructor.
virtual ~SourceTest();
void setUp();
void tearDown();
void testSimpleInclude();
void testNestedInclude();
void testIncludeSequence();
void testSimpleMacro();
void testMixedIncludeMacro();
void testMacroInsideInclude();
private:
/// Prevents the use of the copy constructor.
SourceTest( const SourceTest &other );
/// Prevents the use of the copy operator.
void operator =( const SourceTest &other );
private:
};
// Inlines methods for SourceTest:
// ----------------------------------------
} // namespace Refactoring
#endif // RFTA_SOURCETEST_H
Index: PreProcessorTest.cpp
===================================================================
RCS file: /cvsroot/cpptool/rfta/src/rftaparser/PreProcessorTest.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** PreProcessorTest.cpp 16 Dec 2003 13:47:00 -0000 1.1
--- PreProcessorTest.cpp 13 Jan 2004 22:18:25 -0000 1.2
***************
*** 7,12 ****
#include "stdafx.h"
#include "PreProcessorTest.h"
#include <rfta/parser/PreProcessor.h>
- #include <rfta/parser/MacroReplaceRegistration.h>
namespace Refactoring
--- 7,14 ----
#include "stdafx.h"
#include "PreProcessorTest.h"
+ #include <rfta/test/SourceManagerMock.h>
+ #include <rfta/parser/Source.h>
+ #include "TextDocumentMock.h"
#include <rfta/parser/PreProcessor.h>
namespace Refactoring
***************
*** 40,62 ****
PreProcessorTest::testSimpleReplace()
{
! std::string source = "#define TEXT \"String\"\n\
! char * var = TEXT;\n\
! ";
! std::string expected= " \
! char * var = \"String\"; ";
! // do the preprocessing (use directive information)
std::string preprocessed;
! PreProcessor proc(source);
! MacroReplaceRegistration registration;
! preprocessed = proc.process(registration);
CPPUNIT_ASSERT_EQUAL(expected, preprocessed);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected size of Macro-Replacement-Registration is wrong.",
- 1, registration.getSize());
- MacroReplaceRegistration::MacroReplacement entry = registration.getEntry(0);
- CPPUNIT_ASSERT_EQUAL(SourceRange(43,4),entry.position_);
}
} // namespace Refactoring
--- 42,163 ----
PreProcessorTest::testSimpleReplace()
{
! std::string sourcetxt = "#define TEXT \"String\"\n"
! " char * var = TEXT;\n"
! " ";
! std::string expected= " "
! " char * var = \"String\"; ";
! // create a preprocessor context:
! Testing::SourceManagerMock manager;
! PreProcessorContext context(manager);
! TextDocumentPtr textdoc(new Testing::TextDocumentMock(sourcetxt));
! SourcePtr source(new Source(textdoc));
!
! PreProcessor proc(source,context);
!
std::string preprocessed;
! preprocessed = proc.process();
!
! CPPUNIT_ASSERT_EQUAL(std::string("[")+expected+std::string("]"), std::string("[")+preprocessed+std::string("]"));
! }
!
! void
! PreProcessorTest::testSingleInclude()
! {
! // setup:
! std::string sourcetxt = "#include <hello.h> \nint i;\
! char * var;";
! std::string helloinclude = "char * h;int x,y,z;\n";
!
! // expectation:
! std::string expected= "char * h;int x,y,z; int i;\
! char * var;";
!
! TextDocumentPtr sourcedoc(new Testing::TextDocumentMock(sourcetxt));
! SourcePtr source(new Source(sourcedoc));
!
! // prepare some mock objects for 'include file' accesses
! Testing::SourceManagerMock manager;
! TextDocumentPtr textdoc(new Testing::TextDocumentMock(helloinclude));
! manager.setSource("hello.h",SourcePtr(new Source(textdoc)));
!
! // create a preprocessor context with prepared mockups
! PreProcessorContext context(manager);
!
! PreProcessor proc(source,context);
!
! std::string preprocessed;
! preprocessed = proc.process();
CPPUNIT_ASSERT_EQUAL(expected, preprocessed);
}
+ void
+ PreProcessorTest::testSingleIfDef()
+ {
+ // setup:
+ std::string sourcetxt = "#ifdef NOT\n\
+ char * nvar;\n#endif\nchar * var;";
+
+ // expectation:
+ std::string expected= " char * var;";
+
+ TextDocumentPtr sourcedoc(new Testing::TextDocumentMock(sourcetxt));
+ SourcePtr source(new Source(sourcedoc));
+
+ // prepare some mock objects for 'include file' accesses
+ Testing::SourceManagerMock manager;
+
+ // create a preprocessor context with prepared mockups
+ PreProcessorContext context(manager);
+
+ PreProcessor proc(source,context);
+
+ std::string preprocessed;
+ preprocessed = proc.process();
+
+ CPPUNIT_ASSERT_EQUAL(expected, preprocessed);
+ }
+
+ void
+ PreProcessorTest::testNestedIfDef()
+ {
+ // setup:
+ std::string sourcetxt =
+
+ "#define YES\n"
+
+ "#ifndef NOT\n"
+ "#ifndef YES\n"
+ "char * this_not;\n"
+ "#else\n"
+ "short but_this;\n"
+ "#endif\n"
+ "#else\n"
+ "#ifndef NOT\n"
+ "int i;\n"
+ "#endif\n"
+ "#endif\n"
+ "char * var;";
+
+ // expectation:
+ std::string expected= " short but_this; char * var;";
+
+ TextDocumentPtr sourcedoc(new Testing::TextDocumentMock(sourcetxt));
+ SourcePtr source(new Source(sourcedoc));
+
+ // prepare some mock objects for 'include file' accesses
+ Testing::SourceManagerMock manager;
+
+ // create a preprocessor context with prepared mockups
+ PreProcessorContext context(manager);
+
+ PreProcessor proc(source,context);
+
+ std::string preprocessed;
+ preprocessed = proc.process();
+
+ CPPUNIT_ASSERT_EQUAL(expected, preprocessed);
+ }
} // namespace Refactoring
Index: PreProcessorTest.h
===================================================================
RCS file: /cvsroot/cpptool/rfta/src/rftaparser/PreProcessorTest.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** PreProcessorTest.h 16 Dec 2003 13:47:00 -0000 1.1
--- PreProcessorTest.h 13 Jan 2004 22:18:25 -0000 1.2
***************
*** 17,20 ****
--- 17,23 ----
CPPUNIT_TEST_SUITE( PreProcessorTest );
CPPUNIT_TEST( testSimpleReplace );
+ CPPUNIT_TEST( testSingleInclude );
+ CPPUNIT_TEST( testSingleIfDef );
+ CPPUNIT_TEST( testNestedIfDef );
CPPUNIT_TEST_SUITE_END();
***************
*** 31,34 ****
--- 34,40 ----
void testSimpleReplace();
+ void testSingleInclude();
+ void testSingleIfDef();
+ void testNestedIfDef();
private:
Index: PreProcessor.cpp
===================================================================
RCS file: /cvsroot/cpptool/rfta/src/rftaparser/PreProcessor.cpp,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** PreProcessor.cpp 19 Dec 2003 20:13:21 -0000 1.2
--- PreProcessor.cpp 13 Jan 2004 22:18:25 -0000 1.3
***************
*** 8,97 ****
#include <rfta/parser/PreProcessor.h>
#include <xtl/Type.h>
#include <xtl/CStringView.h>
#include <rfta/parser/SourceRange.h>
#include <rfta/parser/ParserTools.h>
! #include "PreProcessResolver.h"
! #include <rfta/parser/NonSemanticBlanker.h>
namespace Refactoring
{
! PreProcessor::PreProcessor( const std::string &textToPreProcess):
! toPreProcess_(textToPreProcess)
{
}
/**
! * does tokenize the string, skipping strings and symbols.
! * only identifiers will be checked for macro identifiers.
*
*/
std::string
! PreProcessor::process(MacroReplaceListener& listener)
! {
! std::string preprocessed;
! std::string blanked(toPreProcess_);
! // first blank the source code
! PreProcessResolverPtr pResolver(new PreProcessResolver());
! NonSemanticBlanker blanker(toPreProcess_, blanked, pResolver);
! blanker.blank();
! // now do the preprocessing
! const char * start = blanked.c_str();
! const char * end = start + blanked.length();
! bool foundIdentifier;
! const char * current = start;
! do
! {
! const char * lastpos = current;
! int identlen = 0;
! foundIdentifier = false;
! while (current != end && !ParserTools::isIdentifierLetter(*current))
{
! current++;
}
! if (current != end && ParserTools::isValidIdentifierFirstLetter(*current))
{
! foundIdentifier = true;
! while (current+identlen != end && ParserTools::isIdentifierLetter(*(current+identlen)))
! identlen++;
}
! else
! // this is a number - skip:
! while (current != end && ParserTools::isIdentifierLetter(*current))
! current++;
!
! if (foundIdentifier)
{
! // copy the skipped characters:
! preprocessed += std::string( lastpos,current-lastpos);
! // do the replacement:
! std::string ident(current,identlen);
! if (pResolver->isMacroIdentifier(ident))
{
! // replace
! std::string replacement = pResolver->getMacroReplacement(ident);
! preprocessed += replacement;
! // register macro replacement
! SourceRange position(current-start,identlen);
! listener.macroReplacement(position,ident,replacement);
} else
! // keep it, this is no macro
! preprocessed += ident;
! current+=identlen;
! } else
! {
! // copy the last characters:
! preprocessed += std::string( lastpos,(end-lastpos));
! }
! } while (current != end);
! return preprocessed;
}
--- 8,464 ----
#include <rfta/parser/PreProcessor.h>
+ #include <rfta/parser/PreProcessorError.h>
#include <xtl/Type.h>
#include <xtl/CStringView.h>
#include <rfta/parser/SourceRange.h>
#include <rfta/parser/ParserTools.h>
! #include <rfta/parser/Source.h>
namespace Refactoring
{
! PreProcessor::PreProcessor( const SourcePtr source,PreProcessorContext& context)
! :source_(source)
! ,toPreProcess_(source->getOriginalText())
! ,context_(context)
{
}
/**
! * processes directives, blank text strings,
! * tokenize the string:
! * + skip symbols.
! * + identifiers will be compared with macro identifiers.
*
*/
std::string
! PreProcessor::process()
! {
! blanked_ = toPreProcess_;
! const char *str = toPreProcess_.c_str();
! textStart_ = str;
! textEnd_ = str + toPreProcess_.length();
! // blank the source code and replace macros / insert includes
! current_ = textStart_;
! newLine_ = true;
! offset_diff = 0;
! while ( current_ != textEnd_ )
! {
! char c = *current_;
! if ( c == '/' && current_[1] )
! {
! char c2 = current_[1];
! if ( c2 == '/' )
! current_ = deleteUntilEol( current_ );
! else if ( c2 == '*' )
! current_ = deleteCComment( current_ );
! else
! ++current_;
! }
! else
! blanksStringsAndPreprocessorDirectives();
! }
! return blanked_;
! }
!
! bool
! PreProcessor::isEol( char c )
! {
! return c == '\n' || c == '\r';
! }
!
! void PreProcessor::addSpaceReplacement(const int start, const int len)
! {
! blanked_.erase(start - offset_diff, len-1 );
! blanked_[start- offset_diff]=' ';
! offset_diff+=len-1;
! source_->registerReplacement(SourceRange(start,len),1);
! }
!
! void PreProcessor::addTextDeletion(const int start, const int len)
! {
! blanked_.erase(start - offset_diff, len );
! offset_diff+=len;
! source_->registerReplacement(SourceRange(start,len),0);
! }
!
! void PreProcessor::addTextReplacement(const int relstart, const int len, std::string text)
! {
! int start = relstart - offset_diff;
! blanked_.replace(start , len, text );
! offset_diff+=len - text.length();
! }
!
! const char *
! PreProcessor::deleteUntilEol( const char *current )
! {
! const char *start = current;
! while ( current != textEnd_ && !isEol( *current ) )
! ++current;
!
! int length = current - start;
! addTextDeletion(start - textStart_, length);
!
! return current;
! }
!
!
! const char *
! PreProcessor::deleteCComment( const char *current )
! {
! const char *start = current;
! while ( current != textEnd_ )
{
! char c = *current++;
! if( c == '*' && current[0] == '/' )
! {
! ++current;
! break;
! }
}
! int length = current - start;
! addTextDeletion(start - textStart_, length);
!
! //blanked_.replace( start - textStart_, length, length, ' ' );
! return current;
! }
!
! inline const char *
! PreProcessor::blanksNewLine( const char *current )
! {
! int index = current - textStart_;
!
! blanked_[index - offset_diff] = ' ';
! ++index;
! ++current;
! if ( *current == '\n' || *current == '\r' )
{
! blanked_[index - offset_diff] = ' ';
! ++current;
}
!
! return current;
! }
!
!
! const char *
! PreProcessor::blanksString( const char *current,
! char endOfStringChar )
! {
! const char *start = current;
! ++current;
! while ( current != textEnd_ )
{
! char c = *current++;
! if ( c == '\\' )
! {
! if ( current == textEnd_ )
! break;
! ++current;
! }
! else if ( c == endOfStringChar )
! break;
! }
!
! int length = current - start -2;
! if (length>0)
! addSpaceReplacement(start - textStart_+1, length);
! return current;
! }
!
!
! const char *
! PreProcessor::blanksPreprocessorDirective( const char *current )
! {
! const char *start = current;
! ++current;
! while ( current != textEnd_ )
! {
! char c = *current++;
! if ( c == '\\' )
! {
! while ( isEol( *current ) )
! ++current;
! }
! else if ( isEol( c ) )
! break;
! }
! return parseDirective( start, current );
! }
!
! const char *
! PreProcessor::parseDirective( const char *first,const char *last )
! {
! // check for '#define' keyword:
! if (strncmp(first,"#define ",8)==0)
! {
! return handleDefine(first,last);
!
! } else
! if (strncmp(first,"#include ",9)==0)
! {
! return handleInclude(first, last);
! } else
! if (strncmp(first,"#ifdef",6)==0)
! {
! if (!checkIfDef(first+6,last))
{
! // continue after 'else' or 'endif'
! const char * end = skipUntilElseEndIf(first );
! addSpaceReplacement( first - textStart_, end - first);
! return end;
! }
! // continue after 'last'
! int length = last - 1 - first;
! addSpaceReplacement( first - textStart_, length);
! return last;
! } else
! if (strncmp(first,"#ifndef",7)==0)
! {
! if (checkIfDef(first+8,last))
! {
! // continue after 'else' or 'endif'
! const char * end = skipUntilElseEndIf(first);
! addTextDeletion( first - textStart_, end - first);
! return end;
! }
! // remove 'directive'
! int length = last -1 - first;
! addTextDeletion( first - textStart_, length);
! // continue at the new line character after 'directive'
! return last - 1;
! } else
! if (strncmp(first,"#else", 5)==0)
! {
! // continue after 'else' or 'endif'
! const char * end = skipUntilElseEndIf(first+5 );
! addSpaceReplacement( first - textStart_, end - first);
! return end;
! } else
! {
! int length = last - 1 - first;
! addTextDeletion( first - textStart_, length);
! // continue at the new line character after 'directive'
! return last - 1;
! }
! }
! const char *
! PreProcessor::handleDefine(const char *first, const char *last )
! {
! Xtl::CStringEnumerator en( Xtl::CStringView( first, last ) );
! // skip define keyword and spaces
! en.setCurrent( first + 8 );
! ParserTools::skipSpaces(en);
!
! Xtl::CStringView identifier;
! ParserTools::tryReadIdentifier( en, identifier );
!
! // do not understand this...
! if (identifier.getLength() == 0)
! ThrowPreProcessError("Do not understand syntax of this '#define' directive.");
!
! ParserTools::skipSpaces(en);
! // macro text is all but not the newline character...
! context_.addMacro(identifier.str() , std::string( en.getCurrentPos(), last-1-en.getCurrentPos()));
!
! int length = last -1 - first;
! addSpaceReplacement( first - textStart_, length);
!
! // continue at the new line character after 'directive'
! return last-1;
! }
!
! const char *
! PreProcessor::handleInclude(const char *first, const char *last )
! {
! Xtl::CStringEnumerator en( Xtl::CStringView( first, last ) );
!
! // check preprocessor option 'SkipIncludes'
! if (isOptionSet(skipIncludes))
! {
! int length = last - 1 - first;
! addTextDeletion( first - textStart_, length);
! return last-1;
! }
!
! // skip define keyword and spaces
! en.setCurrent( first + 9 );
! ParserTools::skipSpaces(en);
!
! const char * current = en.getCurrentPos();
! if (*current != '\"' && *current !='<')
! ThrowPreProcessError("Do not understand syntax of this '#include' directive.");
!
! const char * filename_start = current+1;
! en++;
! if (*current=='\"')
! ParserTools::skipUntil(en,'\"');
! else
! ParserTools::skipUntil(en,'>');
!
! std::string filename(current+1,en.getCurrentPos()-current-2);
!
! SourcePtr pIncludefile = context_.getSourceManager().open(filename);
!
! PreProcessor include_processor(pIncludefile,context_);
! std::string include_txt = include_processor.process();
!
! int length = last - 1 - first;
! if (checkNoneWhiteSpace(include_txt))
! {
! addTextReplacement(first - textStart_, length, include_txt);
! } else
! {
! // TODO: remove added position translation from 'context_'
! addTextDeletion( first - textStart_, length);
! }
! source_->registerInclude(SourceRange(first - textStart_,length),include_txt.length(),pIncludefile);
!
! // continue at the new line character after 'directive'
! return last-1;
! }
!
! /**
! * checks an ifdef/ifndef term
! */
! bool
! PreProcessor::checkIfDef(const char * start, const char * end)
! {
! // TODO: real expression evaluation
! Xtl::CStringEnumerator en( Xtl::CStringView( start, end ) );
! Xtl::CStringView identifier;
! ParserTools::skipSpaces(en);
! ParserTools::tryReadIdentifier( en, identifier );
! if (identifier.getLength() != 0)
! {
! return context_.isMacroIdentifier(identifier.str());
! } else
! return false;
! }
!
! const char *
! PreProcessor::skipUntilElseEndIf(const char * start)
! {
! int cnt=0;
! const char * cur = start;
! do {
! const char * next = strstr(cur,"#ifdef");
! const char * end = strstr(cur,"#endif");
! const char * end2 = strstr(cur,"#else");
!
! // first check if '#else' comes before '#endif'
! if (end2 != 0 && end2 < end)
! end = end2;
!
! if (end == 0)
! {
! cur = textEnd_;
! return textEnd_;
! }
! if (next == 0 || next > end)
! {
! cnt--;
! cur = end+6;
! } else if (next != 0)
! {
! cnt++;
! cur = next+6;
} else
! cur++;
! }
! while (start != textEnd_ && cnt>0);
! // return final location
! return cur;
! }
! void
! PreProcessor::blanksStringsAndPreprocessorDirectives( )
! {
! char c(*current_);
! switch ( c )
! {
! case ' ':
! ++current_;
! break;
! case '\t':
! blanked_[ current_++ - textStart_ - offset_diff ] = ' ';
! break;
! case '\n':
! case '\r':
! current_ = blanksNewLine( current_ );
! newLine_ = true;
! break;
! case '"':
! current_ = blanksString( current_, '"' );
! break;
! case '\'':
! current_ = blanksString( current_, '\'' );
! break;
! case '#':
! if ( newLine_ )
! {
! current_ = blanksPreprocessorDirective( current_ );
! break;
! }
! newLine_ = true;
! ++current_;
! break;
! default:
! // identifier letters can lead to macro replacements:
! if (ParserTools::isValidIdentifierFirstLetter(*current_))
! {
! int identlen=1;
! while (current_+identlen != textEnd_ && ParserTools::isIdentifierLetter(*(current_+identlen)))
! identlen++;
! std::string ident(current_,identlen);
! if (ident == "_STD_END")
! identlen++;
! if (context_.isMacroIdentifier(ident))
! {
! // macro replace
! std::string replacement = context_.getMacroReplacement(ident);
! addTextReplacement(current_-textStart_,identlen,replacement);
! // register macro replacement
! SourceRange position(current_-textStart_,identlen);
! source_->registerReplacement(position,replacement.length());
! }
! current_+=identlen;
! } else
! {
! // skip any other non whitespace character and set the 'we-are-in-a-new-line' flag to false
! newLine_ = false;
! ++current_;
! }
! break;
! }
! // TODO: Remove this implementation check.
! if (*current_ != blanked_[current_-textStart_-offset_diff])
! int debug=0;
! }
! bool
! PreProcessor::checkNoneWhiteSpace(const std::string& text)
! {
! Xtl::CStringEnumerator en( Xtl::CStringView( text.c_str(), text.c_str()+text.length() ) );
! ParserTools::skipSpaces(en);
! return (en.getCurrentPos()!=text.c_str()+text....
[truncated message content] |