Wiki

Ticket #38: multi-arg-for.patch

File multi-arg-for.patch, 16.0 KB (added by hopscc, 16 years ago)
  • Source/Statements.cobra

     
    521521 
    522522    var _what as Expr 
    523523    var _varNumber as int 
     524    var _multiArgs as List<of Expr>?        # actually contains NameExprs 
    524525 
    525526    def init(token as IToken, varr as NameExpr, what as Expr, block as BlockStmt) 
    526527        base.init(token, varr, block) 
    527528        _what = what 
    528  
     529         
     530    #multiArg assignment in for stmt 
     531    def init(token as IToken, varr as NameExpr, args as List<of Expr>, what as Expr, block as BlockStmt) 
     532        .init(token, varr, what, block) 
     533        _multiArgs = args 
     534         
    529535    def addSubFields 
    530536        base.addSubFields 
    531537        .addField('_what', _what) 
    532538        .addField('_varNumber', _varNumber) 
     539        .addField('_multiArgs', _multiArgs) 
    533540 
    534541    get what from var 
    535542     
    536543    get varNumber from var 
    537544     
     545    get multiArgs from var 
     546     
    538547    def _bindImp 
    539548        _what.bindImp 
    540549        if _what.type.isDynamic 
     
    545554            base._bindImp  # just to pass the assertion that base._bindImp was invoked 
    546555        else 
    547556            _var = .bindVar(_varExpr) 
     557            if _multiArgs 
     558                _unpackMultiArgStmts 
    548559            base._bindImp 
    549560            _varNumber = .compiler.curBox.makeNextPrivateSerialNumber 
    550561            _block.bindImp 
     562         
     563    # perhaps this should be pushed up to Stmt 
     564    def _isDict(type as IType) as bool 
     565        if type inherits NilableType 
     566            return _isDict(type.theWrappedType to passthrough) 
    551567 
     568        if type.isDescendantOf(.compiler.idictionaryType) 
     569            return true 
     570        genericDict = .compiler.dictionaryOfType 
     571        if type.isDescendantOf(genericDict) 
     572            return true 
     573        if type inherits Box 
     574            if type.genericDef is genericDict 
     575                return true 
     576        genericIDict = .compiler.idictionaryOfType 
     577        if type.isDescendantOf(genericIDict) 
     578             
     579            return true 
     580        if type inherits Box 
     581            if type.genericDef is genericIDict 
     582                return true 
     583        if type.isDynamic 
     584            return true 
     585        return false 
     586         
     587    def _unpackMultiArgStmts 
     588        """ 
     589        Handle expansion of statements for multiArg variables in for statements:  
     590        for k,v in <dict>  becomes 
     591            for varExpr_uniq in <dict> 
     592                k = varExpr_uniq.key 
     593                v = varExpr_uniq.value 
     594        and  
     595        for a,b,c in <Inumerable (of triplets in this case)> becomes 
     596            for varExpr_uniq in <IEnumerable> 
     597                a = varExpr_uniq[0] 
     598                b = varExpr_uniq[1] 
     599                c = varExpr_uniq[2] 
     600        with rest of block following                 
     601        """ 
     602        stmts = List<of Stmt>() 
     603        varName = _varExpr.name # "[_varExpr.name]_[.serialNum]" 
     604        token = _token.copy 
     605        count=0 
     606        if _isDict(.what.type to !) 
     607            # assume enumerator will return KeyValue so cant have other than 2 multiargs 
     608            if _multiArgs.count <> 2 
     609                .throwError('Cannot have other than two multiarg variables in a for statement as assignment targets from a Dictionary') 
     610            kvRHS=[ 'key', 'value' ] 
     611            for id in _multiArgs 
     612                forVar =  IdentifierExpr(token.copy('ID', varName), varName) 
     613                rhsToken = token.copy('ID', kvRHS[count]) 
     614                keyOrVal =  MemberExpr(rhsToken ) 
     615                kvExpr = BinaryOpExpr.make(token.copy('DOT', '.'), 'DOT', forVar, keyOrVal) # forVar.{key,value} 
     616                assignTkn = token.copy('ASSIGN', '=') 
     617                #print id.toCobraSource, '=', kvExpr.toCobraSource 
     618                stmts.add(AssignExpr(assignTkn, assignTkn.which, id, kvExpr))  # id = forVar.{key,value} 
     619                count += 1 
     620        else # enumeration each item of which is matching count sequence 
     621            #TODO: ? Add check varName.count <> multArgs.count throw RTException 
     622            for id in _multiArgs 
     623                intToken = token.copy('INTEGER_LIT', '[count]') 
     624                intToken.value = count 
     625                countIntLit = IntegerLit(intToken) 
     626                exprs = List<of Expr>() 
     627                exprs.add(countIntLit) 
     628                forVar = IdentifierExpr(token.copy('ID', varName), varName) 
     629                idxExpr = IndexExpr(token.copy('LBRACKET', r'['), forVar, exprs)  # forVar[count] 
     630                assignTkn = token.copy('ASSIGN', '=') 
     631                stmts.add(AssignExpr(assignTkn, assignTkn.which, id, idxExpr))   
     632                count += 1 
     633        _block.stmts.insertRange(0, stmts) 
     634     
    552635    def inferredType as IType? is override 
    553636        assert _what.type 
    554637        return _what.type.innerType 
  • Source/Compiler.cobra

     
    12421242    def listOfType as Class 
    12431243        return _libraryClass('System.Collections.Generic.List<of>') 
    12441244 
    1245     def idictionaryOfType as Class 
    1246         return _libraryClass('System.Collections.Generic.IDictionary<of,>') 
     1245    def idictionaryType as Box 
     1246        return _libraryBox('System.Collections.IDictionary') 
     1247         
     1248    def idictionaryOfType as Box 
     1249        return _libraryBox('System.Collections.Generic.IDictionary<of,>') 
    12471250 
    12481251    def dictionaryOfType as Class 
    12491252        return _libraryClass('System.Collections.Generic.Dictionary<of,>') 
  • Source/CobraParser.cobra

     
    19901990    def forStmtBeginning as Stmt 
    19911991        """ 
    19921992        numeric    for int x = 0 up to n step 2 
    1993         enumerable for Customer cust in customers 
     1993        enumerable for cust as Customer in customers 
     1994                   for k,v in dict 
     1995                   for i,j,k in listOfThrees 
    19941996        """ 
    19951997        token = .expect('FOR') 
    19961998        varr = .nameExpr 
     1999        if .optional('COMMA')     # forStmt multiarg    for v1,...  
     2000            args = .commaSepNameIds(['IN' ,'EOL']) 
     2001            if .last.which <> 'IN' 
     2002                .throwError("Comma separated nameId list in forStatement needs to terminate with an IN token ('in')") 
     2003            if args.count == 0   # "for v1, in ..."  single multiarg variable  
     2004                .undo   # 'in' token 
     2005                return .forStmt(token, varr) 
     2006            else 
     2007                args.insert(0, varr) 
     2008                what = .expression 
     2009                name='forEnumVar[what.serialNum]'  # will get made more unique by For Stmt  
     2010                nameToken = token.copy('ID', name) 
     2011                varr =  IdentifierExpr(nameToken, name) 
     2012                return ForEnumerableStmt(token, varr, args, what, .block) 
     2013 
     2014        # forStmt single variable assign - for v {in,=} ... 
    19972015        peek = .peek.which 
    19982016        if peek=='ASSIGN' 
    19992017            return .oldNumericForStmt(token, varr) 
     
    20542072        else 
    20552073            # for x in stuff 
    20562074            return ForEnumerableStmt(token, varr, what, .block) 
    2057  
     2075             
    20582076    def ifStmt as Stmt 
    20592077        token = .expect('IF') 
    20602078        cond = .expression 
     
    26982716                    throw FallThroughException() 
    26992717                else 
    27002718                    throw 
    2701  
    27022719    def multiArgAssign as Stmt 
    27032720        # id, [id,]... = <expr> 
    27042721        args = .commaSepIds(['ASSIGN', 'EOL']) 
     
    27452762            idxExpr = IndexExpr(token.copy('LBRACKET', r'['), tmpIdn, exprs) 
    27462763            stmts.add(AssignExpr(assignTkn, assignTkn.which, id , idxExpr)) 
    27472764            count += 1 
    2748         #return BlockStmt(token.copy('INDENT', ''), stmts)   
    2749         # if prefer exception when nArgs > nRHSExprs Uncomment above line and lose lines below  
    2750         tryBlock = BlockStmt(token.copy('INDENT',''), stmts)         
    2751         catchBlocks = List<of CatchBlock>() 
    2752         catchStmts = List<of Stmt>() 
    2753         catchStmts.add(PassStmt(token.copy('PASS', 'pass'))) 
    2754         catchBlock = BlockStmt(token.copy('INDENT',''), catchStmts) 
    2755         catchType = TypeIdentifier(token.copy('ID', 'IndexOutOfRangeException')) 
    2756         catchBlocks.add(CatchBlock(token.copy('CATCH', 'catch'), catchType, catchBlock)) 
    2757         # 'IndexOutOfRangeException' for Arrays and Strings  
    2758         catchType = TypeIdentifier(token.copy('ID', 'ArgumentOutOfRangeException')) 
    2759         catchBlocks.add(CatchBlock(token.copy('CATCH', 'catch'), catchType, catchBlock)) 
    2760         # 'ArgumentOutOfRangeException' for others, probably something else yet to find 
    2761         return TryStmt(token.copy('TRY', 'try'), tryBlock, catchBlocks, nil, nil) 
     2765        return BlockStmt(token.copy('INDENT', ''), stmts)    
    27622766 
    27632767    def callExpr as Expr 
    27642768        """ 
     
    28622866            expectSep = true 
    28632867        return exprs 
    28642868         
    2865     def commaSepIds(terminators as IList<of String>) as List<of Expr> 
     2869    def commaSepNameIds(terminators as IList<of String>) as List<of Expr> 
     2870        return _commaSepLValues(terminators, true) 
     2871 
     2872    def  commaSepIds(terminators as IList<of String>) as List<of Expr> 
     2873        return _commaSepLValues(terminators, false) 
     2874             
     2875    def _commaSepLValues(terminators as IList<of String>, expectingNameIds as bool ) as List<of Expr> 
    28662876        """ 
     2877        pickup comma sep stream of Identifiers or NameIds  
    28672878        Example source 
    28682879            ... idexpr, TERMINATOR 
    28692880            ... idexpr, idexpr ...  TERMINATOR 
    28702881            ... idexpr, idexpr, ... TERMINATOR 
    28712882        Returns 
    2872             A list of Identifier Expressions ([DotExpr] IdentifiersExpr)  
    2873         Notes 
    2874             The terminator token is consumed, but can be examined with .last. 
     2883            A list of Identifier Expressions ([DotExpr] IdentifiersExpr) or NameIds  
    28752884        """ 
    28762885        expectSep = false 
    28772886        sep = 'COMMA' 
     
    28872896                break 
    28882897            .newOpStack 
    28892898            try 
    2890                 exprs.add(.idExpression(0, nil)) 
     2899                if expectingNameIds 
     2900                    exprs.add(.nameExpr) 
     2901                else 
     2902                    exprs.add(.idExpression(0,nil)) 
    28912903            finally 
    28922904                .delOpStack 
    28932905            expectSep = true 
     
    34523464            if token.which == 'DOT' 
    34533465                state=1 
    34543466            else if token.which == 'ID' 
    3455                 if not (token.text[0] == '_' or Utils.startsWithLowerLetter(token.text)) 
    3456                     break 
    34573467                state = 2 
    34583468            else if token.which == 'COMMA'  
    34593469                match = state == 2 # comma after [.]ID 
  • Tests/820-errors/300-statements/510-multi-arg-forList-fail.cobra

     
     1# multi arg assign in forEnum from non list of lists 
     2class ListTst 
     3    def main is shared 
     4        ListTst() 
     5         
     6    def init 
     7        ls = [1,2,3,4,5,6] 
     8        for l1,l2 in ls     # .error. Cannot find an indexer 
     9            assert l1 in [1,3,5] 
     10            assert l2 in [2,4,6] 
  • Tests/820-errors/300-statements/500-multi-arg-forDict-fail.cobra

     
     1class Dict 
     2    def main is shared 
     3        xdict = {0:'z', 1:'w', 3:'x'} 
     4        for k,v,c in xdict   # .error. other than two multiarg variables 
     5            pass 
  • Tests/110-basics-two/915-multi-arg-forDict.cobra

     
     1# Test multivariable assignment in forEnumeration from a Dictionary 
     2# this is oriented towards dicts of generics since HashTable enumerator 
     3# type inference is NYI 
     4class Dict 
     5    def main is shared 
     6        d = Dict() 
     7        d.manualAssign 
     8        d.multiAssign 
     9        d.twoArgs 
     10        d.dictGen 
     11        d.hashTblDict 
     12         
     13    def manualAssign 
     14        xdict = {0:'z', 1:'w', 3:'x'} 
     15        for kv in xdict 
     16            k = kv.key 
     17            v = kv.value 
     18            #print 'k=',k, 'v=',v 
     19            assert k in [0,1,3] 
     20            assert v in ['z', 'w', 'x'] 
     21 
     22    def multiAssign 
     23        xdict = {0:'z', 1:'w', 3:'x'} 
     24        for kv in xdict 
     25            k,v = [kv.key, kv.value] 
     26            #print 'k=',k, 'v=',v 
     27            assert k in [0,1,3] 
     28            assert v in ['z', 'w', 'x'] 
     29 
     30    def twoArgs 
     31        xdict = {0:'z', 1:'w', 3:'x'} 
     32        for k,v in xdict 
     33            #print 'k=',k, 'v=',v 
     34            assert k in [0,1,3] 
     35            assert v in ['z', 'w', 'x'] 
     36         
     37    def edgeCases 
     38        xdict = {0:'z', 1:'w', 3:'x'} 
     39        for kv, in xdict   # note trailing comma after kv 
     40            k,v = [kv.key, kv.value] 
     41            #print 'k=',k, 'v=',v 
     42            assert k in [0,1,3] 
     43            assert v in ['z', 'w', 'x'] 
     44         
     45        x1dict = {99:'zzz'} 
     46        for k,v in x1dict 
     47            pass 
     48        assert k == 99 
     49        assert v == 'zzz' 
     50     
     51        k2=994 
     52        x2dict = {:} 
     53        for k2,v2 in x2dict 
     54            pass 
     55        assert k2 == 994 
     56        assert v2 == nil 
     57         
     58    def dictGen 
     59        gdict = Dictionary<of String,int>() 
     60        gdict['a1'] = 1 
     61        gdict['b1'] = 2 
     62        gdict['c1'] = 3 
     63         
     64        for k,v in gdict 
     65        #for kv in gdict 
     66        #   k,v = [kv.key, kv.value] 
     67            assert k in ['a1','b1','c1'] 
     68            assert v in [1,2,3] 
     69             
     70    def hashTblDict 
     71        gdict = Hashtable() 
     72        gdict['a'] = 'z' 
     73        gdict['b'] = 'y' 
     74        gdict['c'] = 'w' 
     75        assert gdict['a'] == 'z' 
     76         
     77        for dev in gdict 
     78            de = dev to DictionaryEntry # necessary casting 
     79            k,v = [de.key, de.value] 
     80            assert k in ['a','b','c'] 
     81            assert v in ['z','y','w'] 
     82             
     83        # this not work till HashTable enumerator type inference implemented 
     84/#      for k,v in gdict 
     85            assert k in ['a','b','c'] 
     86            assert v in ['z','y','w'] 
     87#/           
  • Tests/110-basics-two/918-multi-arg-forList.cobra

     
     1#multiarg assignment in a for-statement on a List 
     2class ListTst 
     3    def main is shared 
     4        d = ListTst() 
     5        d.ma1 
     6        d.ooRange 
     7         
     8    def ma1 
     9        l2s = [[1,2],[3,4],[5,6]] 
     10        for l in l2s 
     11            l1,l2 = l 
     12            assert l1 in [1,3,5] 
     13            assert l2 in [2,4,6] 
     14            #print 'l1=[l1], l2=[l2]'            
     15        assert l1 == 5 
     16        assert l2 == 6 
     17     
     18        # existing variables 
     19        for l1,l2 in l2s 
     20            assert l1 in [1,3,5] 
     21            assert l2 in [2,4,6] 
     22        assert l1 == 5 
     23        assert l2 == 6 
     24         
     25        #new variables 
     26        for l3,l4 in l2s 
     27            assert l3 in [1,3,5] 
     28            assert l4 in [2,4,6] 
     29        assert l3 == 5 
     30        assert l4 == 6 
     31     
     32        for l5,l6 in [[0,1],[1,2],[2,3]] 
     33            assert l5 in [0,1,2] 
     34            assert l6 in [1,2,3] 
     35        assert l5 == 2 
     36        assert l6 == 3 
     37         
     38    def ooRange      
     39        l2s = [[1,2],[3,4],[5,6]] 
     40        expect ArgumentOutOfRangeException # ?? multi-arg list size mismatch? 
     41            for l1,l2,l3 in l2s 
     42                assert l1 in [1,3,5] 
     43                assert l2 in [2,4,6] 
     44                assert l3 == 0  
     45    # test for assignment from a flatlist done in 800-errors/300-statements 
  • Tests/110-basics-two/910-multi-assign.cobra

     
    8787        assert s2 == 'three' 
    8888         
    8989    def nonMatchCount 
    90         a, b, c = [11, 12] 
     90        expect ArgumentOutOfRangeException 
     91            a, b, c = [11, 12] 
    9192        assert a == 11 
    9293        assert b == 12 
    9394        assert c == 0  # instantiated but unassigned 
    94  
    9595        f, = [3, 4] 
    9696        assert f == 3 
    9797     
     
    117117 
    118118        m = '' 
    119119        c1 as Object = 'x' 
    120         l, m, n, c1 = @[1, 'Two', c'3'] 
     120        expect IndexOutOfRangeException 
     121            l, m, n, c1 = @[1, 'Two', c'3'] 
    121122         
    122123        assert l == 1 
    123124        assert m == 'Two' 
    124125        assert n == c'3' 
    125126        assert c1 == 'x' 
    126127     
    127         p, q, r = 'ab' 
     128        expect IndexOutOfRangeException 
     129            p, q, r = 'ab' 
    128130        assert p == c'a' 
    129131        assert q == c'b' 
    130132        assert r == c'\0' 
  • Tests/110-basics-two/912-multi-assign.cobra

     
    1313        ma = MultiAssign() 
    1414        ma.nonLocalVar 
    1515        ma.forIEnum 
     16        ma.forSwap 
    1617 
    1718    def nonLocalVar 
    1819        _i0, _i1 = [3,4] 
     
    4445        a, b = for k in list get k  
    4546        assert a == 1 
    4647        assert b == 2 
     48         
     49    def forSwap 
     50        # common variable swap idiom ( python a,b = b,a) 
     51        a = 99 
     52        b = 100 
     53        a,b = [b,a] 
     54        assert a == 100 
     55        assert b == 99 
     56         
  • Developer/IntermediateReleaseNotes.text

     
    1313* Added support for "multi-arg assignment" which allows you to assign to a number of variables from contents of a list in one statement 
    1414.code 
    1515    a,b,c = ['val1', 'val2', 'val3'] 
    16  
    17  
     16Also support such assignment in for statements for (generic) Dictionaries  
     17and Lists 
     18.code 
     19    for k,v in aDictionary 
     20        print 'key=[k]' 
     21    for a,b in [[1,2],[3,4]] 
     22        print a+b 
     23             
    1824* Added support for a new compiler directive 'args' that takes command line options and applies them to the compilation environment from that point on. 
    1925.code 
    2026    %% args -target:lib -embed-run-time:no