Ticket #38: multi-arg-for.patch

File multi-arg-for.patch, 16.0 kB (added by hopscc, 3 months 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