Wiki

Ticket #339: nil-safe-deref-updated.patch

File nil-safe-deref-updated.patch, 13.0 KB (added by hopscc, 5 years ago)
  • Source/Expr.cobra

     
    13531353    def setDefinition(value as INamedNode?) 
    13541354        # cannot override definition with a set, because base does not do a set 
    13551355        _definition = value 
     1356        # override use detection for compiler generated variables - not being set under normal checks 
     1357        if .isImplicit, _definition.isUsed = true 
     1358             
    13561359 
    13571360    get canBeStatement as bool is override 
    13581361        return _definition inherits Method 
     
    17781781    def _bindImp is override 
    17791782        base._bindImp 
    17801783        assert .superNode inherits DotExpr 
    1781         assert .binarySuperNode.op == 'DOT' 
     1784        assert .binarySuperNode.op == 'DOT' or .binarySuperNode.op == 'QUESTION_DOT' 
    17821785        assert .binarySuperNode.right is this 
    17831786        left = .binarySuperNode.left 
    17841787        if _definition is nil or _type is nil 
  • Source/BinaryOpExpr.cobra

     
    5050                    return AssignExpr(opToken, op, left, right) 
    5151                on 'DOT' 
    5252                    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 
    5392                on 'INHERITS' or 'IMPLEMENTS' 
    5493                    return InheritsExpr(opToken, op, left, right) 
    5594                on 'IN' or 'NOTIN' 
     
    633672 
    634673    cue init(opToken as IToken, op as String, left as Expr, right as Expr) 
    635674        require 
    636             op == 'DOT' 
     675            op == 'DOT' or op == 'QUESTION_DOT' 
    637676            right inherits IDotRightExpr  
    638677        body 
    639678            base.init(opToken, op, left, right) 
     
    10431082    cue init(opToken as IToken, op as String, left as Expr, right as Expr) 
    10441083        base.init(opToken, op, left, right) 
    10451084 
     1085    def addMinFields 
     1086        base.addMinFields 
     1087        .addField('left.toCobraSource', .left.toCobraSource) 
     1088        .addField('right.toCobraSource', .right.toCobraSource) 
     1089 
    10461090    def _bindImp 
    10471091        base._bindImp 
    10481092        left, right = .left, .right 
  • Source/Node.cobra

     
    11551155 
    11561156    def addMinFields 
    11571157        .addField('name', .name) 
     1158        .addField('isUsed', .isUsed) 
    11581159        base.addMinFields 
    11591160 
    11601161    get typeForIdentifier as IType is abstract 
  • Source/CobraParser.cobra

     
    27312731            'LBRACKET':         80, 
    27322732            'LPAREN':           80, 
    27332733            'ARRAY_OPEN':       80, 
     2734            'QUESTION_DOT':     80, 
    27342735 
    27352736            'STARSTAR':         70,  # right associative 
    27362737 
     
    29102911                        # support to expression. Ex: x to int  Ex: x to? Shape 
    29112912                        right = .typeExpr to Expr 
    29122913                        getTypeExprForRightHandSide = false 
    2913                     else if op == 'DOT' and .peek.isKeyword 
     2914                    else if (op == 'DOT' or op == 'QUESTION_DOT') and .peek.isKeyword 
    29142915                        # support foo.bar where bar is a keyword. Ex: foo.this 
    29152916                        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) 
    29172918                        # support foo.@help 
    29182919                        left.isHelpRequested = true 
    29192920                        op = ''  # cancel the creation of a new binary op expr 
    29202921                        .grab 
    29212922                    else 
    29222923                        right = _continuedExpression(prec, opToken) 
    2923                     if op == 'DOT' and not right inherits IDotRightExpr 
     2924                    if (op == 'DOT' or op == 'QUESTION_DOT') and not right inherits IDotRightExpr 
    29242925                        if left inherits StringLit or right inherits StringLit 
    29252926                            sugg = ' Use plus "+", not dot ".", for string concatenation.' 
    29262927                        else 
     
    33193320        """ 
    33203321        nameToken = .idOrKeyword 
    33213322        name = nameToken.text 
    3322         if .opStack.count and .opStack.peek=='DOT' 
     3323        if .opStack.count and (.opStack.peek=='DOT' or .opStack.peek=='QUESTION_DOT') 
    33233324            return MemberExpr(nameToken) 
    33243325        if .peek.which=='AS' 
    33253326            return AsExpr(.grab, nameToken, .typeId) 
  • Source/CobraTokenizer.cobra

     
    239239            r'DOUBLE_LT_EQUALS      s   <<=', 
    240240            r'DOUBLE_GT_EQUALS      s   >>=', 
    241241 
     242            r'QUESTION_DOT      s   ?.', 
    242243            r'QUESTION_EQUALS   s   ?=', 
    243244            r'BANG_EQUALS       s   !=', 
    244245        ] 
  • Tests/320-misc-two/410-nil-safe/020-nil-safe.cobra

     
     1class 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     
     37class 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 
     2class 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 
     49class 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 
     2class 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         
     48class Company 
     49    var name as String? 
     50    var subsid as Company? 
     51     
  • Tests/320-misc-two/410-nil-safe/010-simple-nil-safe.cobra

     
     1class 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     
     58class Company 
     59    var name as String? 
     60     
     61