Ticket #339: nil-safe-deref-updated.patch
File nil-safe-deref-updated.patch, 13.0 KB (added by hopscc, 11 years ago) |
---|
-
Source/Expr.cobra
1353 1353 def setDefinition(value as INamedNode?) 1354 1354 # cannot override definition with a set, because base does not do a set 1355 1355 _definition = value 1356 # override use detection for compiler generated variables - not being set under normal checks 1357 if .isImplicit, _definition.isUsed = true 1358 1356 1359 1357 1360 get canBeStatement as bool is override 1358 1361 return _definition inherits Method … … 1778 1781 def _bindImp is override 1779 1782 base._bindImp 1780 1783 assert .superNode inherits DotExpr 1781 assert .binarySuperNode.op == 'DOT' 1784 assert .binarySuperNode.op == 'DOT' or .binarySuperNode.op == 'QUESTION_DOT' 1782 1785 assert .binarySuperNode.right is this 1783 1786 left = .binarySuperNode.left 1784 1787 if _definition is nil or _type is nil -
Source/BinaryOpExpr.cobra
50 50 return AssignExpr(opToken, op, left, right) 51 51 on 'DOT' 52 52 return DotExpr(opToken, op, left, right) 53 on 'QUESTION_DOT' 54 # Convert nilSafe deref sequence to a sequence of cascaded non-nil coalesce assignments which have 55 # the same effect but are a little tricky to code up correctly by hand. 56 # AST generated uses implicit (and inferred) temporary nilSafeHelper variables prefixed 57 # 'tnsh' to capture each possibly-nil expression to deref. 58 # There are two cases: 59 # 1) simple binary 2 expr: leftExpr?.rightExpr (like a?.b ) 60 # becomes (tnsh0 = leftExpr) ! (tnsh1 = tnsh0.rightExpr) 61 # 2) multiple cascaded ( like from a?.b?.c...): 62 # at the ?. operator the first part is already partially converted from 1) above 63 # (so the bit before the '?.' is already an inverse-coalesce-expr): 64 # i.e (tnsh0 = a) ! (tnsh1 = b) ?. c ... 65 # or more generally: 66 # leftInverseCoalesceExpr ?. rightExpr 67 # which becomes: 68 # leftInverseCoalesceExpr ! (tnsh2 = tnsh1.rightExpr) 69 # 70 # e.g a?.b?.c becomes (tnsh0 = a) ! (tnsh1 = b) ! (tnsh2 = tnsh1.c) 71 # a?.b?.c?.d becomes (tnsh0 = a) ! (tnsh1 = b) ! (tnsh2 = tnsh1.c) ! (tnsh3 = tnsh2.d) 72 # 73 bangToken = opToken.copy('BANG', '!') 74 serNo = IdentifierExpr(opToken).serialNum 75 tmpNameToken = opToken.copy('ID', 'tnsh_[serNo]' ) # tnsh = tmp nilsafe helper 76 leftId = IdentifierExpr(tmpNameToken) # new id to capture value of this expr tnsh0 77 leftId.isImplicit = true # variable is compiler generated 78 assToken = opToken.copy('ASSIGN', '=') 79 if left inherits InverseCoalesceExpr # previous InverseCoalesceExpr 80 prevId = (left.right to AssignExpr).left # tnsh1 81 dotex = DotExpr(opToken, 'DOT', prevId, right) # tnsh1.right 82 rhs = AssignExpr(assToken, 'ASSIGN', leftId, dotex) # tnsh2 = tnsh1.right 83 ice = InverseCoalesceExpr(bangToken, 'BANG', left, rhs) # (<prev-ice>) ! (tnsh2 = tnsh1.right) 84 else 85 lhs = AssignExpr(assToken, 'ASSIGN', leftId, left) #tnsh0 = left 86 serNo = IdentifierExpr(opToken).serialNum # for a different serNo 87 rightId = IdentifierExpr(opToken.copy('ID', 'tnsh_[serNo]')) 88 rightId.isImplicit = true 89 rhs = AssignExpr(assToken, 'ASSIGN', rightId, DotExpr(opToken, 'DOT', leftId, right)) # tnsh1 = tnsh0.right 90 ice = InverseCoalesceExpr(bangToken, 'BANG', lhs, rhs) # tnsh0 = left ! tnsh1 = tnsh0.right 91 return ice 53 92 on 'INHERITS' or 'IMPLEMENTS' 54 93 return InheritsExpr(opToken, op, left, right) 55 94 on 'IN' or 'NOTIN' … … 633 672 634 673 cue init(opToken as IToken, op as String, left as Expr, right as Expr) 635 674 require 636 op == 'DOT' 675 op == 'DOT' or op == 'QUESTION_DOT' 637 676 right inherits IDotRightExpr 638 677 body 639 678 base.init(opToken, op, left, right) … … 1043 1082 cue init(opToken as IToken, op as String, left as Expr, right as Expr) 1044 1083 base.init(opToken, op, left, right) 1045 1084 1085 def addMinFields 1086 base.addMinFields 1087 .addField('left.toCobraSource', .left.toCobraSource) 1088 .addField('right.toCobraSource', .right.toCobraSource) 1089 1046 1090 def _bindImp 1047 1091 base._bindImp 1048 1092 left, right = .left, .right -
Source/Node.cobra
1155 1155 1156 1156 def addMinFields 1157 1157 .addField('name', .name) 1158 .addField('isUsed', .isUsed) 1158 1159 base.addMinFields 1159 1160 1160 1161 get typeForIdentifier as IType is abstract -
Source/CobraParser.cobra
2731 2731 'LBRACKET': 80, 2732 2732 'LPAREN': 80, 2733 2733 'ARRAY_OPEN': 80, 2734 'QUESTION_DOT': 80, 2734 2735 2735 2736 'STARSTAR': 70, # right associative 2736 2737 … … 2910 2911 # support to expression. Ex: x to int Ex: x to? Shape 2911 2912 right = .typeExpr to Expr 2912 2913 getTypeExprForRightHandSide = false 2913 else if op == 'DOT'and .peek.isKeyword2914 else if (op == 'DOT' or op == 'QUESTION_DOT') and .peek.isKeyword 2914 2915 # support foo.bar where bar is a keyword. Ex: foo.this 2915 2916 right = MemberExpr(.grab) to Expr 2916 else if op == 'DOT'and _isHelpDirective(.peek)2917 else if (op == 'DOT' or op == 'QUESTION_DOT') and _isHelpDirective(.peek) 2917 2918 # support foo.@help 2918 2919 left.isHelpRequested = true 2919 2920 op = '' # cancel the creation of a new binary op expr 2920 2921 .grab 2921 2922 else 2922 2923 right = _continuedExpression(prec, opToken) 2923 if op == 'DOT'and not right inherits IDotRightExpr2924 if (op == 'DOT' or op == 'QUESTION_DOT') and not right inherits IDotRightExpr 2924 2925 if left inherits StringLit or right inherits StringLit 2925 2926 sugg = ' Use plus "+", not dot ".", for string concatenation.' 2926 2927 else … … 3319 3320 """ 3320 3321 nameToken = .idOrKeyword 3321 3322 name = nameToken.text 3322 if .opStack.count and .opStack.peek=='DOT'3323 if .opStack.count and (.opStack.peek=='DOT' or .opStack.peek=='QUESTION_DOT') 3323 3324 return MemberExpr(nameToken) 3324 3325 if .peek.which=='AS' 3325 3326 return AsExpr(.grab, nameToken, .typeId) -
Source/CobraTokenizer.cobra
239 239 r'DOUBLE_LT_EQUALS s <<=', 240 240 r'DOUBLE_GT_EQUALS s >>=', 241 241 242 r'QUESTION_DOT s ?.', 242 243 r'QUESTION_EQUALS s ?=', 243 244 r'BANG_EQUALS s !=', 244 245 ] -
Tests/320-misc-two/410-nil-safe/020-nil-safe.cobra
1 class NilSafe 2 var co as Company? 3 var co1 as Company? 4 var ccnil as Object? 5 6 def main is shared 7 ns = NilSafe() 8 co = Company() 9 co.name ='H Die and Tool' 10 ns.co = co 11 12 assert ns.co.name == co.name 13 name = ns.co ! ns.co.name ? 'x' 14 assert name == co.name 15 16 name = ns.co1?.name ? 'no-Name?' 17 # name = (t=ns.co1) : t.name ) 18 assert name == 'no-Name?' 19 20 #name = (t=ns.co) ! t.name ? 'x' 21 #assert name == co.name 22 23 # inverse coalesce in similar form to what the ?. operator generates 24 ns1 as NilSafe? 25 name = ((t0=ns1) ! (t1=t0.co1)) ! t1.name ? 'no.Name?' 26 assert name == 'no.Name?' 27 28 # nilsafe deref ops 29 name = ns1?.co1?.name ? 'no.Name?' 30 assert name == 'no.Name?' 31 32 def x 33 pass 34 35 invariant .ccnil == nil 36 37 class Company 38 var name as String? 39 40 -
Tests/320-misc-two/410-nil-safe/040-nil-safe-non-idempotent.cobra
1 # non idempotent: calls changing return values on each call 2 class NilSafe 3 var co as Company? 4 var co1 as Company? 5 6 def main is shared 7 ns = NilSafe() 8 co = Company() 9 co.name ='Hops Die and Tool' 10 ns.co = co 11 12 13 #print ns.co.chkCo ? 'chkFail' 14 #print ns.co.chkCo ? 'chkFail' 15 16 # simplifid safe-deref using inverse coalesce ops 17 ns2 = ns to ? 18 r = ns2 ! ns2.co ! ns2.co.chkCo ? 'chkFail' 19 assert r == 'OK' 20 r = ns2 ! ns2.co ! ns2.co.chkCo ? 'chkFail' 21 assert r == 'chkFail' 22 23 ns2 =NilSafe() 24 n = ns2.coCalc.name 25 assert n == 'Synth1' 26 n = ns2.coCalc.name 27 assert n == 'Synth2' 28 # ns2?.coCalc?.name 29 #n1 = ns2 ! ns2.coCalc ! ns2.coCalc.name # simplified 30 n1 = (t=ns2) ! (t1=t.coCalc) ! t1.name # similar to that generated by ?. op 31 assert n1 == 'Synth3' 32 ## ns2.coCalc?.name 33 n1 = ns2 ! (u=ns2.coCalc) ! u.name 34 assert n1 == 'Synth4' 35 36 # 37 # same as above but using nilsafe deref op 38 # 39 n1 = ns2?.coCalc?.name 40 assert n1 == 'Synth5' 41 #print n1 42 n1 = ns2.coCalc?.name 43 assert n1 == 'Synth6' 44 #print n1 45 46 def coCalc as Company? 47 return .co1 ? Company(dname='Synth[Company.cnt]') 48 49 class Company 50 shared 51 var cnt = 0 52 var name as String? ='' 53 var n =0 54 55 set dname from name 56 57 cue init 58 base .init 59 Company.cnt += 1 60 61 def chkCo as String? 62 n = .n 63 .n += 1 64 if n ==0, return 'OK' 65 return nil 66 67 def chkN as int 68 n, .n = .n, .n+1 69 return n -
Tests/320-misc-two/410-nil-safe/030-many-nil-safe.cobra
1 # cascaded nilsafe deref operators to a ridiculous depth, access and mutate 2 class NilSafe 3 var co as Company? 4 var co1 as Company? 5 6 def main is shared 7 ns = NilSafe() 8 co = Company() 9 co.name ='Die and Tool' 10 co.subsid = Company() 11 co.subsid.name = 'Just Tool' 12 ns.co = co 13 14 assert ns.co.name == co.name 15 name = ns.co1?.name ? 'X?' 16 assert name == 'X?' 17 18 n2= ns.co1 ! ns.co1.name ? 'X!' 19 assert n2 == 'X!' 20 21 ns1 as NilSafe? 22 name = ns1?.co1?.name ? 'X?' 23 assert name == 'X?' 24 name = ns.co?.name ? 'X' 25 assert name == co.name 26 27 ssname = ns.co?.subsid?.name ? 'X' 28 assert ssname == 'Just Tool' 29 ns.co?.subsid.subsid =Company() 30 ns.co.subsid.subsid.name= 'Justy' 31 ns1 = ns 32 name = ns1?.co?.subsid?.subsid?.name ? 'X' 33 assert name == 'Justy' 34 35 ns1.co.subsid.subsid.name = nil 36 name = ns1?.co?.subsid?.subsid?.name ? 'X' 37 assert name == 'X' 38 ns1.co.subsid.subsid = nil 39 name = ns1?.co?.subsid?.subsid?.name ? 'X' 40 assert name == 'X' 41 42 coX = ns.co?.subsid?.subsid ? co 43 assert coX.name == 'Die and Tool' 44 45 coX1 = ns1?.co?.subsid?.subsid ? co 46 assert (coX1 to Company).name == 'Die and Tool' 47 48 class Company 49 var name as String? 50 var subsid as Company? 51 -
Tests/320-misc-two/410-nil-safe/010-simple-nil-safe.cobra
1 class NilSafe 2 var co as Company? 3 var co1 as Company? 4 var ccnil as Object? 5 6 def main is shared 7 ns = NilSafe() 8 co = Company() 9 co.name ='H Die and Tool' 10 ns.co = co 11 12 assert ns.co.name == co.name 13 14 # In each test pair below 15 # first is a nilsafe deref using hand created cascaded non nil coalesce expressions 16 # indicating nilsafe deref is possible without using the specific operator 17 # second is the same check using nilsafe deref operator (less duplication and all round nicer) 18 19 name = ns.co ! ns.co.name ? 'x' 20 assert name == co.name 21 22 name = ns.co1?.name ? 'no-Name?' 23 assert name == 'no-Name?' 24 25 n2= ns.co1 ! ns.co1.name ? 'noName!' 26 assert n2 == 'noName!' 27 28 ns1 as NilSafe? 29 name = ns1?.co1?.name ? 'no.Name?' 30 assert name == 'no.Name?' 31 32 n2 = ns1 ! ns1.co1 ! ns.co1.name ? 'no.Name!' 33 assert n2 == 'no.Name!' 34 35 ns1 = NilSafe() 36 name = ns1?.co1?.name ? 'no.Name-co?' 37 assert name == 'no.Name-co?' 38 39 n2 = ns1 ! ns1.co1 ! ns1.co1.name ? 'no.Name-co!' 40 assert n2 == 'no.Name-co!' 41 42 ns1.co1 = Company() 43 name = ns1?.co1?.name ? 'no.Name-co-name?' 44 assert name == 'no.Name-co-name?' 45 46 n2 = ns1 ! ns1.co1 ! ns1.co1.name ? 'no.Name-co-name!' 47 assert n2 == 'no.Name-co-name!' 48 49 ns1.co1.name = 'xx' 50 name = ns1?.co1?.name ? 'no.Name-co-name?' 51 assert name == 'xx' 52 53 def x 54 pass 55 56 invariant .ccnil == nil 57 58 class Company 59 var name as String? 60 61