Adjust documentation path.
// lexer.cpp
//
// Copyright (c) 2006 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Accenture - Initial contribution
//
#include "lexer.h"
const TInt KMaxReadLength = 512;
//
// TToken
//
TToken::TToken()
: iToken(NULL, 0), iPos(0)
{
}
TToken::TToken(TType aType, const TDesC& aToken, TInt aPos)
: iType(aType), iToken(aToken), iPos(aPos)
{
}
TToken& TToken::operator=(const TToken& aToken)
{
iType = aToken.iType;
iToken.Set(aToken.iToken);
iPos = aToken.iPos;
return *this;
}
TToken::TType TToken::Type() const
{
return iType;
}
TInt TToken::Position() const
{
return iPos;
}
const TDesC& TToken::String() const
{
return iToken;
}
//
// CLex - A cut down version of the TLex API that supports reading data incrementally from an RIoReadHandle.
//
class CLex : public CBase
{
public:
static CLex* NewL();
~CLex();
void Set(const TDesC& aDes);
void Set(RIoReadHandle& aHandle);
void Purge();
void SkipToEnd();
TBool EosL();
void Mark();
void IncL(TInt aNumber);
TChar GetL();
TChar Peek() const;
TPtrC RemainderL(TInt aMinLength);
void UnGet();
TInt Offset() const;
TInt MarkedOffset() const;
TPtrC MarkedToken() const;
const TUint16* Ptr() const;
private:
CLex();
void DoReadL();
private:
TLex iLex;
TInt iMarkedOffset;
TPtrC iLexPtr;
RBuf iLexBuf;
TBuf<KMaxReadLength> iReadBuf;
RIoReadHandle iHandle;
TBool iUsingHandle;
TBool iEos;
};
CLex* CLex::NewL()
{
return new(ELeave) CLex();
}
CLex::CLex() : iLexPtr(NULL, 0)
{
}
CLex::~CLex()
{
iLexBuf.Close();
}
void CLex::Set(const TDesC& aDes)
{
iEos = EFalse;
iUsingHandle = EFalse;
iMarkedOffset = 0;
iLexPtr.Set(aDes);
iLex = iLexPtr;
}
void CLex::Set(RIoReadHandle& aHandle)
{
iEos = EFalse;
iUsingHandle = ETrue;
iMarkedOffset = 0;
iHandle = aHandle;
iHandle.SetReadMode(RIoReadHandle::EOneOrMore);
iLexBuf.Zero();
iLex = iLexBuf;
}
void CLex::Purge()
{
if (iUsingHandle)
{
iLexBuf.Delete(0, iLex.Offset());
iLex = iLexBuf;
}
iMarkedOffset = 0;
}
void CLex::SkipToEnd()
{
iEos = ETrue;
}
TBool CLex::EosL()
{
if (!iEos)
{
if (!iLex.Eos())
{
// If iLex still has data, then we're definately not at the end of the string.
// Do nothing. This test avoids us doing I/O handle reads before draining using the existing data.
}
else if (iUsingHandle)
{
DoReadL();
}
else
{
iEos = ETrue;
}
}
return iEos;
}
void CLex::Mark()
{
iMarkedOffset = iLex.Offset();
}
void CLex::IncL(TInt aNumber)
{
if (iUsingHandle)
{
while (!iEos && (iLex.Remainder().Length() < aNumber))
{
DoReadL();
}
}
iLex.Inc(aNumber);
}
TChar CLex::GetL()
{
if (iUsingHandle && !iEos && (iLex.Remainder().Length() < 1))
{
DoReadL();
}
return iLex.Get();
}
TChar CLex::Peek() const
{
return iLex.Peek();
}
TPtrC CLex::RemainderL(TInt aMinLength)
{
if (iUsingHandle)
{
while (!iEos && (iLex.Remainder().Length() < aMinLength))
{
DoReadL();
}
}
return iLex.Remainder();
}
void CLex::UnGet()
{
iLex.UnGet();
}
TInt CLex::Offset() const
{
return iLex.Offset();
}
TInt CLex::MarkedOffset() const
{
return iMarkedOffset;
}
TPtrC CLex::MarkedToken() const
{
if (iUsingHandle)
{
return TPtrC(iReadBuf.Ptr() + iMarkedOffset, iLex.Offset() - iMarkedOffset);
}
else
{
return TPtrC(iLexPtr.Ptr() + iMarkedOffset, iLex.Offset() - iMarkedOffset);
}
}
const TUint16* CLex::Ptr() const
{
if (iUsingHandle)
{
return iLexBuf.Ptr();
}
else
{
return iLexPtr.Ptr();
}
}
void CLex::DoReadL()
{
ASSERT(iUsingHandle);
if (!iEos)
{
if (iReadBuf.Length() == 0) // iReadBuf may contain data if a realloc of iLexBuf failed previously.
{
TInt err = iHandle.Read(iReadBuf);
if (err == KErrEof)
{
iEos = ETrue;
}
else
{
User::LeaveIfError(err);
}
}
TInt offset = iLex.Offset();
if ((iLexBuf.MaxLength() - iLexBuf.Length()) < iReadBuf.Length())
{
iLexBuf.ReAllocL(iLexBuf.Length() + iReadBuf.Length());
}
iLexBuf.Append(iReadBuf);
iReadBuf.Zero();
iLex = iLexBuf;
iLex.Inc(offset);
}
}
//
// CReservedLookup
//
class CReservedLookup : public CBase
{
public:
class TResult
{
public:
enum TType
{
ENoMatch,
EMatch
};
public:
TResult();
public:
TType iResultType;
TToken::TType iTokenType;
TInt iTokenLength;
};
enum TCharPos
{
ENotLast,
ELast
};
public:
static CReservedLookup* NewL();
~CReservedLookup();
void DefineTokenTypeL(TToken::TType aTokenType, const TDesC& aString);
void Reset();
TResult Lookup(const TDesC& aDes);
TInt Longest() const;
private:
class TReserved
{
public:
TReserved(TToken::TType aType, const TDesC& aString);
public:
TToken::TType iType;
TPtrC iString;
};
private:
RArray<TReserved> iList;
TInt iLongest;
};
CReservedLookup* CReservedLookup::NewL()
{
return new(ELeave) CReservedLookup();
}
CReservedLookup::~CReservedLookup()
{
iList.Close();
}
void CReservedLookup::DefineTokenTypeL(TToken::TType aTokenType, const TDesC& aString)
{
User::LeaveIfError(iList.Append(TReserved(aTokenType, aString)));
if (aString.Length() > iLongest)
{
iLongest = aString.Length();
}
}
CReservedLookup::TResult CReservedLookup::Lookup(const TDesC& aDes)
{
// Find the longest reserved word that matches from the beginning of this string.
TResult result;
const TInt count = iList.Count();
for (TInt i = 0; i < count; ++i)
{
const TReserved& reserved = iList[i];
if (aDes.Left(reserved.iString.Length()) == reserved.iString)
{
if (result.iTokenLength < reserved.iString.Length())
{
result.iTokenLength = reserved.iString.Length();
result.iResultType = TResult::EMatch;
result.iTokenType = reserved.iType;
}
}
}
return result;
}
TInt CReservedLookup::Longest() const
{
return iLongest;
}
CReservedLookup::TReserved::TReserved(TToken::TType aType, const TDesC& aString)
: iType(aType), iString(aString)
{
}
CReservedLookup::TResult::TResult()
: iResultType(ENoMatch), iTokenType(TToken::EString), iTokenLength(0)
{
}
//
// CLexer
//
CLexer* CLexer::NewL()
{
CLexer* self = CLexer::NewLC();
CleanupStack::Pop(self);
return self;
}
CLexer* CLexer::NewL(TUint aBehaviour)
{
CLexer* self = CLexer::NewLC(aBehaviour);
CleanupStack::Pop(self);
return self;
}
CLexer* CLexer::NewLC()
{
CLexer* self = new(ELeave) CLexer(EHandleSingleQuotes | EHandleDoubleQuotes | EHandleComments);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CLexer* CLexer::NewLC(TUint aBehaviour)
{
CLexer* self = new(ELeave) CLexer(aBehaviour);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CLexer::~CLexer()
{
delete iLex;
delete iReservedLookup;
}
void CLexer::DefineTokenTypeL(TToken::TType aTokenType, const TDesC& aString)
{
iReservedLookup->DefineTokenTypeL(aTokenType, aString);
}
void CLexer::Set(const TDesC& aDes, const TChar& aEscapeChar)
{
iLex->Set(aDes);
iEscapeChar = aEscapeChar;
}
void CLexer::Set(RIoReadHandle& aHandle, const TChar& aEscapeChar)
{
iLex->Set(aHandle);
iEscapeChar = aEscapeChar;
}
void CLexer::Purge()
{
iLex->Purge();
}
void CLexer::SkipToEnd()
{
iLex->SkipToEnd();
}
TToken CLexer::NextTokenL()
{
SkipWhiteSpaceL();
iLex->Mark();
TToken::TType type(TToken::ENull);
while (!iLex->EosL())
{
type = TToken::EString;
TChar c = iLex->GetL();
if (c == iEscapeChar)
{
iLex->GetL();
}
else if ((c == '\'') && (iBehaviour & EHandleSingleQuotes))
{
SkipSingleQuotedCharsL();
}
else if ((c == '\"') && (iBehaviour & EHandleDoubleQuotes))
{
SkipDoubleQuotedCharsL();
}
else if ((c == '#') && (iBehaviour & EHandleComments))
{
if (iLex->MarkedToken().Length() > 1)
{
iLex->UnGet();
break;
}
else
{
SkipCommentL();
if (iLex->EosL())
{
type = TToken::ENull;
break;
}
else
{
iLex->Mark();
}
}
}
else if (c.IsSpace() && (c != '\n') && (c != '\r'))
{
iLex->UnGet();
break;
}
else
{
iLex->UnGet();
CReservedLookup::TResult result = iReservedLookup->Lookup(iLex->RemainderL(iReservedLookup->Longest()));
if (result.iResultType == CReservedLookup::TResult::EMatch)
{
if (iLex->MarkedToken().Length() > 0)
{
break;
}
else
{
iLex->IncL(result.iTokenLength);
type = result.iTokenType;
break;
}
}
iLex->GetL();
}
}
return TToken(type, iLex->MarkedToken(), iLex->MarkedOffset());
}
TInt CLexer::CurrentOffset() const
{
return iLex->Offset();
}
TBool CLexer::MoreL()
{
SkipWhiteSpaceL();
return !iLex->EosL();
}
const TUint16* CLexer::Ptr() const
{
return iLex->Ptr();
}
CLexer::CLexer(TUint aBehaviour)
: iBehaviour(aBehaviour)
{
}
void CLexer::ConstructL()
{
iLex = CLex::NewL();
iReservedLookup = CReservedLookup::NewL();
}
void CLexer::SkipSingleQuotedCharsL()
{
while (!iLex->EosL())
{
TChar c = iLex->GetL();
if ((c == iEscapeChar) && !iLex->EosL() && (iLex->Peek() == '\''))
{
// Allow quoted single quote characters. Note, the is a departure from Bash behaviour, but is in line with Perl and is generally helpful.
iLex->GetL();
}
else if (c == '\'')
{
break;
}
}
}
void CLexer::SkipDoubleQuotedCharsL()
{
while (!iLex->EosL())
{
TChar c = iLex->GetL();
if ((c == iEscapeChar) && !iLex->EosL())
{
iLex->GetL();
}
else if (c == '"')
{
break;
}
}
}
void CLexer::SkipCommentL()
{
while (!iLex->EosL())
{
TChar c = iLex->GetL();
if ((c == '\n') || (c == '\r'))
{
iLex->UnGet();
break;
}
}
}
void CLexer::SkipWhiteSpaceL()
{
while (!iLex->EosL())
{
TChar c = iLex->GetL();
if (!c.IsSpace() || (c == '\n') || (c == '\r'))
{
iLex->UnGet();
break;
}
}
}