Changeset 1704

Show
Ignore:
Timestamp:
10/25/08 00:42:49 (2 months ago)
Author:
Chuck.Esterbrook
Message:

Add support for multitarget assignment in for statements:

for key, value in aDictionary
    trace key, value
for a, b in [[1, 2], [3, 4]]
    print a + b

This is a patch application, part 1 of 2.
ticket:38
credit:hopscc

Location:
cobra/trunk
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • cobra/trunk/Developer/IntermediateReleaseNotes.text

    r1703 r1704  
    2121.code 
    2222        a,b,c = ['val1', 'val2', 'val3'] 
    23  
     23Also support such assignment in for statements for (generic) dictionaries and lists 
     24.code 
     25    for key, value in aDictionary 
     26        trace key, value 
     27    for a, b in [[1, 2], [3, 4]] 
     28        print a + b 
    2429 
    2530* Added support for a new compiler directive 'args' that takes command line options and applies them to the compilation environment from that point on. 
  • cobra/trunk/Source/CobraParser.cobra

    r1702 r1704  
    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' 
     
    27042722                args = .commaSepIds(['ASSIGN', 'EOL']) 
    27052723                if .last.which <> 'ASSIGN' 
    2706                         .throwError("Comma separated multiArg identifier list needs to end with an assignment ('=')") 
     2724                        .throwError('Comma separated identifier list needs to end with an assignment ("=").') 
    27072725                assignTkn = .last 
    27082726                rhs = .expression 
     
    27462764                        stmts.add(AssignExpr(assignTkn, assignTkn.which, id , idxExpr)) 
    27472765                        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) 
     2766                return BlockStmt(token.copy('INDENT', ''), stmts)        
    27622767 
    27632768        def callExpr as Expr 
     
    28632868                return exprs 
    28642869                 
    2865         def commaSepIds(terminators as IList<of String>) as List<of Expr> 
    2866                 """ 
     2870        def commaSepNameIds(terminators as IList<of String>) as List<of Expr> 
     2871                return _commaSepLValues(terminators, true) 
     2872 
     2873        def      commaSepIds(terminators as IList<of String>) as List<of Expr> 
     2874                return _commaSepLValues(terminators, false) 
     2875                         
     2876        def _commaSepLValues(terminators as IList<of String>, expectingNameIds as bool ) as List<of Expr> 
     2877                """ 
     2878                pickup comma sep stream of Identifiers or NameIds  
    28672879                Example source 
    28682880                        ... idexpr, TERMINATOR 
     
    28702882                        ... idexpr, idexpr, ... TERMINATOR 
    28712883                Returns 
    2872                         A list of Identifier Expressions ([DotExpr] IdentifiersExpr)  
    2873                 Notes 
    2874                         The terminator token is consumed, but can be examined with .last. 
     2884                        A list of Identifier Expressions ([DotExpr] IdentifiersExpr) or NameIds  
    28752885                """ 
    28762886                expectSep = false 
     
    28882898                        .newOpStack 
    28892899                        try 
    2890                                 exprs.add(.idExpression(0, nil)) 
     2900                                if expectingNameIds 
     2901                                        exprs.add(.nameExpr) 
     2902                                else 
     2903                                        exprs.add(.idExpression(0,nil)) 
    28912904                        finally 
    28922905                                .delOpStack 
     
    34533466                                state=1 
    34543467                        else if token.which == 'ID' 
    3455                                 if not (token.text[0] == '_' or Utils.startsWithLowerLetter(token.text)) 
    3456                                         break 
    34573468                                state = 2 
    34583469                        else if token.which == 'COMMA'  
  • cobra/trunk/Source/Compiler.cobra

    r1696 r1704  
    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 
  • cobra/trunk/Source/Statements.cobra

    r1699 r1704  
    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) 
     
    527528                _what = what 
    528529 
     530        def init(token as IToken, varr as NameExpr, args as List<of Expr>, what as Expr, block as BlockStmt) 
     531                # multiArg assignment in for stmt 
     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 
     544         
     545        get multiArgs from var 
    537546         
    538547        def _bindImp 
     
    546555                else 
    547556                        _var = .bindVar(_varExpr) 
     557                        if _multiArgs 
     558                                _unpackMultiArgStmts 
    548559                        base._bindImp 
    549560                        _varNumber = .compiler.curBox.makeNextPrivateSerialNumber 
    550561                        _block.bindImp 
    551  
     562                 
     563        def _unpackMultiArgStmts 
     564                """ 
     565                Handle expansion of statements for multiArg variables in for statements:  
     566                for k, v in <dict>  # becomes 
     567                        for varExpr_uniq in <dict> 
     568                                k = varExpr_uniq.key 
     569                                v = varExpr_uniq.value 
     570                and  
     571                for a, b, c in <IEnumerable (of triplets in this case)> becomes 
     572                        for varExpr_uniq in <IEnumerable> 
     573                                a = varExpr_uniq[0] 
     574                                b = varExpr_uniq[1] 
     575                                c = varExpr_uniq[2] 
     576                with rest of block following                             
     577                """ 
     578                stmts = List<of Stmt>() 
     579                varName = _varExpr.name # "[_varExpr.name]_[.serialNum]" 
     580                token = _token.copy 
     581                count = 0 
     582                if .what.type.isDictionaryLike 
     583                        # assume enumerator will return KeyValue so cant have other than 2 multiargs 
     584                        if _multiArgs.count <> 2 
     585                                .throwError('Cannot have other than two multiarg variables in a for statement as assignment targets from a Dictionary') 
     586                        kvRHS = ['key', 'value'] 
     587                        for id in _multiArgs 
     588                                forVar    = IdentifierExpr(token.copy('ID', varName), varName) 
     589                                rhsToken  = token.copy('ID', kvRHS[count]) 
     590                                keyOrVal  = MemberExpr(rhsToken ) 
     591                                kvExpr    = BinaryOpExpr.make(token.copy('DOT', '.'), 'DOT', forVar, keyOrVal) # forVar.{key,value} 
     592                                assignTkn = token.copy('ASSIGN', '=') 
     593                                # print id.toCobraSource, '=', kvExpr.toCobraSource 
     594                                stmts.add(AssignExpr(assignTkn, assignTkn.which, id, kvExpr))  # id = forVar.{key,value} 
     595                                count += 1 
     596                else  # enumeration, each item of which is matching count sequence 
     597                        # TODO: ? Add check varName.count <> multArgs.count throw RTException 
     598                        for id in _multiArgs 
     599                                intToken = token.copy('INTEGER_LIT', '[count]') 
     600                                intToken.value = count 
     601                                countIntLit = IntegerLit(intToken) 
     602                                exprs = List<of Expr>() 
     603                                exprs.add(countIntLit) 
     604                                forVar = IdentifierExpr(token.copy('ID', varName), varName) 
     605                                idxExpr = IndexExpr(token.copy('LBRACKET', r'['), forVar, exprs)  # forVar[count] 
     606                                assignTkn = token.copy('ASSIGN', '=') 
     607                                stmts.add(AssignExpr(assignTkn, assignTkn.which, id, idxExpr))   
     608                                count += 1 
     609                _block.stmts.insertRange(0, stmts) 
     610         
    552611        def inferredType as IType? is override 
    553612                assert _what.type 
  • cobra/trunk/Source/Types.cobra

    r1675 r1704  
    16031603 
    16041604 
     1605extend IType 
     1606 
     1607        def isDictionaryLike as bool 
     1608                """ 
     1609                Returns true if the receiver is like a dictionary, such as being an actual dictionary type or a nilable version thereof. 
     1610                The dynamic type will also return true because at compile-time, dynamic is considered to be like everything. 
     1611                Requires Node.getCompiler 
     1612                """ 
     1613                type = this 
     1614                if type inherits NilableType 
     1615                        return type.theWrappedType.isDictionaryLike 
     1616                tp = Node.getCompiler  # TODO? expand ITypeProvider to cover idictionaryType, dictionaryOfType, idictionaryOfType 
     1617                if type.isDescendantOf(tp.idictionaryType) 
     1618                        return true 
     1619                genericDict = tp.dictionaryOfType 
     1620                if type.isDescendantOf(genericDict) 
     1621                        return true 
     1622                if type inherits Box 
     1623                        if type.genericDef is genericDict 
     1624                                return true 
     1625                genericIDict = tp.idictionaryOfType 
     1626                if type.isDescendantOf(genericIDict) 
     1627                        return true 
     1628                if type inherits Box 
     1629                        if type.genericDef is genericIDict 
     1630                                return true 
     1631                if type.isDynamic 
     1632                        return true 
     1633                return false 
     1634 
     1635 
    16051636class TypeUtil 
    16061637         
  • cobra/trunk/Tests/110-basics-two/910-multi-assign.cobra

    r1669 r1704  
    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 
     
    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 
     
    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' 
  • cobra/trunk/Tests/110-basics-two/912-multi-assign.cobra

    r1669 r1704  
    1414                ma.nonLocalVar 
    1515                ma.forIEnum 
     16                ma.forSwap 
    1617 
    1718        def nonLocalVar 
     
    4142                 
    4243        def forIEnum 
    43                 list = [1, 2, 3, 4] 
     44                list = [1, 2, 3, 4]  # TODO: should be an error with too many elements 
    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