1609 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1609 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <cmath>
 | 
						|
#include <string>
 | 
						|
#include <sstream>
 | 
						|
#include <iomanip>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include "expression.h"
 | 
						|
 | 
						|
#define UNREFERENCED_PARAM(P) ((void)P)
 | 
						|
 | 
						|
 | 
						|
namespace Expression
 | 
						|
{
 | 
						|
    std::string _expressionToParse;
 | 
						|
    char* _expression;
 | 
						|
 | 
						|
    int _lineNumber = 0;
 | 
						|
 | 
						|
    bool _advanceError = false;
 | 
						|
    bool _enableOptimisedPrint = false;
 | 
						|
 | 
						|
    bool _binaryChars[256]      = {false};
 | 
						|
    bool _octalChars[256]       = {false};
 | 
						|
    bool _decimalChars[256]     = {false};
 | 
						|
    bool _hexaDecimalChars[256] = {false};
 | 
						|
 | 
						|
    uintptr_t _advancePtr;
 | 
						|
    exprFuncPtr _exprFunc;
 | 
						|
 | 
						|
    Numeric _output;
 | 
						|
 | 
						|
 | 
						|
    // Forward declarations
 | 
						|
    Numeric expression(bool);
 | 
						|
 | 
						|
    // Helpers
 | 
						|
    uint32_t revHelper(uint32_t input, uint32_t n)
 | 
						|
    {
 | 
						|
        uint32_t output = 0;
 | 
						|
        uint32_t bits = input & uint32_t(pow(2, n) - 1);
 | 
						|
        for(uint32_t i=0; i<=n-1; i++)
 | 
						|
        {
 | 
						|
            output = (output << 1) | (bits & 1);
 | 
						|
            bits = bits >> 1;
 | 
						|
        }
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    // Unary logic operators
 | 
						|
    Numeric& operatorNEG(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = -numeric._value;
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorNOT(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = ~int16_t(std::lround(numeric._value));
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
 | 
						|
    // Unary math operators
 | 
						|
    Numeric& operatorCEIL(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = ceil(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorFLOOR(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = floor(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorPOWF(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._params.size() > 0) numeric._value = pow(numeric._value, numeric._params[0]._value);
 | 
						|
        numeric._params.clear();
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorSQRT(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._value > 0.0) numeric._value = sqrt(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorEXP(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = exp(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorEXP2(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = exp2(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorLOG(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._value > 0.0) numeric._value = log(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorLOG2(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._value > 0.0) numeric._value = log2(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorLOG10(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._value > 0.0) numeric._value = log10(numeric._value);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorSIN(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = sin(numeric._value*MATH_PI/180.0);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorCOS(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = cos(numeric._value*MATH_PI/180.0);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorTAN(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = tan(numeric._value*MATH_PI/180.0);
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorASIN(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = asin(numeric._value)/MATH_PI*180.0;
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorACOS(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = acos(numeric._value)/MATH_PI*180.0;
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorATAN(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = atan(numeric._value)/MATH_PI*180.0;
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorATAN2(Numeric& numeric)
 | 
						|
    {
 | 
						|
        if(numeric._params.size() > 0  &&  (numeric._value != 0.0  ||  numeric._params[0]._value != 0.0)) numeric._value = atan2(numeric._value, numeric._params[0]._value)/MATH_PI*180.0;
 | 
						|
        numeric._params.clear();
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorRAND(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = double(rand() % std::lround(numeric._value));
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorREV16(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = double(revHelper(uint32_t(std::lround(numeric._value)), 16));
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorREV8(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = double(revHelper(uint32_t(std::lround(numeric._value)), 8));
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
    Numeric& operatorREV4(Numeric& numeric)
 | 
						|
    {
 | 
						|
        numeric._value = double(revHelper(uint32_t(std::lround(numeric._value)), 4));
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
 | 
						|
    // Binary logic operators
 | 
						|
    Numeric& operatorAND(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) & int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorOR(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) | int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorXOR(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) ^ int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorLSL(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = (int16_t(std::lround(left._value)) << int16_t(std::lround(right._value))) & 0x0000FFFF;
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorLSR(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) >> int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
 | 
						|
    // Binary math operators
 | 
						|
    Numeric& operatorADD(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value += right._value;
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorSUB(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value -= right._value;
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorMUL(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value *= right._value;
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorDIV(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = (right._value == 0) ? 0 : left._value / right._value;
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorMOD(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = (right._value == 0) ? 0 : int16_t(std::lround(left._value)) % int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorPOW(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = pow(left._value, right._value);
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
 | 
						|
    // Relational operators
 | 
						|
    Numeric& operatorLT(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) < int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorGT(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) > int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorEQ(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) == int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorNE(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) != int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorLE(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) <= int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
    Numeric& operatorGE(Numeric& left, Numeric& right)
 | 
						|
    {
 | 
						|
        left._value = int16_t(std::lround(left._value)) >= int16_t(std::lround(right._value));
 | 
						|
        return left;
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric& getOutputNumeric(void) {return _output;}
 | 
						|
    bool getEnableOptimisedPrint(void) {return _enableOptimisedPrint;}
 | 
						|
 | 
						|
    void setExprFunc(exprFuncPtr exprFunc) {_exprFunc = exprFunc;}
 | 
						|
    void setEnableOptimisedPrint(bool enableOptimisedPrint) {_enableOptimisedPrint = enableOptimisedPrint;}
 | 
						|
 | 
						|
 | 
						|
    void initialise(void)
 | 
						|
    {
 | 
						|
        bool* b = _binaryChars;      b['0']=1; b['1']=1;
 | 
						|
        bool* o = _octalChars;       o['0']=1; o['1']=1; o['2']=1; o['3']=1; o['4']=1; o['5']=1; o['6']=1; o['7']=1;
 | 
						|
        bool* d = _decimalChars;     d['0']=1; d['1']=1; d['2']=1; d['3']=1; d['4']=1; d['5']=1; d['6']=1; d['7']=1; d['8']=1; d['9']=1; d['-']=1;
 | 
						|
        bool* h = _hexaDecimalChars; h['0']=1; h['1']=1; h['2']=1; h['3']=1; h['4']=1; h['5']=1; h['6']=1; h['7']=1; h['8']=1; h['9']=1; h['A']=1; h['B']=1; h['C']=1; h['D']=1; h['E']=1; h['F']=1;
 | 
						|
 | 
						|
        setExprFunc(expression);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    // Strings
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    int isSpace(int chr)
 | 
						|
    {
 | 
						|
        return isspace((unsigned char)chr);
 | 
						|
    }
 | 
						|
 | 
						|
    bool isNumber(const std::string& input)
 | 
						|
    {
 | 
						|
        return !input.empty()  &&  std::find_if(input.begin(), input.end(), [](unsigned char c) { return !isdigit(c); }) == input.end();
 | 
						|
    }
 | 
						|
 | 
						|
    ExpressionType isExpression(const std::string& input)
 | 
						|
    {
 | 
						|
        if(input.find_first_of("[]") != std::string::npos) return IsInvalid;
 | 
						|
        if(input.find("++") != std::string::npos) return IsInvalid;
 | 
						|
        if(input.find("--") != std::string::npos) return IsInvalid;
 | 
						|
        if(input.find_first_of("~-+/%*()&|^<>") != std::string::npos) return HasOperators;
 | 
						|
        if(input.find("**") != std::string::npos  ||  input.find(">>") != std::string::npos  ||  input.find("<<") != std::string::npos) return HasOperators;
 | 
						|
        if(input.find("==") != std::string::npos  ||  input.find("!=") != std::string::npos  ||  input.find("<=") != std::string::npos  ||  input.find(">=") != std::string::npos) return HasOperators;
 | 
						|
        return HasNumbers;
 | 
						|
    }
 | 
						|
 | 
						|
    // First char must be alpha, all chars except first can be numerics, all chars except first and last can be underscores, can contain valid brackets as an array reference
 | 
						|
    TokType isVarNameValid(const std::string& varName)
 | 
						|
    {
 | 
						|
        TokType tokType = Invalid;
 | 
						|
 | 
						|
        if(varName.size() == 0) return Invalid;
 | 
						|
        if(!isalpha((unsigned char)varName[0])) return Invalid;
 | 
						|
 | 
						|
        size_t lbra, rbra;
 | 
						|
        if(!findMatchingBrackets(varName, 0, lbra, rbra))
 | 
						|
        {
 | 
						|
            tokType = Variable;
 | 
						|
            lbra = varName.size();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            tokType = Array;
 | 
						|
        }
 | 
						|
 | 
						|
        if(!isalnum((unsigned char)varName[lbra - 1])) return Invalid;
 | 
						|
 | 
						|
        for(int i=1; i<int(lbra-1); i++)
 | 
						|
        {
 | 
						|
            if(!isalnum((unsigned char)varName[i])  &&  varName[i] != '_') return Invalid;
 | 
						|
        }
 | 
						|
 | 
						|
        return tokType;
 | 
						|
    }
 | 
						|
 | 
						|
    // First char must be alpha, all chars except first can be numerics, all chars except first and last can be underscores, last char can be a dollar sign, can contain valid brackets as an array reference
 | 
						|
    TokType isStrNameValid(const std::string& strName)
 | 
						|
    {
 | 
						|
        TokType tokType = Invalid;
 | 
						|
 | 
						|
        if(strName.size() == 0) return Invalid;
 | 
						|
        if(!isalpha((unsigned char)strName[0])) return Invalid;
 | 
						|
 | 
						|
        size_t lbra, rbra;
 | 
						|
        if(!findMatchingBrackets(strName, 0, lbra, rbra))
 | 
						|
        {
 | 
						|
            tokType = Variable;
 | 
						|
            lbra = strName.size();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            tokType = Array;
 | 
						|
        }
 | 
						|
 | 
						|
        if(strName[lbra - 1] != '$') return Invalid;
 | 
						|
 | 
						|
        for(int i=1; i<int(lbra-1); i++)
 | 
						|
        {
 | 
						|
            if(!isalnum((unsigned char)strName[i])  &&  strName[i] != '_') return Invalid;
 | 
						|
        }
 | 
						|
 | 
						|
        return tokType;
 | 
						|
    }
 | 
						|
 | 
						|
    // First char must be alpha, all chars except first can be numerics, all chars except first and last can be underscores
 | 
						|
    bool isLabNameValid(const std::string& labName)
 | 
						|
    {
 | 
						|
        if(labName.size() == 0) return false;
 | 
						|
        if(!isalpha((unsigned char)labName[0])) return false;
 | 
						|
        if(!isalnum((unsigned char)labName[labName.size() - 1])) return false;
 | 
						|
 | 
						|
        for(int i=1; i<int(labName.size())-1; i++)
 | 
						|
        {
 | 
						|
            if(!isalnum((unsigned char)labName[i])  &&  labName[i] != '_') return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool isStringValid(const std::string& input)
 | 
						|
    {
 | 
						|
        std::string str = input;
 | 
						|
        stripNonStringWhitespace(str);
 | 
						|
        if(str.size() < 2) return false;
 | 
						|
        if(str[0] == '"'  &&  str.back() == '"')
 | 
						|
        {
 | 
						|
            for(int i=1; i<int(str.size())-1; i++)
 | 
						|
            {
 | 
						|
                if(str[i] == '"'  &&  str[i-1] != '\\') return false;
 | 
						|
            }
 | 
						|
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    int _chrBra = -1;
 | 
						|
    int _chrPrev = -1;
 | 
						|
    bool _containsQuotes = false;
 | 
						|
    void initHasHelpers(int bra=-1)
 | 
						|
    {
 | 
						|
        _chrBra = bra;
 | 
						|
        _chrPrev = -1;
 | 
						|
        _containsQuotes = false;
 | 
						|
    }
 | 
						|
    bool hasNonStringWhiteSpace(int chr)
 | 
						|
    {
 | 
						|
        bool result = true;
 | 
						|
 | 
						|
        if(chr == '"'  &&  _chrPrev != '\\') _containsQuotes = !_containsQuotes;
 | 
						|
        if(!isspace((unsigned char)chr) || _containsQuotes) result = false;
 | 
						|
 | 
						|
        _chrPrev = chr;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    bool hasNonStringEquals(int chr)
 | 
						|
    {
 | 
						|
        bool result = true;
 | 
						|
 | 
						|
        if(chr == '"'  &&  _chrPrev != '\\') _containsQuotes = !_containsQuotes;
 | 
						|
        if(chr != '='  ||  _containsQuotes) result = false;
 | 
						|
 | 
						|
        _chrPrev = chr;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    bool hasNonStringColon(int chr)
 | 
						|
    {
 | 
						|
        bool result = true;
 | 
						|
 | 
						|
        if(chr == '"'  &&  _chrPrev != '\\') _containsQuotes = !_containsQuotes;
 | 
						|
        if(chr != ':'  ||  _containsQuotes) result = false;
 | 
						|
 | 
						|
        _chrPrev = chr;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    bool hasNonStringBracket(int chr)
 | 
						|
    {
 | 
						|
        bool result = true;
 | 
						|
 | 
						|
        if(chr == '"'  &&  _chrPrev != '\\') _containsQuotes = !_containsQuotes;
 | 
						|
        if(chr != _chrBra  ||  _containsQuotes) result = false;
 | 
						|
 | 
						|
        _chrPrev = chr;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    bool hasOnlyWhiteSpace(const std::string& input)
 | 
						|
    {
 | 
						|
        if(std::find_if_not(input.begin(), input.end(), isspace) != input.end()) return false;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void isInComment(const std::string& input, int index, bool& output)
 | 
						|
    {
 | 
						|
        // Once a comment is found this function must instantly return when called again, otherwise the logic breaks
 | 
						|
        if(output) return;
 | 
						|
 | 
						|
        if(index >= int(input.size())) return;
 | 
						|
    
 | 
						|
        // In comment if trailing a single quote or if NOT a forward char literal or if trailing a 'REM' sequence
 | 
						|
        if((input[index] == '\''  &&  index > int(input.size())-3)  ||  (index <= int(input.size())-3  &&  input[index] == '\''  &&  input[index+2] != '\'')  ||
 | 
						|
           (index <= int(input.size()) - 3  &&  toupper((unsigned char)input[index]) == 'R'  &&  toupper((unsigned char)input[index+1]) == 'E'  &&  toupper((unsigned char)input[index+2]) == 'M' && (unsigned char)input[index + 3] == ' '))
 | 
						|
        {
 | 
						|
            output = true;
 | 
						|
 | 
						|
            // Not in comment if a reverse char literal
 | 
						|
            if(index >= 2  &&  input[index] == '\''  &&  input[index-2] == '\'')
 | 
						|
            {
 | 
						|
                output = false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    std::string::const_iterator findNonStringEquals(const std::string& input)
 | 
						|
    {
 | 
						|
        initHasHelpers();
 | 
						|
        return std::find_if(input.begin(), input.end(), hasNonStringEquals);
 | 
						|
    }
 | 
						|
 | 
						|
    std::string::const_iterator findNonStringColon(const std::string& input)
 | 
						|
    {
 | 
						|
        initHasHelpers();
 | 
						|
        return std::find_if(input.begin(), input.end(), hasNonStringColon);
 | 
						|
    }
 | 
						|
 | 
						|
    std::string::const_iterator findNonStringBracket(const std::string& input, int bra)
 | 
						|
    {
 | 
						|
        initHasHelpers(bra);
 | 
						|
        return std::find_if(input.begin(), input.end(), hasNonStringBracket);
 | 
						|
    }
 | 
						|
 | 
						|
    void stripNonStringWhitespace(std::string& input)
 | 
						|
    {
 | 
						|
        initHasHelpers();
 | 
						|
        input.erase(remove_if(input.begin(), input.end(), hasNonStringWhiteSpace), input.end());
 | 
						|
    }
 | 
						|
 | 
						|
    void stripWhitespace(std::string& input)
 | 
						|
    {
 | 
						|
        input.erase(remove_if(input.begin(), input.end(), isSpace), input.end());
 | 
						|
    }
 | 
						|
 | 
						|
    bool stripChars(std::string& input, const std::string& chars)
 | 
						|
    {
 | 
						|
        std::string output = input;
 | 
						|
        for(int i=0; i<int(chars.size()); i++)
 | 
						|
        {
 | 
						|
            output.erase(std::remove(output.begin(), output.end(), chars[i]), output.end()) == output.end();
 | 
						|
        }
 | 
						|
 | 
						|
        input = output;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string stripStrings(const std::string& input)
 | 
						|
    {
 | 
						|
        size_t start = 0, end = std::string::npos;
 | 
						|
        std::string output = input;
 | 
						|
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            start = output.find_first_of('"', end + 1);
 | 
						|
            end = output.find_first_of('"', start + 1);
 | 
						|
            if(start == std::string::npos  ||  end == std::string::npos) break;
 | 
						|
 | 
						|
            output.erase(start, end - start + 1);
 | 
						|
            start = 0;
 | 
						|
            end = std::string::npos;
 | 
						|
        }
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string stripStrings(const std::string& input, std::vector<std::string>& strings, bool saveExtraFields)
 | 
						|
    {
 | 
						|
        size_t start, end = std::string::npos;
 | 
						|
        std::string output = input;
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            start = output.find_first_of('"', end + 1);
 | 
						|
            end = output.find_first_of('"', start + 1);
 | 
						|
            if(start == std::string::npos  ||  end == std::string::npos) break;
 | 
						|
 | 
						|
            strings.push_back(output.substr(start, end - start + 1));
 | 
						|
            output.erase(start, end - start + 1);
 | 
						|
 | 
						|
            // Save extra fields, (digits and semicolons)
 | 
						|
            if(saveExtraFields  &&  output[start] != ',')
 | 
						|
            {
 | 
						|
                // Save semicolon
 | 
						|
                if(output[start] == ';')
 | 
						|
                {
 | 
						|
                    strings.back() += ';';
 | 
						|
                    output.erase(start, 1);
 | 
						|
                }
 | 
						|
 | 
						|
                // Save digits
 | 
						|
                if(isdigit((unsigned char)output[start]))
 | 
						|
                {
 | 
						|
                    size_t i = start;
 | 
						|
                    while(isdigit((unsigned char)output[i]))
 | 
						|
                    {
 | 
						|
                        strings.back() += output[i++];
 | 
						|
                    }
 | 
						|
                    output.erase(start, i - start);
 | 
						|
                }
 | 
						|
 | 
						|
                // Save semicolon
 | 
						|
                if(output[start] == ';')
 | 
						|
                {
 | 
						|
                    strings.back() += ';';
 | 
						|
                    output.erase(start, 1);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            start = 0, end = 0;
 | 
						|
        }
 | 
						|
        while(start != std::string::npos  &&  end != std::string::npos);
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    void trimWhitespace(std::string& input)
 | 
						|
    {
 | 
						|
        size_t start = input.find_first_not_of(" \n\r\f\t\v");
 | 
						|
        if(start == std::string::npos)
 | 
						|
        {
 | 
						|
            stripWhitespace(input);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        size_t end = input.find_last_not_of(" \n\r\f\t\v");
 | 
						|
        size_t size = end - start + 1;
 | 
						|
 | 
						|
        input = input.substr(start, size);
 | 
						|
    }
 | 
						|
 | 
						|
    std::string collapseWhitespace(const std::string& input)
 | 
						|
    {
 | 
						|
        std::string output;
 | 
						|
 | 
						|
        std::unique_copy(input.begin(), input.end(), std::back_insert_iterator<std::string>(output), [](unsigned char a, unsigned char b) {return isspace(a) && isspace(b);});
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string collapseWhitespaceNotStrings(const std::string& input)
 | 
						|
    {
 | 
						|
        std::string output;
 | 
						|
        int spaceCount = 0;
 | 
						|
        bool inString = false;
 | 
						|
 | 
						|
        for(int i=0; i<int(input.size()); i++)
 | 
						|
        {
 | 
						|
            if((i==0  &&  input[i] == '\"')  ||  (i > 0  &&  input[i] == '\"'  &&  input[i-1] != '\\')) inString = !inString;
 | 
						|
 | 
						|
            if(isspace((unsigned char)input[i]))
 | 
						|
            {
 | 
						|
                if(!inString)
 | 
						|
                {
 | 
						|
                    if(spaceCount++ == 0) output.push_back(input[i]);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    output.push_back(input[i]);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                spaceCount = 0;
 | 
						|
                output.push_back(input[i]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string removeCommentsNotInStrings(const std::string& input)
 | 
						|
    {
 | 
						|
        std::string output;
 | 
						|
        bool inString = false;
 | 
						|
        bool inComment = false;
 | 
						|
 | 
						|
        for(int i=0; i<int(input.size()); i++)
 | 
						|
        {
 | 
						|
            // Check for string
 | 
						|
            if(!inComment)
 | 
						|
            {
 | 
						|
                if((i==0  &&  input[i] == '"')  ||  (i > 0  &&  input[i] == '"'  &&  input[i-1] != '\\')) inString = !inString;
 | 
						|
            }
 | 
						|
 | 
						|
            if(!inString)
 | 
						|
            {
 | 
						|
                // Check for comment, ' and REM
 | 
						|
                isInComment(input, i, inComment);
 | 
						|
 | 
						|
                // If not in string but in comment, skip char
 | 
						|
                if(inComment) continue;
 | 
						|
 | 
						|
                // Save char
 | 
						|
                output.push_back(input[i]);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Save char
 | 
						|
                output.push_back(input[i]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
 | 
						|
    void padString(std::string &input, int num, char pad)
 | 
						|
    {
 | 
						|
        if(num > int(input.size())) input.insert(0, num - int(input.size()), pad);
 | 
						|
    }
 | 
						|
 | 
						|
    void addString(std::string &input, int num, char add)
 | 
						|
    {
 | 
						|
        if(num > 0) input.append(num, add);
 | 
						|
    }
 | 
						|
 | 
						|
    int tabbedStringLength(const std::string& input, int tabSize)
 | 
						|
    {
 | 
						|
        int length = 0, newLine = 0;
 | 
						|
        for(int i=0; i<int(input.size()); i++)
 | 
						|
        {
 | 
						|
            switch(input[i])
 | 
						|
            {
 | 
						|
                case '\n':
 | 
						|
                case '\r': length = 0; newLine = i + 1;                   break;
 | 
						|
                case '\t': length += tabSize - ((i - newLine) % tabSize); break;
 | 
						|
                default:   length++;                                      break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return length;
 | 
						|
    }
 | 
						|
 | 
						|
    bool findMatchingBrackets(const std::string& input, size_t start, size_t& lbra, size_t& rbra, char obra)
 | 
						|
    {
 | 
						|
        char cbra = 0;
 | 
						|
        switch(obra)
 | 
						|
        {
 | 
						|
            case '(': cbra = ')'; break;
 | 
						|
            case '{': cbra = '}'; break;
 | 
						|
            case '[': cbra = ']'; break;
 | 
						|
 | 
						|
            default: return false;
 | 
						|
        }
 | 
						|
 | 
						|
        lbra = std::string::npos;
 | 
						|
        rbra = std::string::npos;
 | 
						|
 | 
						|
        int matched = 0;
 | 
						|
        bool startMatching = false;
 | 
						|
 | 
						|
        for(size_t i=start; i<input.size(); i++)
 | 
						|
        {
 | 
						|
            if(input[i] == obra)
 | 
						|
            {
 | 
						|
                if(!startMatching)
 | 
						|
                {
 | 
						|
                    lbra = i;
 | 
						|
                    startMatching = true;
 | 
						|
                }
 | 
						|
                matched++;
 | 
						|
            }
 | 
						|
 | 
						|
            if(input[i] == cbra)
 | 
						|
            {
 | 
						|
                rbra = i;
 | 
						|
                matched--;
 | 
						|
            }
 | 
						|
 | 
						|
            if(startMatching  &&  matched == 0) return true;
 | 
						|
        }
 | 
						|
 | 
						|
        lbra = std::string::npos;
 | 
						|
        rbra = std::string::npos;
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool findMatchingBrackets(const std::string& input, size_t start, size_t& lbra, size_t& rbra, char obra, std::string& name, int& paramNum)
 | 
						|
    {
 | 
						|
        bool result = findMatchingBrackets(input, start, lbra, rbra, obra);
 | 
						|
        std::vector<std::string> params = Expression::tokenise(input.substr(lbra + 1, rbra - (lbra + 1)), ',', true);
 | 
						|
        name = (lbra) ? input.substr(0, lbra): input;
 | 
						|
        paramNum = (result) ? int(params.size()) : 0;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    void operatorReduction(std::string& input)
 | 
						|
    {
 | 
						|
        size_t ss, aa, sa, as;
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            ss = input.find("--");
 | 
						|
            if(ss != std::string::npos)
 | 
						|
            {
 | 
						|
                input.erase(ss, 2);
 | 
						|
                input.insert(ss, "+");
 | 
						|
            }
 | 
						|
 | 
						|
            aa = input.find("++");
 | 
						|
            if(aa != std::string::npos)
 | 
						|
            {
 | 
						|
                input.erase(aa, 2);
 | 
						|
                input.insert(aa, "+");
 | 
						|
            }
 | 
						|
 | 
						|
            sa = input.find("-+");
 | 
						|
            if(sa != std::string::npos)
 | 
						|
            {
 | 
						|
                input.erase(sa, 2);
 | 
						|
                input.insert(sa, "-");
 | 
						|
            }
 | 
						|
 | 
						|
            as = input.find("+-");
 | 
						|
            if(as != std::string::npos)
 | 
						|
            {
 | 
						|
                input.erase(as, 2);
 | 
						|
                input.insert(as, "-");
 | 
						|
            }
 | 
						|
        }
 | 
						|
        while(ss != std::string::npos  ||  aa != std::string::npos  ||  sa != std::string::npos  ||  as != std::string::npos);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    // String/number conversions
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    std::string byteToHexString(uint8_t n)
 | 
						|
    {
 | 
						|
        std::stringstream ss;
 | 
						|
        ss << std::hex << std::setfill('0') << std::setw(2) << (int)n;
 | 
						|
        return "0x" + ss.str();
 | 
						|
    }
 | 
						|
 | 
						|
    std::string wordToHexString(uint16_t n)
 | 
						|
    {
 | 
						|
        std::stringstream ss;
 | 
						|
        ss << std::hex << std::setfill('0') << std::setw(4) << n;
 | 
						|
        return "0x" + ss.str();
 | 
						|
    }
 | 
						|
 | 
						|
    std::string strLower(const std::string& s)
 | 
						|
    {
 | 
						|
        std::string str = s;
 | 
						|
        std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {return char(tolower(c));} );
 | 
						|
        return str;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string strUpper(const std::string& s)
 | 
						|
    {
 | 
						|
        std::string str = s;
 | 
						|
        std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {return char(toupper(c));} );
 | 
						|
        return str;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string& strToLower(std::string& s)
 | 
						|
    {
 | 
						|
        std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {return char(tolower(c));} );
 | 
						|
        return s;
 | 
						|
    }
 | 
						|
 | 
						|
    std::string& strToUpper(std::string& s)
 | 
						|
    {
 | 
						|
        std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {return char(toupper(c));} );
 | 
						|
        return s;
 | 
						|
    }
 | 
						|
 | 
						|
    bool subAlphaHelper(int i)
 | 
						|
    {
 | 
						|
        // Valid chars are alpha, 'address of' and 'size of'
 | 
						|
        return (isalpha((unsigned char)i) || (i=='@') || (i=='#'));
 | 
						|
    }
 | 
						|
    std::string getSubAlpha(const std::string& s)
 | 
						|
    {
 | 
						|
        if(s.size() > 1)
 | 
						|
        {
 | 
						|
            char uchr = char(toupper((unsigned char)s[1]));
 | 
						|
            if((s[0] == '&'  &&  (uchr == 'H'  ||  uchr == 'B'  ||  uchr == 'O'))  ||
 | 
						|
               (s[0] == '0'  &&  (uchr == 'X'  ||  uchr == 'B'  ||  uchr == 'O')))
 | 
						|
            {
 | 
						|
                return s;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        auto it = std::find_if(s.begin(), s.end(), subAlphaHelper);
 | 
						|
        if(it == s.end()) return std::string("");
 | 
						|
 | 
						|
        size_t start = it - s.begin();
 | 
						|
        size_t end = s.find_first_of("-+/*%&<>=();,. ");
 | 
						|
        if(end == std::string::npos) return s.substr(start);
 | 
						|
 | 
						|
        return s.substr(start, end - start);
 | 
						|
    }
 | 
						|
 | 
						|
    NumericType getBase(const std::string& input, long& result)
 | 
						|
    {
 | 
						|
        bool success = true;
 | 
						|
        std::string token = input;
 | 
						|
        strToUpper(token);
 | 
						|
        
 | 
						|
        // Hex
 | 
						|
        if(token.size() >= 2  &&  token.c_str()[0] == '$')
 | 
						|
        {
 | 
						|
            for(int i=1; i<int(token.size()); i++) success &= _hexaDecimalChars[uint8_t(token.c_str()[i])];
 | 
						|
            if(success)
 | 
						|
            {
 | 
						|
                result = strtol(&token.c_str()[1], NULL, 16);
 | 
						|
                return HexaDecimal; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Hex
 | 
						|
        else if(token.size() >= 3  &&  ((token.c_str()[0] == '0'  &&  token.c_str()[1] == 'X')  ||  (token.c_str()[0] == '&'  &&  token.c_str()[1] == 'H')))
 | 
						|
        {
 | 
						|
            for(int i=2; i<int(token.size()); i++) success &= _hexaDecimalChars[uint8_t(token.c_str()[i])];
 | 
						|
            if(success)
 | 
						|
            {
 | 
						|
                result = strtol(&token.c_str()[2], NULL, 16);
 | 
						|
                return HexaDecimal; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Octal
 | 
						|
        else if(token.size() >= 3  &&  ((token.c_str()[0] == '0'  &&  (token.c_str()[1] == 'O' || token.c_str()[1] == 'Q'))  ||  (token.c_str()[0] == '&'  &&  token.c_str()[1] == 'O')))
 | 
						|
        {
 | 
						|
            for(int i=2; i<int(token.size()); i++) success &= _octalChars[uint8_t(token.c_str()[i])];
 | 
						|
            if(success)
 | 
						|
            {
 | 
						|
                result = strtol(&token.c_str()[2], NULL, 8);
 | 
						|
                return Octal; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Binary
 | 
						|
        else if(token.size() >= 3  &&  ((token.c_str()[0] == '0'  &&  token.c_str()[1] == 'B')  ||  (token.c_str()[0] == '&'  &&  token.c_str()[1] == 'B')))
 | 
						|
        {
 | 
						|
            for(int i=2; i<int(token.size()); i++) success &= _binaryChars[uint8_t(token.c_str()[i])];
 | 
						|
            if(success)
 | 
						|
            {
 | 
						|
                result = strtol(&token.c_str()[2], NULL, 2);
 | 
						|
                return Binary; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Decimal
 | 
						|
        else
 | 
						|
        {
 | 
						|
            for(int i=0; i<int(token.size()); i++) success &= _decimalChars[uint8_t(token.c_str()[i])];
 | 
						|
            if(success)
 | 
						|
            {
 | 
						|
                result = strtol(&token.c_str()[0], NULL, 10);
 | 
						|
                return Decimal; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return BadBase;
 | 
						|
    }
 | 
						|
 | 
						|
    bool stringToI8(const std::string& token, int8_t& result)
 | 
						|
    {
 | 
						|
        if(token.size() < 1  ||  token.size() > 10) return false;
 | 
						|
 | 
						|
        long lResult;
 | 
						|
        NumericType base = getBase(token, lResult);
 | 
						|
        if(base == BadBase) return false;
 | 
						|
 | 
						|
        result = int8_t(lResult);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool stringToU8(const std::string& token, uint8_t& result)
 | 
						|
    {
 | 
						|
        if(token.size() < 1  ||  token.size() > 10) return false;
 | 
						|
 | 
						|
        long lResult;
 | 
						|
        NumericType base = getBase(token, lResult);
 | 
						|
        if(base == BadBase) return false;
 | 
						|
 | 
						|
        result = uint8_t(lResult);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool stringToI16(const std::string& token, int16_t& result)
 | 
						|
    {
 | 
						|
        if(token.size() < 1  ||  token.size() > 18) return false;
 | 
						|
 | 
						|
        long lResult;
 | 
						|
        NumericType base = getBase(token, lResult);
 | 
						|
        if(base == BadBase) return false;
 | 
						|
 | 
						|
        result = int16_t(lResult);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool stringToU16(const std::string& token, uint16_t& result)
 | 
						|
    {
 | 
						|
        if(token.size() < 1  ||  token.size() > 18) return false;
 | 
						|
 | 
						|
        long lResult;
 | 
						|
        NumericType base = getBase(token, lResult);
 | 
						|
        if(base == BadBase) return false;
 | 
						|
 | 
						|
        result = uint16_t(lResult);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void stringToDouble(const std::string& token, double& result)
 | 
						|
    {
 | 
						|
        result = std::stod(token);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    // Tokenising
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    void tokeniseHelper(std::vector<std::string>& result, const std::string& text, size_t offset, size_t size, bool toUpper)
 | 
						|
    {
 | 
						|
        std::string s = text.substr(offset, size);
 | 
						|
        if(s.size() == 0) return;
 | 
						|
        if(toUpper) strToUpper(s);
 | 
						|
        result.push_back(s);
 | 
						|
    }
 | 
						|
 | 
						|
    // Tokenise using a list of chars as delimiters, returns all tokens, (i.e. start, end and whole if no delimiters)
 | 
						|
    std::vector<std::string> tokeniseMulti(const std::string& text, const std::string& delimiterStr, bool toUpper)
 | 
						|
    {
 | 
						|
        size_t offset0 = 0;
 | 
						|
        size_t offset1 = SIZE_MAX;
 | 
						|
        bool firstToken = true;
 | 
						|
        std::vector<std::string> result;
 | 
						|
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            offset0 = text.find_first_of(delimiterStr, offset1 + 1);
 | 
						|
            if(offset0 == std::string::npos)
 | 
						|
            {
 | 
						|
                // Entire text is a token
 | 
						|
                if(firstToken)
 | 
						|
                {
 | 
						|
                    result.push_back(text);
 | 
						|
                    return result;
 | 
						|
                }
 | 
						|
 | 
						|
                // Last token
 | 
						|
                tokeniseHelper(result, text, offset1 + 1, text.size() - offset0 + 1, toUpper);
 | 
						|
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
 | 
						|
            // First token
 | 
						|
            if(firstToken)
 | 
						|
            {
 | 
						|
                firstToken = false;
 | 
						|
                tokeniseHelper(result, text, 0, offset0, toUpper);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                tokeniseHelper(result, text, offset1 + 1, offset0 - (offset1 + 1), toUpper);
 | 
						|
            }
 | 
						|
 | 
						|
            offset1 = text.find_first_of(delimiterStr, offset0 + 1);
 | 
						|
            if(offset1 == std::string::npos)
 | 
						|
            {
 | 
						|
                // Last token
 | 
						|
                tokeniseHelper(result, text, offset0 + 1, text.size() - offset0 + 1, toUpper);
 | 
						|
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
            
 | 
						|
            tokeniseHelper(result, text, offset0 + 1, offset1 - (offset0 + 1), toUpper);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Tokenise using any char as a delimiter, returns tokens, preserve strings, preserves bracketed expressions
 | 
						|
    std::vector<std::string> tokenise(const std::string& text, char c, bool skipSpaces, bool toUpper)
 | 
						|
    {
 | 
						|
        std::vector<std::string> result;
 | 
						|
        const char* str = text.c_str();
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            const char *begin = str;
 | 
						|
 | 
						|
            int numQuotes = 0;
 | 
						|
            int numBrackets = 0;
 | 
						|
            while(*str  &&  (*str != c  ||  (numQuotes & 1)  ||  numBrackets))
 | 
						|
            {
 | 
						|
                if(*str == '"'  &&  (str == begin  ||  (str > begin  &&  *(str-1) != '\\'))) numQuotes++;
 | 
						|
                if(*str == '(') numBrackets++;
 | 
						|
                if(*str == ')') numBrackets--;
 | 
						|
                str++;
 | 
						|
            }
 | 
						|
 | 
						|
            std::string s = std::string(begin, str);
 | 
						|
            if(str > begin  &&  !(skipSpaces  &&  std::all_of(s.begin(), s.end(), isSpace)))
 | 
						|
            {
 | 
						|
                if(toUpper) strToUpper(s);
 | 
						|
                result.push_back(s);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        while(*str++ != 0);
 | 
						|
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    // Tokenise using any char as a delimiter, returns tokens and their offsets in original text, preserve strings
 | 
						|
    std::vector<std::string> tokeniseOffsets(const std::string& text, char c, std::vector<size_t>& offsets, bool skipSpaces, bool toUpper)
 | 
						|
    {
 | 
						|
        std::vector<std::string> result;
 | 
						|
        const char* str = text.c_str();
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            const char *begin = str;
 | 
						|
 | 
						|
            int numQuotes = 0;
 | 
						|
            while(*str  &&  (*str != c  ||  (numQuotes & 1)))
 | 
						|
            {
 | 
						|
                if(*str == '"'  &&  (str == begin  ||  (str > begin  &&  *(str-1) != '\\'))) numQuotes++;
 | 
						|
                str++;
 | 
						|
            }
 | 
						|
 | 
						|
            std::string s = std::string(begin, str);
 | 
						|
            if(str > begin  &&  !(skipSpaces  &&  std::all_of(s.begin(), s.end(), isSpace)))
 | 
						|
            {
 | 
						|
                if(toUpper) strToUpper(s);
 | 
						|
                offsets.push_back(size_t(str - text.c_str()) + 1);
 | 
						|
                result.push_back(s);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        while (*str++ != 0);
 | 
						|
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    // Tokenise using delimiters and single quotes, preserves strings
 | 
						|
    std::vector<std::string> tokeniseLine(const std::string& line, const std::string& delimiterStr)
 | 
						|
    {
 | 
						|
        std::string token = "";
 | 
						|
        bool delimiterStart = true;
 | 
						|
        bool stringStart = false;
 | 
						|
        enum DelimiterState {WhiteSpace, Quotes};
 | 
						|
        DelimiterState delimiterState = WhiteSpace;
 | 
						|
        std::vector<std::string> tokens;
 | 
						|
 | 
						|
        for(int i=0; i<=int(line.size()); i++)
 | 
						|
        {
 | 
						|
            // End of line is a delimiter for white space
 | 
						|
            if(i == int(line.size()))
 | 
						|
            {
 | 
						|
                if(delimiterState != Quotes)
 | 
						|
                {
 | 
						|
                    delimiterState = WhiteSpace;
 | 
						|
                    delimiterStart = false;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // White space delimiters
 | 
						|
                if(strchr(delimiterStr.c_str(), line[i]))
 | 
						|
                {
 | 
						|
                    if(delimiterState != Quotes)
 | 
						|
                    {
 | 
						|
                        delimiterState = WhiteSpace;
 | 
						|
                        delimiterStart = false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // Single quote string delimiters, skip escaped quotes
 | 
						|
                else if((i == 0  ||  (i > 0  &&  line[i-1] != '\\'))  &&  line[i] == '\'')
 | 
						|
                {
 | 
						|
                    delimiterState = Quotes;
 | 
						|
                    stringStart = !stringStart;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Build token
 | 
						|
            switch(delimiterState)
 | 
						|
            {
 | 
						|
                case WhiteSpace:
 | 
						|
                {
 | 
						|
                    // Don't save delimiters
 | 
						|
                    if(delimiterStart)
 | 
						|
                    {
 | 
						|
                        if(!strchr(delimiterStr.c_str(), line[i])) token += line[i];
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        if(token.size()) tokens.push_back(token);
 | 
						|
                        delimiterStart = true;
 | 
						|
                        token = "";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                case Quotes:
 | 
						|
                {
 | 
						|
                    // Save delimiters as well as chars
 | 
						|
                    if(stringStart)
 | 
						|
                    {
 | 
						|
                        token += line[i];
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        token += line[i];
 | 
						|
                        tokens.push_back(token);
 | 
						|
                        delimiterState = WhiteSpace;
 | 
						|
                        stringStart = false;
 | 
						|
                        token = "";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                default: break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return tokens;
 | 
						|
    }
 | 
						|
 | 
						|
    // Tokenise using delimiters and double quotes, preserves strings, outputs offsets to tokens
 | 
						|
    std::vector<std::string> tokeniseLineOffsets(const std::string& line, const std::string& delimiterStr, std::vector<size_t>& offsets)
 | 
						|
    {
 | 
						|
        std::string token = "";
 | 
						|
        bool delimiterStart = true;
 | 
						|
        bool stringStart = false;
 | 
						|
        enum DelimiterState {WhiteSpace, Quotes};
 | 
						|
        DelimiterState delimiterState = WhiteSpace;
 | 
						|
        std::vector<std::string> tokens;
 | 
						|
 | 
						|
        for(int i=0; i<=int(line.size()); i++)
 | 
						|
        {
 | 
						|
            // End of line is a delimiter for white space
 | 
						|
            if(i == int(line.size()))
 | 
						|
            {
 | 
						|
                if(delimiterState != Quotes)
 | 
						|
                {
 | 
						|
                    delimiterState = WhiteSpace;
 | 
						|
                    delimiterStart = false;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // White space delimiters
 | 
						|
                if(strchr(delimiterStr.c_str(), line[i]))
 | 
						|
                {
 | 
						|
                    if(delimiterState != Quotes)
 | 
						|
                    {
 | 
						|
                        delimiterState = WhiteSpace;
 | 
						|
                        delimiterStart = false;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // String delimiters, skip escaped quotes
 | 
						|
                else if((i==0  &&  line[i] == '\"')  ||  (i > 0  &&  line[i] == '\"'  &&  line[i-1] != '\\'))
 | 
						|
                {
 | 
						|
                    delimiterState = Quotes;
 | 
						|
                    stringStart = !stringStart;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Build token
 | 
						|
            switch(delimiterState)
 | 
						|
            {
 | 
						|
                case WhiteSpace:
 | 
						|
                {
 | 
						|
                    // Don't save delimiters
 | 
						|
                    if(delimiterStart)
 | 
						|
                    {
 | 
						|
                        if(!strchr(delimiterStr.c_str(), line[i])) token += line[i];
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        if(token.size())
 | 
						|
                        {
 | 
						|
                            tokens.push_back(token);
 | 
						|
                            offsets.push_back(i - token.size());
 | 
						|
                        }
 | 
						|
                        delimiterStart = true;
 | 
						|
                        token = "";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                case Quotes:
 | 
						|
                {
 | 
						|
                    // Save delimiters as well as chars
 | 
						|
                    if(stringStart)
 | 
						|
                    {
 | 
						|
                        token += line[i];
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        token += line[i];
 | 
						|
                        tokens.push_back(token);
 | 
						|
                        offsets.push_back(i - token.size());
 | 
						|
                        delimiterState = WhiteSpace;
 | 
						|
                        stringStart = false;
 | 
						|
                        token = "";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                default: break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return tokens;
 | 
						|
    }
 | 
						|
 | 
						|
    void replaceText(std::string& input, const std::string& text, const std::string& replace, size_t offset)
 | 
						|
    {
 | 
						|
        for(size_t foundPos=offset; ; foundPos+=replace.size())
 | 
						|
        {
 | 
						|
            foundPos = input.find(text, foundPos);
 | 
						|
            if(foundPos == std::string::npos) break;
 | 
						|
 | 
						|
            input.erase(foundPos, text.size());
 | 
						|
            input.insert(foundPos, replace);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    // Recursive decent parser
 | 
						|
    // ****************************************************************************************************************
 | 
						|
    char* getExpression(void) {return _expression;}
 | 
						|
    const char* getExpressionToParse(void) {return _expressionToParse.c_str();}
 | 
						|
    std::string& getExpressionToParseString(void) {return _expressionToParse;}
 | 
						|
    int getLineNumber(void) {return _lineNumber;}
 | 
						|
 | 
						|
    void setExpression(const std::string& expression, intptr_t n)
 | 
						|
    {
 | 
						|
        _advanceError = false;
 | 
						|
        _expressionToParse = expression;
 | 
						|
        _expression = (char*)_expressionToParse.c_str() + n;
 | 
						|
    }
 | 
						|
 | 
						|
    char peek(void)
 | 
						|
    {
 | 
						|
        if(_advanceError) return 0;
 | 
						|
 | 
						|
        return *_expression;
 | 
						|
    }
 | 
						|
 | 
						|
    char get(void)
 | 
						|
    {
 | 
						|
        if(_advanceError) return 0;
 | 
						|
 | 
						|
        char chr = *_expression;
 | 
						|
        //fprintf(stderr, "%s : %s : %c\n", _expressionToParse.c_str(), _expression, chr);
 | 
						|
        advance(1);
 | 
						|
        return chr;
 | 
						|
    }
 | 
						|
 | 
						|
    void save(void)
 | 
						|
    {
 | 
						|
        _advancePtr = uintptr_t(_expression);
 | 
						|
    }
 | 
						|
 | 
						|
    void restore(void)
 | 
						|
    {
 | 
						|
        _expression = (char *)_advancePtr;
 | 
						|
    }
 | 
						|
 | 
						|
    bool advance(intptr_t n)
 | 
						|
    {
 | 
						|
        if(size_t(_expression + n - _expressionToParse.c_str()) >= _expressionToParse.size())
 | 
						|
        {
 | 
						|
            _expression = (char*)_expressionToParse.c_str() + _expressionToParse.size();
 | 
						|
            _advanceError = true;
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        //std::string text = std::string(_expression, n);
 | 
						|
        //fprintf(stderr, "%s : %s : %d : %d\n", _expressionToParse.c_str(), text.c_str(), int(n), int(_expression + n - _expressionToParse.c_str()));
 | 
						|
 | 
						|
        _advanceError = false;
 | 
						|
        _expression += n;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool find(const std::string& text)
 | 
						|
    {
 | 
						|
        size_t pos = size_t(_expression - _expressionToParse.c_str());
 | 
						|
        std::string expr = _expressionToParse.substr(pos, text.size());
 | 
						|
        if(strToUpper(expr) == text)
 | 
						|
        {
 | 
						|
            advance(text.size());
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Searches for a function name in expression and then expects a left bracket before any other non whitespace char
 | 
						|
    bool findFunc(const std::string& text)
 | 
						|
    {
 | 
						|
        size_t pos = size_t(_expression - _expressionToParse.c_str());
 | 
						|
        std::string expr = _expressionToParse.substr(pos, text.size());
 | 
						|
        std::string expression = _expressionToParse.substr(pos);
 | 
						|
        stripNonStringWhitespace(expression);
 | 
						|
        if(strToUpper(expr) == text  &&  expression.size() > text.size())
 | 
						|
        {
 | 
						|
            char lbra = expression[text.size()];
 | 
						|
            if(lbra == '(')
 | 
						|
            {
 | 
						|
                advance(text.size());
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool number(int16_t& value)
 | 
						|
    {
 | 
						|
        char uchr;
 | 
						|
 | 
						|
        std::string valueStr;
 | 
						|
        uchr = char(toupper((unsigned char)peek()));
 | 
						|
        valueStr.push_back(uchr); get();
 | 
						|
        uchr = char(toupper((unsigned char)peek()));
 | 
						|
        if((uchr >= '0'  &&  uchr <= '9')  ||  uchr == 'X'  ||  uchr == 'H'  ||  uchr == 'B'  ||  uchr == 'O'  ||  uchr == 'Q')
 | 
						|
        {
 | 
						|
            valueStr.push_back(uchr); get();
 | 
						|
            uchr = char(toupper((unsigned char)peek()));
 | 
						|
            while((uchr >= '0'  &&  uchr <= '9')  ||  (uchr >= 'A'  &&  uchr <= 'F'))
 | 
						|
            {
 | 
						|
                valueStr.push_back(get());
 | 
						|
                uchr = char(toupper((unsigned char)peek()));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return stringToI16(valueStr, value);
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric factor(int16_t defaultValue)
 | 
						|
    {
 | 
						|
        int16_t value = 0;
 | 
						|
        Numeric numeric;
 | 
						|
 | 
						|
        if(peek() == '(')
 | 
						|
        {
 | 
						|
            get();
 | 
						|
            numeric = expression();
 | 
						|
 | 
						|
            if(peek() != ')')
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Expression::factor() : '%s:%d' : missing ')'\n", _expressionToParse.c_str(), _lineNumber + 1);
 | 
						|
                numeric = Numeric();
 | 
						|
            }
 | 
						|
            get();
 | 
						|
        }
 | 
						|
        else if((peek() >= '0'  &&  peek() <= '9')  ||  peek() == '$'  ||  peek() == '&')
 | 
						|
        {
 | 
						|
            if(!number(value))
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Expression::factor() : '%s:%d' : bad numeric data\n", _expressionToParse.c_str(), _lineNumber + 1);
 | 
						|
                numeric = Numeric();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                numeric = Numeric(value, -1, true, false, false, Number, BooleanCC, Int16Both, std::string(""), std::string(""));
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Functions
 | 
						|
        else if(find("POW"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorPOWF(numeric);
 | 
						|
        }
 | 
						|
        else if(find("SQRT"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorSQRT(numeric);
 | 
						|
        }
 | 
						|
        else if(find("EXP2"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorEXP2(numeric);
 | 
						|
        }
 | 
						|
        else if(find("EXP"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorEXP(numeric);
 | 
						|
        }
 | 
						|
        else if(find("LOG10"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorLOG10(numeric);
 | 
						|
        }
 | 
						|
        else if(find("LOG2"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorLOG2(numeric);
 | 
						|
        }
 | 
						|
        else if(find("LOG"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorLOG(numeric);
 | 
						|
        }
 | 
						|
        else if(find("SIN"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorSIN(numeric);
 | 
						|
        }
 | 
						|
        else if(find("COS"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorCOS(numeric);
 | 
						|
        }
 | 
						|
        else if(find("TAN"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorTAN(numeric);
 | 
						|
        }
 | 
						|
        else if(find("ASIN"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorASIN(numeric);
 | 
						|
        }
 | 
						|
        else if(find("ACOS"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorACOS(numeric);
 | 
						|
        }
 | 
						|
        else if(find("ATAN2"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorATAN2(numeric);
 | 
						|
        }
 | 
						|
        else if(find("ATAN"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorATAN(numeric);
 | 
						|
        }
 | 
						|
        else if(find("RAND"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorRAND(numeric);
 | 
						|
        }
 | 
						|
        else if(find("REV16"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorREV16(numeric);
 | 
						|
        }
 | 
						|
        else if(find("REV8"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorREV8(numeric);
 | 
						|
        }
 | 
						|
        else if(find("REV4"))
 | 
						|
        {
 | 
						|
            numeric = factor(0); numeric = operatorREV4(numeric);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Unary operators
 | 
						|
            switch(peek())
 | 
						|
            {
 | 
						|
                case '+': get(); numeric = factor(0);                         break;
 | 
						|
                case '-': get(); numeric = factor(0); numeric = operatorNEG(numeric); break;
 | 
						|
                case '~': get(); numeric = factor(0); numeric = operatorNOT(numeric); break;
 | 
						|
 | 
						|
                // Unknown
 | 
						|
                default: numeric = Numeric(defaultValue, -1, true, false, false, Number, BooleanCC, Int16Both, std::string(_expression), std::string("")); break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return numeric;
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric term(void)
 | 
						|
    {
 | 
						|
        Numeric numeric, result = factor(0);
 | 
						|
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            if(find("**"))         {       numeric = factor(0); result = operatorPOW(result, numeric);}
 | 
						|
            else if(peek() == '*') {get(); numeric = factor(0); result = operatorMUL(result, numeric);}
 | 
						|
            else if(peek() == '/') {get(); numeric = factor(0); result = operatorDIV(result, numeric);}
 | 
						|
            else if(peek() == '%') {get(); numeric = factor(0); result = operatorMOD(result, numeric);}
 | 
						|
            else return result;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric expr(void)
 | 
						|
    {
 | 
						|
        Numeric numeric, result = term();
 | 
						|
    
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            if(peek() == '+')      {get(); numeric = term(); result = operatorADD(result, numeric);}
 | 
						|
            else if(peek() == '-') {get(); numeric = term(); result = operatorSUB(result, numeric);}
 | 
						|
            else return result;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric logical(void)
 | 
						|
    {
 | 
						|
        Numeric numeric, result = expr();
 | 
						|
    
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            if(peek() == '&')      {get(); numeric = expr(); result = operatorAND(result, numeric);}
 | 
						|
            else if(peek() == '^') {get(); numeric = expr(); result = operatorXOR(result, numeric);}
 | 
						|
            else if(peek() == '|') {get(); numeric = expr(); result = operatorOR(result,  numeric);}
 | 
						|
            else if(find("<<"))    {       numeric = expr(); result = operatorLSL(result, numeric);}
 | 
						|
            else if(find(">>"))    {       numeric = expr(); result = operatorLSR(result, numeric);}
 | 
						|
            else return result;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    Numeric expression(bool returnAddress)
 | 
						|
    {
 | 
						|
        UNREFERENCED_PARAM(returnAddress);
 | 
						|
 | 
						|
        Numeric numeric, result = logical();
 | 
						|
            
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            if(find("=="))         {       numeric = logical(); result = operatorEQ(result, numeric);}
 | 
						|
            else if(find("!="))    {       numeric = logical(); result = operatorNE(result, numeric);}
 | 
						|
            else if(find("<="))    {       numeric = logical(); result = operatorLE(result, numeric);}
 | 
						|
            else if(find(">="))    {       numeric = logical(); result = operatorGE(result, numeric);}
 | 
						|
            else if(peek() == '<') {get(); numeric = logical(); result = operatorLT(result, numeric);}
 | 
						|
            else if(peek() == '>') {get(); numeric = logical(); result = operatorGT(result, numeric);}
 | 
						|
            else return result;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    bool parse(const std::string& expression, int lineNumber, Numeric& numeric)
 | 
						|
    {
 | 
						|
        _output = numeric;
 | 
						|
 | 
						|
        _advanceError = false;
 | 
						|
        _expressionToParse = expression;
 | 
						|
        _lineNumber = lineNumber;
 | 
						|
 | 
						|
        _expression = (char*)_expressionToParse.c_str();
 | 
						|
 | 
						|
        numeric = _exprFunc(numeric._returnAddress);
 | 
						|
 | 
						|
        // If there are trailing chars left over and they are not whitespace, then syntax error
 | 
						|
        char* chr = _expression;
 | 
						|
        while(*chr)
 | 
						|
        {
 | 
						|
            if(!isspace(*chr++))
 | 
						|
            {
 | 
						|
                numeric._isValid = false;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return numeric._isValid;
 | 
						|
    }
 | 
						|
} |