evenro evenro - 3 months ago 5
Swift Question

swift LLDB produces correct results, but different results from code execution

I'm experiencing a weird behavior...

In short - running this command (on a

Range<Index>
) within an application code

currIndexPlaceA = a!.startIndex.successor()..<newText.endIndex;


is ignored...

When I set a breakpoint after this command, and run
po a
in the debug window (lldb), I get:


▿ Optional> ▿ Some : Range(39..<44)
- startIndex : 39
- endIndex : 44


while
po currIndexPlaceA
is:


▿ Range(5..<54)
- startIndex : 5
- endIndex : 54


it is clear that startIndex should have been higher than 39!

but, running the same command through the lldb window:

expr currIndexPlaceA = a!.startIndex.successor()..<newText.endIndex;


results in (
po currIndexPlaceA
):


▿ Range(40..<54)
- startIndex : 40
- endIndex : 54


What's going on here?????

If you want to debug my full function.. it's kind of a headache - it's a sort of xml parser...

I test it with text:
<id><id></id><id/><id /><id><id /><id/><id></id></id></id>


and tagName:
id


func getTagContent(text : String, tagName : String) -> [String!]!{
// Extracts reply
var newText = text;
var currProcessing = text;
var retVal = [String!]();
while (newText != ""){
let rangeFirst = newText.rangeOfString("<" + tagName, options: [], range: nil, locale: nil);
if let actrange = rangeFirst
{
newText = newText.substringFromIndex(actrange.startIndex.advancedBy(tagName.characters.count + 1));

let rangeEndFirstTag = newText.rangeOfString(">");
if let actRangeEndFirstTab = rangeEndFirstTag {
let distToCloseTag = newText.startIndex.distanceTo(actRangeEndFirstTab.startIndex)
if (distToCloseTag == 0 || (distToCloseTag > 0 && newText[actRangeEndFirstTab.startIndex.predecessor()] != "/")) {
newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.advancedBy(1));

var currIndexPlaceA = newText.startIndex..<newText.endIndex;
var currIndexPlaceB = newText.startIndex..<newText.endIndex;
var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
if a == nil {a = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);}
var b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if b == nil {b = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);}

while (a != nil && b != nil && b?.startIndex < a?.startIndex){
let txt = newText.substringFromIndex(b!.endIndex);
var closedTag = false;
if let tmpRagne = txt.rangeOfString(">"){
if (tmpRagne.startIndex > txt.startIndex){
if (txt[tmpRagne.startIndex.predecessor()] == "/"){
closedTag = true;
}
}
}

if closedTag{
currIndexPlaceB = b!.startIndex.successor()..<newText.endIndex;
var b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if b == nil {b = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);}
}
else{
currIndexPlaceA = a!.startIndex.successor()..<newText.endIndex;
currIndexPlaceB = b!.startIndex.successor()..<newText.endIndex;

var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
if a == nil {a = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);}
var b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
if b == nil {b = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);}
}
}
if a == nil { return nil; }


currProcessing = newText.substringToIndex(a!.startIndex);
retVal.append(currProcessing);
newText = newText.substringFromIndex(a!.startIndex.successor());
if let lastRange = newText.rangeOfString(">"){
newText = newText.substringFromIndex(lastRange.endIndex);
}
else {
return nil;
}

}
else{
newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.successor());
retVal.append(nil);
}
}
}
else {
break;
}
}


return retVal;
}

Answer

So here is the offending code.

currIndexPlaceA = a!.startIndex.successor()..<newText.endIndex;
currIndexPlaceB = b!.startIndex.successor()..<newText.endIndex;
var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);

A definite bug is the shadowing of a and b declared outside the while loop with the var a and var b in this else block. As for your LLDB problem, make absolutely sure that you have stopped on the line where currIndexPlaceB is set and not on or after where var a is set. Make sure all optimizations have been turned off as that may be causing code placement to be shuffled.


(Question writer... Adding code edits which solved the issue)

I'm adding here the final code, with additional bugs fixed (so if someone wants to get tags from xml - you'll have a copy-paste). When debugging initially - I did't understand what was going on so I started using endIndex instead of startIndex etc.

func getTagContent(text : String, tagName : String) -> [String!]!{
    // Extracts reply
    var newText = text;
    var currProcessing = text;
    var retVal = [String!]();
    while (newText != ""){
        let rangeFirst = newText.rangeOfString("<" + tagName, options: [], range: nil, locale: nil);
        if let actrange = rangeFirst
        {
            newText = newText.substringFromIndex(actrange.startIndex.advancedBy(tagName.characters.count + 1));

            let rangeEndFirstTag = newText.rangeOfString(">");
            if let actRangeEndFirstTab = rangeEndFirstTag {
                let distToCloseTag = newText.startIndex.distanceTo(actRangeEndFirstTab.startIndex)
                if (distToCloseTag == 0 || (distToCloseTag > 0 && newText[actRangeEndFirstTab.startIndex.predecessor()] != "/")) {
                    newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.advancedBy(1));

                    var currIndexPlaceA = newText.startIndex..<newText.endIndex;
                    var currIndexPlaceB = newText.startIndex..<newText.endIndex;
                    var a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
                    if a == nil {a = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);}
                    var b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
                    if b == nil {b = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);}

                    while (a != nil && b != nil && b?.startIndex < a?.startIndex){
                        let txt = newText.substringFromIndex(b!.startIndex);
                        var closedTag = false;
                        if let tmpRagne = txt.rangeOfString(">"){
                            if (tmpRagne.startIndex > txt.startIndex){
                                if (txt[tmpRagne.startIndex.predecessor()] == "/"){
                                    closedTag = true;
                                }
                            }
                        }

                        if closedTag{
                            currIndexPlaceB = b!.endIndex..<newText.endIndex;
                            b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
                            let tmpb = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
                            if (b == nil || (tmpb != nil && tmpb?.startIndex < b?.startIndex)) {b = tmpb;}
                        }
                        else{
                            currIndexPlaceA = a!.endIndex..<newText.endIndex;
                            currIndexPlaceB = b!.endIndex..<newText.endIndex;

                            a = newText.rangeOfString("</" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
                            let tmpa = newText.rangeOfString("</" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceA, locale: nil);
                            if (a == nil || (tmpa != nil && tmpa!.startIndex < a?.startIndex))  {a = tmpa;}
                            b = newText.rangeOfString("<" + tagName + ">", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
                            let tmpb = newText.rangeOfString("<" + tagName + " ", options: NSStringCompareOptions.LiteralSearch, range: currIndexPlaceB, locale: nil);
                            if (b == nil || (tmpb != nil && tmpb?.startIndex < b?.startIndex)) {b = tmpb;}
                        }
                    }
                    if a == nil { return nil; }


                    currProcessing = newText.substringToIndex(a!.startIndex);
                    retVal.append(currProcessing);
                    newText = newText.substringFromIndex(a!.startIndex.successor());
                    if let lastRange = newText.rangeOfString(">"){
                        newText = newText.substringFromIndex(lastRange.endIndex);
                    }
                    else {
                        return nil;
                    }

                }
                else{
                    newText = newText.substringFromIndex(actRangeEndFirstTab.startIndex.successor());
                    retVal.append(nil);
                }
            }
        }
        else {
            break;
        }
    }


    return retVal;
}