Wiki

Ticket #161: ticket161.patch

File ticket161.patch, 8.9 KB (added by eric.sellon, 15 years ago)
  • Source/Expr.cobra

     
    30683068        else 
    30693069            .compiler.warning(this, 'The given expression is already non-nilable so "to !" is redundant. You can remove it.') # TODO: needs test case 
    30703070            _type = _expr.type 
     3071 
     3072 
     3073class ChainedCompareExpr 
     3074    is partial 
     3075    inherits Expr 
     3076         
     3077    var _items as List<of Expr> 
     3078    var _operations as List<of String> 
     3079     
     3080    cue init(opToken as IToken, items as List<of Expr>, operations as List<of String>) 
     3081        require 
     3082            # should not happen, there is an error in the compiler if this occurs 
     3083            items.count == operations.count + 1 
     3084            items.count > 2 
     3085        body 
     3086            base.init(opToken) 
     3087            _items = List<of Expr>() 
     3088            _operations = List<of String>() 
     3089            _items = items 
     3090            _operations = operations 
     3091     
     3092    def _bindImp is override 
     3093        base._bindImp 
     3094        itemIndex = 1 
     3095        for operation in _operations 
     3096            _items[itemIndex - 1].bindImp 
     3097            _items[itemIndex].bindImp 
     3098            _willCompare(_items[itemIndex - 1], operation, _items[itemIndex]) 
     3099            itemIndex += 1 
     3100        _type = .compiler.boolType 
     3101         
     3102    def _willCompare(left as Expr, op as String, right as Expr) as bool 
     3103        if left.type is nil or right.type is nil 
     3104            assert .hasError 
     3105            return false 
     3106        leftType = left.type to ! 
     3107        rightType = right.type to ! 
     3108        innerMsg as String? 
     3109        # cannot invoke .isAssignableTo directly on the types because if one is nilable and the other is not, you can get false, even though that doesn't apply for "is". So use isEquatableTo and isComparableTo 
     3110        if op in ['EQ', 'NE', 'IS', 'ISNOT'] 
     3111            if not leftType.isEquatableTo(rightType) 
     3112                innerMsg = if(op.startsWith('IS'), 'can never be identical', 'cannot be equated') 
     3113            else if op in ['IS', 'ISNOT'] 
     3114                # Essentially "is" and "is not" are for reference types. Give a warning when used for value types. 
     3115                if not leftType.isDynamicOrPassThrough and not rightType.isDynamicOrPassThrough 
     3116                    opName = if(op=='IS', 'is', 'is not') 
     3117                    altName = if(op=='IS', '==', '<>') 
     3118                    if leftType inherits NilableType and rightType inherits NilType 
     3119                        pass 
     3120                    else if not leftType.isReference and not rightType.isReference 
     3121                        .compiler.warning(this, 'Both the left and right sides of "[opName]" are value types ("[leftType.name]" and "[rightType.name]"), but "[opName]" applies to reference types. Use "[altName]" instead.') 
     3122                    # interestingly, I don't think the following ever happen in practice... 
     3123                    else if not leftType.isReference and not rightType.isNilableAndDescendantOf(leftType) 
     3124                        .compiler.warning(this, 'The left side of "[opName]" is a value type ("[leftType.name]") while the right side is an incompatible reference type ("[rightType.name]").') 
     3125                    else if not rightType.isReference and not leftType.isNilableAndDescendantOf(rightType) 
     3126                        .compiler.warning(this, 'The right side of "[opName]" is a value type ("[rightType.name]") while the left side is an incompatible reference type ("[leftType.name]").')                      
     3127        else 
     3128            if not leftType.isComparableTo(rightType) 
     3129                innerMsg = 'cannot be compared' 
     3130        if innerMsg 
     3131            typeNames = if(left.type.name==right.type.name, 'type ("[left.type.name]")', 'types ("[left.type.name]" and "[right.type.name]")') 
     3132            msg = 'The left and right sides of the "[.token.text]" expression [innerMsg] because of their [typeNames].' 
     3133            if op == 'IS' and right.type.isDescendantOf(.compiler.typeType) # C# 
     3134                msg += ' Maybe you should try "inherits" instead of "is".' 
     3135            .throwError(msg) 
     3136            return false 
     3137        return true 
     3138     
     3139    def toCobraSource as String is override 
     3140        sb = StringBuilder(_items[0].toCobraSource) 
     3141        sep = ' '        
     3142        for index in _operations.count 
     3143            sb.append(sep) 
     3144            branch _operations[index] 
     3145                on 'EQ' 
     3146                    sb.append('==') 
     3147                on 'NE' 
     3148                    sb.append('<>') 
     3149                on 'GT' 
     3150                    sb.append('>') 
     3151                on 'LT' 
     3152                    sb.append('<') 
     3153                on 'GE' 
     3154                    sb.append('>=') 
     3155                on 'LE' 
     3156                    sb.append('<=') 
     3157                on 'IS' 
     3158                    sb.append('is') 
     3159                on 'ISNOT' 
     3160                    sb.append('is not') 
     3161            sb.append(sep) 
     3162            sb.append(_items[index + 1].toCobraSource) 
     3163        return sb.toString 
  • Source/BackEndClr/SharpGenerator.cobra

     
    44394439            throw FallThroughException(op) 
    44404440 
    44414441 
     4442class ChainedCompareExpr 
     4443    is partial 
     4444     
     4445    def writeSharpDef(sw as CurlyWriter, parens as bool) is override 
     4446        if parens, sw.write('(') 
     4447        .writeSharpDef(sw) 
     4448        if parens, sw.write(')') 
     4449     
     4450    def writeSharpDef(sw as CurlyWriter) is override 
     4451        sw.write('CobraLangInternal.CobraCore.ChainedComparison(') 
     4452        _items[0].writeSharpDef(sw) 
     4453        itemIndex = 1 
     4454        for operation in _operations 
     4455            sw.write(', "[operation]", ') 
     4456            _items[itemIndex].writeSharpDef(sw) 
     4457            itemIndex += 1 
     4458        sw.write(')') 
     4459 
     4460 
    44424461class DotExpr 
    44434462    is partial 
    44444463 
  • Source/Cobra.Lang/CobraCore.cobra

     
    499499                    for code in _htmlEncodes 
    500500                        s = s.replace(code[0], code[1]) 
    501501                    return s 
     502            def singleCompare(left as dynamic, op as String, right as dynamic) as bool 
     503                returnValue = false 
     504                branch op 
     505                    on 'EQ' 
     506                        returnValue = (left == right) 
     507                    on 'NE' 
     508                        returnValue = (left <> right) 
     509                    on 'GT' 
     510                        returnValue = (left > right) 
     511                    on 'LT' 
     512                        returnValue = (left < right) 
     513                    on 'GE' 
     514                        returnValue = (left >= right) 
     515                    on 'LE' 
     516                        returnValue = (left <= right) 
     517                    on 'IS' 
     518                        returnValue = (left == right) 
     519                    on 'ISNOT' 
     520                        returnValue = (left <> right) 
     521                return returnValue 
     522            def chainedComparison(stuff as vari dynamic) as bool 
     523                returnValue = true 
     524                things = List<of dynamic>(stuff) 
     525                for index in 0 : things.count - 1 : 2 
     526                    returnValue = returnValue and .singleCompare(things[index], things[index + 1], things[index + 2]) 
     527                    if not returnValue, return returnValue 
     528                return returnValue 
  • Source/BinaryOpExpr.cobra

     
    5555                on 'IN' or 'NOTIN' 
    5656                    return InExpr(opToken, op, left, right) 
    5757                on 'IS' or 'ISNOT' or 'EQ' or 'NE' or 'LT' or 'GT' or 'LE' or 'GE' 
     58                    if left inherits CompareExpr 
     59                        left.addComparison(op, right) 
     60                        return left 
    5861                    return CompareExpr(opToken, op, left, right) 
    5962                on 'AND' or 'OR' or 'IMPLIES' 
    6063                    return BinaryBoolExpr(opToken, op, left, right) 
     
    528531        'IS': '==', 
    529532        'ISNOT': '!=', 
    530533    } 
     534     
     535    var _items as List<of Expr> 
     536    var _operations as List<of String> 
    531537 
    532538    cue init(opToken as IToken, op as String, left as Expr, right as Expr) 
    533539        base.init(opToken, op, left, right) 
     540        _items = List<of Expr>() 
     541        _items.add(left) 
     542        _items.add(right) 
     543        _operations = List<of String>() 
     544        _operations.add(op) 
    534545        if op == 'ISNOT' and .token.text == 'is' 
    535546            .token.text = 'is not' 
    536547 
     548    def addComparison(op as String, right as Expr) 
     549        _operations.add(op) 
     550        _items.add(right) 
     551     
    537552    def _bindImp 
    538553        base._bindImp 
    539554        _type = .compiler.boolType 
     555        if _items.count > 2 
     556            # this transformation is done so that the back ends handle chained comparisons differently than normal comparisons 
     557            cce = ChainedCompareExpr(.token, _items, _operations) 
     558            cce.bindImp 
     559            _transformTo(cce) 
     560            return 
    540561        if _left.type is nil or _right.type is nil 
    541562            assert .hasError 
    542563            return 
  • Tests/320-misc-two/256-chained-comparison.cobra

     
     1class Test 
     2     
     3    def main 
     4        assert 3 < 5 < 7 
     5         
     6        assert not 3 < 10 < 7 
     7         
     8        aLetter = 'b' 
     9        assert 'c' > aLetter <> 'q' 
     10         
     11        assert "cobra" <= 'is' >= 'cool' 
     12         
     13        a = 1 to dynamic? 
     14        b = 1 to dynamic 
     15        c = 1 
     16        d = 1 to ? 
     17        assert a == b == c == d >= 0 <= 76 
     18         
     19        assert b.getType is c.getType is d.getType is not aLetter.getType 
     20         
     21        fred = Stack<of int>() 
     22        fred.push(1) 
     23        fred.push(5) 
     24        fred.push(4) 
     25        fred.push(3) 
     26        assert fred.pop < fred.pop < fred.pop 
     27         
     28 No newline at end of file 
  • Tests/320-misc-two/258-chained-comparison-errors.cobra

     
     1class Test 
     2     
     3    enum MyEnum 
     4        burrito 
     5        carne 
     6        chili 
     7        enchilada 
     8        flan 
     9        taco 
     10     
     11    def main 
     12        result = 4 > MyEnum.carne == 'hi' # .error. cannot be compared 
     13        result = 'hi' > 4 <> MyEnum.carne # .error. cannot be compared 
     14        result = MyEnum.carne > 'hi' <> 4 # .error. cannot be compared 
     15        print result 
     16         
     17 No newline at end of file