Sam Weaver
Sam Weaver

Reputation: 1079

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

Upvotes: 1

Views: 662

Answers (5)

Eugene Belenkov
Eugene Belenkov

Reputation: 1

More compact and more fast...

String.prototype.toLowerCaseQuoted = function(q=["\"", "\'"], n=0) {
    var s = this.valueOf().split(q[n]);
    if (s.length == 1) return this.valueOf().toLowerCase();
    for (var i = 0; i < s.length; i+=2) {
        s[i] = s[i].toLowerCaseQuoted(q,n+1);
    }
    return s.join(q[n]);
}

For example:

`ThIs iS a teST "sTriNg Y'alL" aS yOu cAN sEE 'hEllO woRl\' o miNE'`.toLowerCaseQuoted();

Returns:

`this is a test "sTriNg Y'alL" as you can see 'hEllO woRl' o mine'`

Upvotes: 0

scuba88
scuba88

Reputation: 458

This is a followup to @Paul S. This should handle strings without a block of quotes...

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 (j === str.length) {
                            // We reached the end without finding any quote blocks
              if (j > i) {
                blocks.push({type: 'regular', str: str.slice(i,j)});
              }
            }
        }
    }
    return blocks;
}

Upvotes: 1

Paul
Paul

Reputation: 467

String.prototype.toLowerCaseQuoted = function() {
    var oldValue = this.valueOf();
    var newValue = '';
    var inside = 0;

    for (var i = 0; i < oldValue.length; i++) {
        if (oldValue[i] == '"') {
            if (inside == 0) {
                inside = 1;
            } else {
                inside = 0;
            }
        }

        if (inside == 1) {
            newValue += oldValue[i];
        } else {
            newValue += oldValue[i].toLowerCase();
        }
    }

    return newValue;
 }

Upvotes: 0

Paul S.
Paul S.

Reputation: 66364

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'

Upvotes: 2

danielrw7
danielrw7

Reputation: 120

I am sure that there is a regex solution, but here is another solution that replaces the quoted strings before it lowercases it:

String.prototype.toLowerCaseQuoted = function() {
   var str = this.valueOf();
   var replacements = [];
   var I = 0;
   str = str
      .replace(/((\".+\")|(\'.+\'))/g, function(s) {
         console.log(s)
         replacements.push(s);
         return "%s"+(I++)+"%"
      })
      .toLowerCase()
      .replace(/%s([0-9]+)%/g, function(s) {
         var k = parseInt(s.match(/([0-9])+/)[0]);
         console.log(k)
         return replacements[k];
      });
   return str;
}

For example:

"WILL BE LOWER CASE \"QUOTED\" \'MORE QUOTED\'".toLowerCaseQuoted()

Returns "will be lower case "QUOTED" 'MORE QUOTED'"

Upvotes: 1

Related Questions