StringtoNumber
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © hapharmonic

//@version=5

// @description str1 = '12340'  , vv = numstrToNum(str1)
library("StringtoNumber")

// @function X.numstrToNum(TF)
// @param x strToNum(Text)
// @returns returns numX


// some enumerations
DECIMAL_POINT = 0.1
MINUS = -1

// error() =>  // a function to throw an error
//     label.new(0, close[-1])

//
// Here goes all the magic.
//
// To detect that the last value is a specific digit (or a minus or a decimal point),
// we should add any symbol which is definitely not in the string to the end of the string.
// I use semicolon (';') for this purpose.
// Then we'are trying to replace the group of the digit and semicolon with empty string.
// Then we're comparing the new string to the string before replacing.
// If the string changed - it means that the last value was the being checked digit.
//
// NOTE: we cannot try to replace just a digit with an empty string for this purpose,
// because replace_all will remove all the digits in the string and we won't be able to understand
// what the digit's position was.
//
cutLastDigit(str) =>
    s = str + ';'
    r = str.replace_all(s, '1;', '')
    if r != s
        [r, 1.]
    else
        r = str.replace_all(s, '2;', '')
        if r != s
            [r, 2.]
        else
            r = str.replace_all(s, '3;', '')
            if r != s
                [r, 3.]
            else
                r = str.replace_all(s, '4;', '')
                if r != s
                    [r, 4.]
                else
                    r = str.replace_all(s, '5;', '')
                    if r != s
                        [r, 5.]
                    else
                        r = str.replace_all(s, '6;', '')
                        if r != s
                            [r, 6.]
                        else
                            r = str.replace_all(s, '7;', '')
                            if r != s
                                [r, 7.]
                            else
                                r = str.replace_all(s, '8;', '')
                                if r != s
                                    [r, 8.]
                                else
                                    r = str.replace_all(s, '9;', '')
                                    if r != s
                                        [r, 9.]
                                    else
                                        r = str.replace_all(s, '0;', '')
                                        if r != s
                                            [r, 0.]
                                        else
                                            r = str.replace_all(s, '.;', '')
                                            if r != s
                                                [r, DECIMAL_POINT]
                                            else
                                                r = str.replace_all(s, '-;', '')
                                                if r != s
                                                    [r, MINUS]
                                                // else
                                                //     error()
                                                //     [str, -1]


strToNum(str) =>
    fractional = 0.  // fractional part of the number
    integer = 0.  // integer part of the number

    s_new = str
    position = 0.0  // handled position of the number.
    sign = 1  // sign of the number. 1.0 is PLUS and -1.0 is MINUS.
    for i = 0 to 1000 by 1
        [s, digit] = cutLastDigit(s_new)  // here we'll cut the digits by one from the string till the string is empty.
        if digit == DECIMAL_POINT  // DECIMAL_POINT is found. That means the number has decimal part.
            order = math.pow(10, i)  // convert the decimal part by shifting the accumulated value to (10^i) positions to right
            fractional := integer / order
            integer := 0.
            position := 0
            position
        else
            if digit == MINUS  // MINUS sing is found.
                sign := MINUS
                break  // I expect that there's nothing after the MINUS ('-') sign.
                0.  // just a little workaround to help the 'if' to detect returning type.
            else
                integer += digit * math.pow(10, position)  // it's just a regular digit.
                position += 1
                position
        if s == ''
            break
        s_new := s  // If we are here, then there are more digits in the string. Let's handle the next one!
        s_new
    sign * (integer + fractional)  // We've exited from the loop. Build the returning value.

// ============================================================================
// ============================ example of using===============================
// ============================================================================
// let's translate the strings below to numbers
export numstrToNum(string Text) =>
    numX = strToNum(Text)
    numX

TF = '240'
GETTF = numstrToNum(TF)
L = label.new(bar_index, high, '|| numstrToNum :>> || ' + str.tostring(GETTF), style=label.style_label_down,size=size.large)
label.delete(L[1])

