Ticket #38: multi-arg-assign.patch

File multi-arg-assign.patch, 12.7 kB (added by hopscc, 3 months ago)
  • Source/CobraParser.cobra

     
    16651665                type as ITypeProxy? 
    16661666                if .peek.which == 'AS' 
    16671667                        .grab 
     1668                        # code below expresses an undocumented dependency and ordering 
     1669                        # allows only    VARI <type>  or  {OUT,INOUT} <type> 
     1670                        # i.e cant have VARI and {OUT,INOUT}, if have VARI or OUT,INOUT must specify type 
    16681671                        branch .peek.which 
    16691672                                on 'VARI' 
    16701673                                        type = VariTypeIdentifier(.grab, .typeId) 
     
    18421845                                else 
    18431846                                        if .looksLikeType(0) and .looksLikeVarNameIsNext(1) 
    18441847                                                .throwError('The correct local variable syntax is "name as Type" or "name = initValue". Try "[.peek(1).text] as [.peek(0).text]."') 
    1845                                         s = .expression 
    1846                                         s.afterParserRecognizesStatement 
     1848                                        if .looksLikeMultiArgIsNext 
     1849                                                s = .multiArgAssign  
     1850                                        else 
     1851                                                s = .expression 
     1852                                                s.afterParserRecognizesStatement 
     1853                                                         
    18471854                if expectEOL 
    18481855                        if .verbosity>=5 
    18491856                                print '<> last statement start token=[token]' 
     
    26502657                                else 
    26512658                                        throw 
    26522659 
     2660        # id, [id,]... = <expr> 
     2661        def multiArgAssign as Stmt 
     2662                args = .commaSepIds(['ASSIGN', 'EOL']) 
     2663                if .last.which <> 'ASSIGN' 
     2664                        .throwError("Comma separated multiArg identifier list needs to end with an assignment ('=')") 
     2665                assignTkn = .last 
     2666                rhs = .expression 
     2667                return .multiArgBlock(assignTkn, args, rhs) 
     2668         
     2669        def multiArgBlock(assignTkn as IToken?, args as List<of Expr>, rhs as Expr) as Stmt 
     2670                """ 
     2671                Construct expanded assignment block for a multi arg assignment. 
     2672                if less args than exprs in rhs - extra exprs (silently) ignored 
     2673                if more args than exprs in rhs  - additional args are (silently) ignored (not changed) 
     2674                        above differ fm python - both give errors 
     2675                Unchecked assumptions;  
     2676                        rhs is indexable (x[n] == IList) - c# compiler pick up 
     2677                Assignment block contains 
     2678                        try 
     2679                                tmpId = <rhsExpr> 
     2680                                id0 = tmpId[0] # id is contents of args[0] 
     2681                                id1 = tmpId[1] # id is contents of args[0] 
     2682                                        (... repeated for number of items in args specifying ids) 
     2683                        catch ArgumentOutOfRangeException 
     2684                                pass     
     2685                """ 
     2686                stmts = List<of Stmt>() 
     2687                tmpName = 'lh_ma_[rhs.serialNum]' 
     2688                token = assignTkn.copy('XXX', 'xxx')  # invalid token template for copying 
     2689                idToken = token.copy('ID', tmpName) 
     2690                 
     2691                tmpId = IdentifierExpr(idToken, idToken.text) 
     2692                s = AssignExpr(assignTkn, assignTkn.which, tmpId, rhs)  
     2693                stmts.add(s)   
     2694 
     2695                count=0 
     2696                for id in args 
     2697                        intToken = token.copy('INTEGER_LIT', '[count]') 
     2698                        intToken.value = count 
     2699                        countIntLit = IntegerLit(intToken) 
     2700                        exprs = List<of Expr>() 
     2701                        exprs.add(countIntLit) 
     2702                        tmpIdn = IdentifierExpr(token.copy('ID', tmpName), tmpName) 
     2703                        idxExpr = IndexExpr(token.copy('LBRACKET', r'['), tmpIdn, exprs) 
     2704                        stmts.add(AssignExpr(assignTkn, assignTkn.which, id , idxExpr)) 
     2705                        count += 1 
     2706                #return BlockStmt(token.copy('INDENT', ''), stmts)       
     2707                # if prefer exception when nArgs > nRHSExprs Uncomment above line and lose lines below  
     2708                tryBlock = BlockStmt(token.copy('INDENT',''), stmts)             
     2709                catchBlocks = List<of CatchBlock>() 
     2710                catchStmts = List<of Stmt>() 
     2711                catchStmts.add(PassStmt(token.copy('PASS', 'pass'))) 
     2712                catchBlock = BlockStmt(token.copy('INDENT',''), catchStmts) 
     2713                catchType = TypeIdentifier(token.copy('ID', 'IndexOutOfRangeException')) 
     2714                catchBlocks.add(CatchBlock(token.copy('CATCH', 'catch'), catchType, catchBlock)) 
     2715                # 'IndexOutOfRangeException' for Arrays and Strings  
     2716                catchType = TypeIdentifier(token.copy('ID', 'ArgumentOutOfRangeException')) 
     2717                catchBlocks.add(CatchBlock(token.copy('CATCH', 'catch'), catchType, catchBlock)) 
     2718                # 'ArgumentOutOfRangeException' for others, probably something else yet to find 
     2719                return TryStmt(token.copy('TRY', 'try'), tryBlock, catchBlocks, nil, nil) 
     2720                 
    26532721        def callExpr as Expr 
    26542722                """ 
    26552723                Syntax: 
     
    27512819                                .delOpStack 
    27522820                        expectSep = true 
    27532821                return exprs 
     2822                 
     2823        def commaSepIds(terminators as IList<of String>) as List<of Expr> 
     2824                """ 
     2825                Example source 
     2826                        ... idexpr, TERMINATOR 
     2827                        ... idexpr, idexpr ...  TERMINATOR 
     2828                        ... idexpr, idexpr, ... TERMINATOR 
     2829                Returns 
     2830                        A list of Identifier Expressions ([DotExpr] IdentifiersExpr)  
     2831                Notes 
     2832                        The terminator token is consumed, but can be examined with .last. 
     2833                """ 
     2834                expectSep = false 
     2835                sep = 'COMMA' 
     2836                exprs = List<of Expr>() 
     2837                while true 
     2838                        if .peek.which in terminators 
     2839                                .grab 
     2840                                break 
     2841                        if expectSep 
     2842                                .expect(sep) 
     2843                        if .peek.which in terminators 
     2844                                .grab 
     2845                                break 
     2846                        .newOpStack 
     2847                        try 
     2848                                exprs.add(.idExpression(0,nil)) 
     2849                        finally 
     2850                                .delOpStack 
     2851                        expectSep = true 
     2852                return exprs 
    27542853 
     2854        # Expression tree walker for Identifier or DotExpr Identifier Stream  
     2855        # This method and next are simplified versions of expression and expression2 
     2856        # this code has to deal with scanning for tokens fo an expression that may be an identifier  
     2857        # (and to stop on anything else) 
     2858        def idExpression(precedence as int, left as Expr?) as Expr 
     2859                if left is nil 
     2860                        left = .idExpression2 
     2861                while true 
     2862                        peek = .peek.which 
     2863                        # get precedence (and detect non-binary operators) 
     2864                        binaryOpPrec = Utils.getSI(_binaryOpPrec, peek, -1) 
     2865                        if peek <> 'DOT'    # DOT is the only binary op allowed in Id stream 
     2866                                break    
     2867 
     2868                        opToken = .grab 
     2869                        op = opToken.which 
     2870                        assert _binaryOpPrec.containsKey(op) 
     2871                        _leftStack.push(left to !) 
     2872                        .opStack.push(op) 
     2873                        try 
     2874                                # get the right hand side of a binary operator expression 
     2875                                prec = if(OperatorSpecs.rightAssoc.containsKey(op), binaryOpPrec, binaryOpPrec+1) 
     2876                                if op == 'DOT' and .peek and .peek.isKeyword 
     2877                                        # support foo.bar where bar is a keyword. Ex: foo.this 
     2878                                        right = MemberExpr(.grab) to Expr 
     2879                                else 
     2880                                        right = .idExpression(prec, nil) 
     2881                                        left = DotExpr(opToken, op, left, right, false) 
     2882                        finally 
     2883                                .opStack.pop 
     2884                                _leftStack.pop 
     2885                assert left 
     2886                return left to ! 
     2887                 
     2888        def idExpression2 as Expr 
     2889                peekToken = .peek 
     2890                peek = peekToken.which 
     2891                if peek=='DOT'          # leading dot 
     2892                        token = .grab 
     2893                        .opStack.push('DOT') 
     2894                        try 
     2895                                peekToken = .peek 
     2896                                peek = peekToken.which 
     2897                                if peek=='ID' or peekToken.isKeyword 
     2898                                        memberToken = .idOrKeyword 
     2899                                        expr = MemberExpr(memberToken) to Expr 
     2900                                else 
     2901                                        .throwError('Syntax error after "." Expecting variable Id ') 
     2902                        finally 
     2903                                .opStack.pop 
     2904                        return BinaryOpExpr.make(token to !, 'DOT', ThisLit(token), expr) 
     2905                else if peek=='ID' 
     2906                        return .identifierExpr 
     2907                else if .opStack.count and .opStack.peek=='DOT' and .peek.isKeyword 
     2908                        return .identifierExpr 
     2909                         
     2910                msg = 'Expecting an Id expression.' 
     2911                if peekToken.isKeyword 
     2912                        msg += ' "[peekToken.text]" is a reserved keyword that is not expected here.' 
     2913                .throwError(msg) 
     2914                assert false 
     2915                return ThisLit(token)  # To shut c# compiler up re paths without return values - notreached 
     2916                 
    27552917        def forExpr as ForExpr 
    27562918                """ 
    27572919                t = for x in stuff where x<0 get x*x 
     
    32333395                token = .peek(peekAhead) 
    32343396                return token is not nil and token.which=='ID' and Utils.startsWithLowerLetter(token.text) 
    32353397 
     3398        def looksLikeMultiArgIsNext as bool 
     3399                """ 
     3400                peek ahead for ID (local or instance variable) COMMA or  
     3401                {DOT, ID}... (memberVar) COMMA 
     3402                """ 
     3403                state = 0 
     3404                peekAhead =0 
     3405                match = false 
     3406                while not match 
     3407                        token = .peek(peekAhead) 
     3408                        if token is nil 
     3409                                break 
     3410                        if token.which == 'DOT' 
     3411                                state=1 
     3412                        else if token.which == 'ID' 
     3413                                if not (token.text[0] == '_' or Utils.startsWithLowerLetter(token.text)) 
     3414                                        break 
     3415                                state = 2 
     3416                        else if token.which == 'COMMA'  
     3417                                match = state == 2 # comma after [.]ID 
     3418                        else 
     3419                                break 
     3420                        peekAhead += 1 
     3421                return match 
     3422 
    32363423        def recordError(msg as String) as ParserException 
    32373424                return .recordError(.last, msg) 
    32383425 
  • Source/SharpGenerator.cobra

     
    22752275                        _ifInheritsVar.ifInheritsStack.pop 
    22762276                if close 
    22772277                        sw.dedent 
    2278                         sw.write('}\n') 
     2278                        sw.write(' }\n') 
    22792279 
    22802280 
    22812281class BreakStmt 
  • Tests/110-basics-two/911-multi-assign.cobra

     
     1# 
     2class MultiAssign 
     3        var _i0 as int 
     4        var _i1 as int 
     5 
     6        var x0 as String ='' 
     7        var x1 as String ='' 
     8         
     9        pro p0 from _i0 
     10        pro p1 from _i1 
     11                 
     12        def main is shared 
     13                ma=MultiAssign() 
     14                ma.nonLocalVar 
     15                ma.forIEnum 
     16 
     17        def nonLocalVar 
     18                _i0,_i1 = [3,4] 
     19                assert _i0 == 3 
     20                assert _i1 == 4 
     21                 
     22                .x0,.x1 = ['03','04'] 
     23                assert .x0 == '03' 
     24                assert .x1 == '04' 
     25         
     26                .x0,.x1 = .methRetList 
     27                assert .x0 == 'a' 
     28                assert .x1 == 'b' 
     29                 
     30                s = this 
     31                s.x0,s.x1 = ['031','041'] 
     32                assert .x0 == '031' 
     33                assert .x1 == '041' 
     34         
     35                .p0,.p1 = [910, 911] 
     36                assert .p0 == 910 
     37                assert .p1 == 911 
     38                 
     39        def methRetList as List<of String> 
     40                return ['a', 'b', 'c', 'd','e'] 
     41                 
     42        def forIEnum 
     43                lst = [1,2,3,4] 
     44                a,b = for k in lst get k  
     45                assert a == 1 
     46                assert b == 2 
     47                 
  • Tests/110-basics-two/910-multi-assign.cobra

     
     1# Tests for multiarg assignment from list, array and method to local vars 
     2class MultiAssign 
     3         
     4        def main is shared 
     5                ma=MultiAssign() 
     6                 
     7                ma.array         
     8                ma.list 
     9                ma.dict 
     10                ma.nonMatchCount 
     11 
     12        def array 
     13                a,b = @[1,2]   
     14                assert a==1 
     15                assert b==2 
     16         
     17                c,d,x = @['1','2','3'] 
     18                assert c=='1' 
     19                assert d=='2' 
     20                assert x=='3' 
     21                 
     22                e,f,g = @[1, 'Two', c'3'] 
     23                assert e ==1 
     24                assert f == 'Two' 
     25                assert g == c'3' 
     26                 
     27        def dict 
     28                # sorta interesting - resolves to a normal lookup by 0-offset  
     29                # numeric key in order of given variables.... 
     30                #x = {0:'a', 1:'b'} 
     31                #print x[0] 
     32 
     33                c,d,e = {0:'a', 1:'b', 2:'c', 3:'d'} 
     34                assert c == 'a' 
     35                assert d == 'b' 
     36                assert e == 'c' 
     37                # but if theres a key missing in the range -> KeyNotFoundException 
     38                 
     39                xdict = {0:'z', 1:'w', 3:'x'} 
     40                #for k in xdict.keys  
     41                #       print k 
     42                #for v in xdict.values 
     43                #       print v 
     44                keysColl = for k in xdict.keys get k  
     45                #print keysColl 
     46                f,g = keysColl 
     47                assert f==0 
     48                assert g==1 
     49                valsColl = for v in xdict.values get v  
     50                h,i = valsColl 
     51                assert h == 'z' 
     52                assert i == 'w' 
     53                #print valsColl 
     54 
     55                 
     56        def list 
     57                a,b = [1,2]   
     58                assert a==1 
     59                assert b==2 
     60 
     61                d,e = .func3   
     62                assert d=='a' 
     63                assert e=='b' 
     64                 
     65                # {:-)} 
     66                d1,d2 = .funcStr1 
     67                assert d1 == c'e' 
     68                assert d2 == c'h' 
     69                 
     70                ll = [21,22] 
     71                f,g = ll    
     72                assert f==21 
     73                assert g==22 
     74 
     75                ii,s = [99, 'STR'] 
     76                assert ii == 99 
     77                assert ii.getType is Int32 
     78                assert s == 'STR' 
     79         
     80                ls = List<of String>() 
     81                ls.add("one") 
     82                ls.add("two") 
     83                ls.add( "three") 
     84                s0,s1,s2 = ls 
     85                assert s0=='one' 
     86                assert s1=='two' 
     87                assert s2=='three' 
     88                 
     89        def nonMatchCount 
     90                a,b,c = [11,12] 
     91                assert a==11 
     92                assert b==12 
     93                assert c==0  # instantiated but unassigned 
     94 
     95                f, = [3,4] 
     96                assert f==3 
     97         
     98                ls = List<of String>() 
     99                ls.add("one") 
     100                ls.add("two") 
     101                ls.add( "three") 
     102                s0,s1 = ls 
     103                assert s0=='one' 
     104                assert s1=='two' 
     105         
     106                g,h = @['1','2','3'] 
     107                assert g=='1' 
     108                assert h=='2' 
     109         
     110                cd,dd = {0:'a', 1:'b', 3:'c'} 
     111                assert cd == 'a' 
     112                assert dd == 'b' 
     113                 
     114                l,m = @[1, 'Two', c'3'] 
     115                assert l ==1 
     116                assert m == 'Two' 
     117 
     118                m='' 
     119                c1 as Object = 'x' 
     120                l,m,n,c1 = @[1, 'Two', c'3'] 
     121                 
     122                assert l ==1 
     123                assert m == 'Two' 
     124                assert n == c'3' 
     125                assert c1 == 'x' 
     126         
     127                p,q,r = 'ab' 
     128                assert p == c'a' 
     129                assert q == c'b' 
     130                assert r == c'\0' 
     131                 
     132        def func3 as List<of String> is shared 
     133                return ['a', 'b', 'c', 'd','e'] 
     134                 
     135        def funcStr1 as String is shared 
     136                return 'eh?' 
     137 
     138                 
  • Developer/IntermediateReleaseNotes.text

     
    11Post 0.8 
    2  
     2* Added support for "multi-arg assignment" which allows you to assign to a number of variables  
     3    from contents of a list in one statement 
     4.code 
     5    a,b,c = ['val1', 'val2', 'val3'] 
     6     
    37* Added support for "extended initializers" which allow you to set properties of the object in the same call being used to create the object: 
    48.code 
    59        r = Rectangle(p1=Point(x=0, y=1), p2=Point(x=2, y=3))