Ticket #161: ticket161.patch
File ticket161.patch, 8.9 KB (added by eric.sellon, 15 years ago) |
---|
-
Source/Expr.cobra
3068 3068 else 3069 3069 .compiler.warning(this, 'The given expression is already non-nilable so "to !" is redundant. You can remove it.') # TODO: needs test case 3070 3070 _type = _expr.type 3071 3072 3073 class 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
4439 4439 throw FallThroughException(op) 4440 4440 4441 4441 4442 class 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 4442 4461 class DotExpr 4443 4462 is partial 4444 4463 -
Source/Cobra.Lang/CobraCore.cobra
499 499 for code in _htmlEncodes 500 500 s = s.replace(code[0], code[1]) 501 501 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
55 55 on 'IN' or 'NOTIN' 56 56 return InExpr(opToken, op, left, right) 57 57 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 58 61 return CompareExpr(opToken, op, left, right) 59 62 on 'AND' or 'OR' or 'IMPLIES' 60 63 return BinaryBoolExpr(opToken, op, left, right) … … 528 531 'IS': '==', 529 532 'ISNOT': '!=', 530 533 } 534 535 var _items as List<of Expr> 536 var _operations as List<of String> 531 537 532 538 cue init(opToken as IToken, op as String, left as Expr, right as Expr) 533 539 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) 534 545 if op == 'ISNOT' and .token.text == 'is' 535 546 .token.text = 'is not' 536 547 548 def addComparison(op as String, right as Expr) 549 _operations.add(op) 550 _items.add(right) 551 537 552 def _bindImp 538 553 base._bindImp 539 554 _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 540 561 if _left.type is nil or _right.type is nil 541 562 assert .hasError 542 563 return -
Tests/320-misc-two/256-chained-comparison.cobra
1 class 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
1 class 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