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