BasicSubStringFn.h
// BasicSubStringFn.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2005
#pragma once
#ifndef Ceda_cxUtils_BasicSubStringFn_H
#define Ceda_cxUtils_BasicSubStringFn_H
#include "xostream.h"
#include "BasicSubString.h"
#include "StringStream.h"
#include "Hex.h"
#include <string.h>
namespace ceda
{
// Compare two substrings lexicographically. Similar in functionality to strcmp(const char*, const char*)
template <typename T>
int _strcmp(BasicSubString<T> s1, BasicSubString<T> s2)
{
while(s1 && s2)
{
int n = *s1 - *s2;
if (n < 0) return -1;
if (n > 0) return +1;
++s1;
++s2;
}
if (s1) { cxAssert(!s2); return +1; }
if (s2) { cxAssert(!s1); return -1; }
// Strings are equal
return 0;
}
template <typename T>
int _strcmp(BasicSubString<T> s1, const T* s2)
{
while(s1 && *s2)
{
int n = *s1 - *s2;
if (n < 0) return -1;
if (n > 0) return +1;
++s1;
++s2;
}
if (s1) { cxAssert(!*s2); return +1; }
if (*s2) { cxAssert(!s1); return -1; }
// Strings are equal
return 0;
}
template <typename T>
void _ScanIndentation(BasicSubString<T>& s, ssize_t indent, ssize_t tabSize)
{
ssize_t pos = 0;
while(s && pos < indent)
{
if (*s == ' ') ++pos;
else if (*s == '\t') pos += tabSize;
else break;
++s;
}
}
template <typename T>
void _WriteSubStringWithAdjustedIndent(xostream& os, BasicSubString<T> s, ssize_t srcIndent)
{
while(s)
{
T c = *s++;
os << c;
if (c == '\n')
{
ScanIndentation(s, srcIndent);
}
}
}
// Returns pointer to start of given line (zero based), or nullptr if s doesn't contain enough lines
template <typename T>
const T* _GetLineNumberPosition(BasicSubString<T> s, ssize_t lineNumber)
{
cxAssert(lineNumber >= 0);
for (ssize_t i=0 ; i < lineNumber ; ++i)
{
// Scan a line
for(;;)
{
if (!s) return nullptr;
if (*s == '\n') break;
++s;
}
++s;
}
return s.ptr();
}
// Returns true if s begins with the given prefix. The comparison is case sensitive. Always returns true
// if the prefix is empty.
template <typename T>
bool _CheckPrefix(BasicSubString<T> s, BasicSubString<T> prefix)
{
return prefix.size() <= s.size() && memcmp(s.begin(), prefix.begin(), prefix.size() * sizeof(T)) == 0;
}
// Returns true if s ends with the given suffix. The comparison is case sensitive. Always returns true
// if the suffix is empty.
template <typename T>
bool _CheckSuffix(BasicSubString<T> s, BasicSubString<T> suffix)
{
return suffix.size() <= s.size() && memcmp(s.end() - suffix.size(), suffix.begin(), suffix.size() * sizeof(T)) == 0;
}
// Find the last position of the character c within SubString s. Returns nullptr if not found
template <typename T>
const T* _ReverseFind(BasicSubString<T> s, T c)
{
const T* p = s.end();
while(p != s.begin())
{
--p;
if (*p == c) return p;
}
return nullptr;
}
// Find the last position of the substring sFind within SubString s. Returns nullptr if not found
template <typename T>
const T* _ReverseFind(BasicSubString<T> s, BasicSubString<T> sFind)
{
SubString p;
p.setbegin(s.end());
p.setend(s.end());
while(p.begin() != s.begin())
{
--p;
if (_CheckPrefix(p,sFind))
{
return p.ptr();
}
}
return nullptr;
}
// If s starts with the given prefix then advance s past the prefix and return true
template <typename T>
bool _EatPrefix(BasicSubString<T>& s, BasicSubString<T> prefix)
{
if (_CheckPrefix(s,prefix))
{
s += prefix.size();
return true;
}
return false;
}
// Case Insensitive version of _CheckPrefix()
template <typename T>
bool _CheckCaseInsensitivePrefix(BasicSubString<T> s, BasicSubString<T> prefix)
{
if (prefix.size() <= s.size())
{
while(prefix)
{
if ( ToLower(*prefix) != ToLower(*s) ) return false;
++prefix;
++s;
}
return true;
}
return false;
}
template <typename T>
bool _CaseInsensitiveCompare(BasicSubString<T> s1, BasicSubString<T> s2)
{
if (s1.size() != s2.size()) return false;
while(s1)
{
cxAssert(s2);
if ( ToLower(*s1) != ToLower(*s2) ) return false;
++s1;
++s2;
}
return true;
}
template <typename T>
bool _RemoveQuotesFromString(BasicSubString<T>& s)
{
if (s.size() >= 2)
{
if (*s != '\"') return false;
++s;
const T* last = s.end()-1;
if (*last != '\"') return false;
s.setend(last);
return true;
}
return false;
}
// If s starts with the given prefix then advance s past the prefix and return true
template <typename T>
bool _EatCaseInsensitivePrefix(BasicSubString<T>& s, BasicSubString<T> prefix)
{
if (_CheckCaseInsensitivePrefix(s,prefix))
{
s += prefix.size();
return true;
}
return false;
}
// Find position of the first occurence of sFind within s. Returns nullptr if not found
template <typename T>
const T* _ForwardsFind(BasicSubString<T> s, BasicSubString<T> sFind)
{
while(s)
{
if (_CheckPrefix(s,sFind))
{
return s.ptr();
}
++s;
}
// Not found
return nullptr;
}
// Find position of the first occurence of character c within s. Returns nullptr if not found
template <typename T>
const T* _ForwardsFind(BasicSubString<T> s, T c)
{
while(s)
{
if (*s == c)
{
return s.ptr();
}
++s;
}
// Not found
return nullptr;
}
// Find last occurence of delimiter character c in s and return prefix of s up to but not
// including the position of c. If c not found then returns an empty string
template <typename T>
BasicSubString<T> _GetPrefixBeforeLastDelimiter(BasicSubString<T> s, T c)
{
const T* p = _ReverseFind(s,c);
if (!p) p = s.begin();
return BasicSubString<T>(s.begin(), p);
}
// Find last occurence of delimiter character c in s and return suffix from s starting just
// after the position of c. If c not found then returns an empty string.
template <typename T>
BasicSubString<T> _GetSuffixAfterLastDelimiter(BasicSubString<T> s, T c)
{
const T* p = _ReverseFind(s,c);
if (p) ++p; else p = s.end();
return BasicSubString<T>(p,s.end());
}
/*
// Find last occurence of delimiter character c in s and return suffix from s starting just
// after the position of c. If c not found then returns entire string.
template <typename T>
BasicSubString<T> _GetSuffixAfterLastDelimiter(BasicSubString<T> s, T c)
{
const T* p = _ReverseFind(s,c);
if (p) ++p; else p = s.begin();
return BasicSubString<T>(p,s.end());
}
*/
template <typename T>
bool _MapEscapeCharacter(T c1, T& c2)
{
if (c1 == 'r') c2 = '\r'; // Carriage return
else if (c1 == 'n') c2 = '\n'; // Linefeed
else if (c1 == 'a') c2 = '\a'; // Alert
else if (c1 == 'b') c2 = '\b'; // Backspace
else if (c1 == 'f') c2 = '\f'; // Formfeed
else if (c1 == 't') c2 = '\t'; // Horizontal tab
else if (c1 == 'v') c2 = '\v'; // Vertical tab
else if (c1 == '0') c2 = '\0';
else if (c1 == '\"') c2 = c1;
else if (c1 == '\\') c2 = c1;
else if (c1 == '\'') c2 = c1;
else if (c1 == '?') c2 = c1;
else
{
return false;
}
return true;
}
template <typename T>
bool _UnmapEscapeCharacter(T c1, T& c2)
{
if (c1 == '\r') c2 = 'r'; // Carriage return
else if (c1 == '\n') c2 = 'n'; // Linefeed
else if (c1 == '\a') c2 = 'a'; // Alert
else if (c1 == '\b') c2 = 'b'; // Backspace
else if (c1 == '\f') c2 = 'f'; // Formfeed
else if (c1 == '\t') c2 = 't'; // Horizontal tab
else if (c1 == '\v') c2 = 'v'; // Vertical tab
else if (c1 == '\0') c2 = '0';
else if (c1 == '\"') c2 = c1;
else if (c1 == '\\') c2 = c1;
else if (c1 == '\'') c2 = c1;
else if (c1 == '?') c2 = c1;
else
{
return false;
}
return true;
}
template <typename T>
void _WriteCharForCStyleLiteralConstants(xostream& os, T c)
{
T c2;
if (_UnmapEscapeCharacter(c, c2))
{
os << '\\' << c2;
}
else if (IsPrint(c))
{
os << c;
}
else
{
// todo: what about unicode?
os << "\\x" << HexNumber<T>(c);
}
}
template <typename T>
void _MakeSingleQuotedLiteralChar(T c,basic_xstring<T>& t)
{
StringStream os;
os << '\'';
WriteCharForCStyleLiteralConstants(os,c);
os << '\'';
os.GetString(t);
}
template <typename T>
void _WriteDoubleQuotedString(xostream& os, BasicSubString<T> s)
{
os << "\"";
while(s)
{
WriteCharForCStyleLiteralConstants(os,*s);
++s;
}
os << "\"";
}
template <typename T>
void _MakeDoubleQuotedString(BasicSubString<T> s, basic_xstring<T>& t)
{
StringStream os;
_WriteDoubleQuotedString(os, s);
os.GetString(t);
}
template <typename T>
ssize_t _CountNumChars(BasicSubString<T> s, T c)
{
ssize_t n = 0;
while(s)
{
if (*s == c) ++n;
++s;
}
return n;
}
// Get the line number corresponding to the given position, or -1 if [s.p1, s.p2] doesn't contain
// pos
template <typename T>
ssize_t _GetLineNumber(BasicSubString<T> s, const T* pos)
{
if (s.begin() <= pos && pos <= s.end())
{
ssize_t lineNumber = 1;
while(s.begin() < pos)
{
if (*s == '\n') ++lineNumber;
++s;
}
return lineNumber;
}
return -1;
}
// Count the number of lines of text by scanning the substring for line feeds. The last line
// doesn't need to end with a line feed
template <typename T>
ssize_t _CountNumLinesOfText(BasicSubString<T> s)
{
ssize_t numLines = 0;
const T* sline = s.begin();
while(s)
{
if (*s == '\n')
{
sline = s.begin() + 1;
++numLines;
}
++s;
}
// If last line didn't end with a line feed then increment numLines
if (sline != s.end()) ++numLines;
return numLines;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Functions to scan tokens from a SubString
template <typename T>
void _ScanWhiteSpace(BasicSubString<T>& s)
{
while(s && IsWhiteSpace(*s))
{
++s;
}
}
template <typename T>
bool _ScanDigits(BasicSubString<T>& s)
{
const T* start = s.begin();
while(s && IsDigit(*s))
{
++s;
}
return s.begin() != start;
}
template <typename T>
bool _ScanIdentifier(BasicSubString<T>& s)
{
cxAssert(s);
if (IsAlpha(*s) || *s == '_')
{
++s;
while(s && (IsAlnum(*s) || *s == '_'))
{
++s;
}
return true;
}
return false;
}
} // namespace ceda
#endif // include guard