Search code examples
javascriptstringcasequoteslowercase

Make a string lowercase respecting quotes - Javascript


I am building a parser for a Domain Specific Language, (or DSL,) and I am attempting to convert a string to all lowercase. I know that toLowerCase would easily do this task, but I need to leave string that is quoted with double or single quotes (" or ') in its original case. For an example, see below:

Input: ThIs iS a teST "sTriNg Y'alL" aS yOu cAN sEE 'hEllO woRl\' o miNE'

Output: this is a test "sTriNg Y'alL" as you can see 'hEllO woRl\' o miNE'

EDIT: Added backslashed quotes


Solution

  • Just threw together a quick parser, not sure how well it works but it should deal with unlimited backslash escaping

    function string_to_block(str) {
        var blocks = [],
            i, j, k;
        function isEscaped(str, i) {
            var escaped = false;
            while (str[--i] === '\\') escaped = !escaped;
            return escaped;
        }
        start: for (i = 0; i < str.length; i = j + 1) {
            find: for (j = i; j < str.length; ++j) {
                if (str[j] === '"' && !isEscaped(str, j)) {
                    if (j > i) {
                        blocks.push({type: 'regular', str: str.slice(i, j)});
                    }
                    end: for (k = j + 1; k < str.length; ++k) {
                        if (str[k] === '"' && !isEscaped(str, k)) {
                            // found a "str" block
                            blocks.push({type: 'quote', str: str.slice(j, k + 1)});
                            j = k;
                            break find;
                        }
                    }
                    throw new SyntaxError('unclosed "str... starting at index ' + j);
                }
                if (str[j] === "'" && !isEscaped(str, j)) {
                    if (j > i) {
                        blocks.push({type: 'regular', str: str.slice(i, j)});
                    }
                    end: for (k = j + 1; k < str.length; ++k) {
                        if (str[k] === "'" && !isEscaped(str, k)) {
                            // found a 'str' block
                            blocks.push({type: 'quote', str: str.slice(j, k + 1)});
                            j = k;
                            break find;
                        }
                    }
                    throw new SyntaxError("unclosed 'str... starting at index " + j);
                }
            }
        }
        if (k + 1 < str.length) {
            blocks.push({type: 'regular', str: str.slice(k + 1)});
        }
        return blocks;
    }
    

    Now

    var foo = string_to_block("ThIs iS a teST \"sTriNg Y'alL\" aS yOu cAN sEE 'hEllO woRl\\' o miNE'");
    /*
    [
        {"type": "regular", "str": "ThIs iS a teST "},
        {"type": "quote"  , "str": "\"sTriNg Y'alL\""},
        {"type": "regular", "str": " aS yOu cAN sEE "},
        {"type": "quote"  , "str": "'hEllO woRl\\' o miNE'"}
    ]
    */
    

    So we can re-build your string as desired;

    var i, str = '';
    for (i = 0; i < foo.length; ++i) {
        if (foo[i].type === 'regular') str += foo[i].str.toLowerCase();
        else str += foo[i].str;
    }
    str; // this is a test "sTriNg Y'alL" as you can see 'hEllO woRl\' o miNE'