1

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

2
  • 1
    You need to write a parser. There's nothing built-in that will do this for you. Commented Jun 26, 2015 at 16:08
  • I gathered that, I was looking for advice on the fastest and cleanest way to do this. Commented Jun 26, 2015 at 16:08

5 Answers 5

2

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'
Sign up to request clarification or add additional context in comments.

3 Comments

This is actually more than I could have asked for, it does exactly what I was asking for, and more! There is a small issue. This test string: "ThIs iS a teST \"sTriNg Y'alL\" aS yOu cAN sEE 'hEllO woRl\\' o miNE'" and this test string: "ThIs iS a teST \"sTriNg Y'alL\" aS yOu cAN sEE 'hEllO woRl\\' o miNE'" test StrING are not identical, but they both generate the same result. If you could fix this, it would be superb!
@SamWeaver I'm guessing you mean the last section, if not part of a quote, was going missing. I think I was checking the wrong variable for it right at the end, give the new edit a go and let me know.
This doesn't work when there are no quote blocks preset. In this case it returns an empty string. I'm looking at how to fix now.
1

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'"

1 Comment

This can't handle escaped quotes "WILL BE \\\"LOWER\\\" CASE \"QUOTED\" \'MORE QUOTED\'".toLowerCaseQuoted(); // "will be \"LOWER\" CASE "QUOTED" 'MORE QUOTED'"
1

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;
}

Comments

0
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;
 }

1 Comment

This handles double quotes but fails to handle single quotes appropriately.
0

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'`

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.