Ticket #38: multi-arg-for.patch
File multi-arg-for.patch, 16.0 KB (added by hopscc, 16 years ago) |
---|
-
Source/Statements.cobra
521 521 522 522 var _what as Expr 523 523 var _varNumber as int 524 var _multiArgs as List<of Expr>? # actually contains NameExprs 524 525 525 526 def init(token as IToken, varr as NameExpr, what as Expr, block as BlockStmt) 526 527 base.init(token, varr, block) 527 528 _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 529 535 def addSubFields 530 536 base.addSubFields 531 537 .addField('_what', _what) 532 538 .addField('_varNumber', _varNumber) 539 .addField('_multiArgs', _multiArgs) 533 540 534 541 get what from var 535 542 536 543 get varNumber from var 537 544 545 get multiArgs from var 546 538 547 def _bindImp 539 548 _what.bindImp 540 549 if _what.type.isDynamic … … 545 554 base._bindImp # just to pass the assertion that base._bindImp was invoked 546 555 else 547 556 _var = .bindVar(_varExpr) 557 if _multiArgs 558 _unpackMultiArgStmts 548 559 base._bindImp 549 560 _varNumber = .compiler.curBox.makeNextPrivateSerialNumber 550 561 _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) 551 567 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 552 635 def inferredType as IType? is override 553 636 assert _what.type 554 637 return _what.type.innerType -
Source/Compiler.cobra
1242 1242 def listOfType as Class 1243 1243 return _libraryClass('System.Collections.Generic.List<of>') 1244 1244 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,>') 1247 1250 1248 1251 def dictionaryOfType as Class 1249 1252 return _libraryClass('System.Collections.Generic.Dictionary<of,>') -
Source/CobraParser.cobra
1990 1990 def forStmtBeginning as Stmt 1991 1991 """ 1992 1992 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 1994 1996 """ 1995 1997 token = .expect('FOR') 1996 1998 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,=} ... 1997 2015 peek = .peek.which 1998 2016 if peek=='ASSIGN' 1999 2017 return .oldNumericForStmt(token, varr) … … 2054 2072 else 2055 2073 # for x in stuff 2056 2074 return ForEnumerableStmt(token, varr, what, .block) 2057 2075 2058 2076 def ifStmt as Stmt 2059 2077 token = .expect('IF') 2060 2078 cond = .expression … … 2698 2716 throw FallThroughException() 2699 2717 else 2700 2718 throw 2701 2702 2719 def multiArgAssign as Stmt 2703 2720 # id, [id,]... = <expr> 2704 2721 args = .commaSepIds(['ASSIGN', 'EOL']) … … 2745 2762 idxExpr = IndexExpr(token.copy('LBRACKET', r'['), tmpIdn, exprs) 2746 2763 stmts.add(AssignExpr(assignTkn, assignTkn.which, id , idxExpr)) 2747 2764 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) 2762 2766 2763 2767 def callExpr as Expr 2764 2768 """ … … 2862 2866 expectSep = true 2863 2867 return exprs 2864 2868 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> 2866 2876 """ 2877 pickup comma sep stream of Identifiers or NameIds 2867 2878 Example source 2868 2879 ... idexpr, TERMINATOR 2869 2880 ... idexpr, idexpr ... TERMINATOR 2870 2881 ... idexpr, idexpr, ... TERMINATOR 2871 2882 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 2875 2884 """ 2876 2885 expectSep = false 2877 2886 sep = 'COMMA' … … 2887 2896 break 2888 2897 .newOpStack 2889 2898 try 2890 exprs.add(.idExpression(0, nil)) 2899 if expectingNameIds 2900 exprs.add(.nameExpr) 2901 else 2902 exprs.add(.idExpression(0,nil)) 2891 2903 finally 2892 2904 .delOpStack 2893 2905 expectSep = true … … 3452 3464 if token.which == 'DOT' 3453 3465 state=1 3454 3466 else if token.which == 'ID' 3455 if not (token.text[0] == '_' or Utils.startsWithLowerLetter(token.text))3456 break3457 3467 state = 2 3458 3468 else if token.which == 'COMMA' 3459 3469 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 2 class 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
1 class 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 4 class 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 2 class 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
87 87 assert s2 == 'three' 88 88 89 89 def nonMatchCount 90 a, b, c = [11, 12] 90 expect ArgumentOutOfRangeException 91 a, b, c = [11, 12] 91 92 assert a == 11 92 93 assert b == 12 93 94 assert c == 0 # instantiated but unassigned 94 95 95 f, = [3, 4] 96 96 assert f == 3 97 97 … … 117 117 118 118 m = '' 119 119 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'] 121 122 122 123 assert l == 1 123 124 assert m == 'Two' 124 125 assert n == c'3' 125 126 assert c1 == 'x' 126 127 127 p, q, r = 'ab' 128 expect IndexOutOfRangeException 129 p, q, r = 'ab' 128 130 assert p == c'a' 129 131 assert q == c'b' 130 132 assert r == c'\0' -
Tests/110-basics-two/912-multi-assign.cobra
13 13 ma = MultiAssign() 14 14 ma.nonLocalVar 15 15 ma.forIEnum 16 ma.forSwap 16 17 17 18 def nonLocalVar 18 19 _i0, _i1 = [3,4] … … 44 45 a, b = for k in list get k 45 46 assert a == 1 46 47 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
13 13 * Added support for "multi-arg assignment" which allows you to assign to a number of variables from contents of a list in one statement 14 14 .code 15 15 a,b,c = ['val1', 'val2', 'val3'] 16 17 16 Also support such assignment in for statements for (generic) Dictionaries 17 and 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 18 24 * Added support for a new compiler directive 'args' that takes command line options and applies them to the compilation environment from that point on. 19 25 .code 20 26 %% args -target:lib -embed-run-time:no