Wiki

Ticket #38: multi-arg-assign.patch

File multi-arg-assign.patch, 12.7 KB (added by hopscc, 16 years 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))