Ticket #38: multi-arg-assign.patch
File multi-arg-assign.patch, 12.7 KB (added by hopscc, 16 years ago) |
---|
-
Source/CobraParser.cobra
1665 1665 type as ITypeProxy? 1666 1666 if .peek.which == 'AS' 1667 1667 .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 1668 1671 branch .peek.which 1669 1672 on 'VARI' 1670 1673 type = VariTypeIdentifier(.grab, .typeId) … … 1842 1845 else 1843 1846 if .looksLikeType(0) and .looksLikeVarNameIsNext(1) 1844 1847 .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 1847 1854 if expectEOL 1848 1855 if .verbosity>=5 1849 1856 print '<> last statement start token=[token]' … … 2650 2657 else 2651 2658 throw 2652 2659 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 2653 2721 def callExpr as Expr 2654 2722 """ 2655 2723 Syntax: … … 2751 2819 .delOpStack 2752 2820 expectSep = true 2753 2821 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 2754 2853 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 2755 2917 def forExpr as ForExpr 2756 2918 """ 2757 2919 t = for x in stuff where x<0 get x*x … … 3233 3395 token = .peek(peekAhead) 3234 3396 return token is not nil and token.which=='ID' and Utils.startsWithLowerLetter(token.text) 3235 3397 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 3236 3423 def recordError(msg as String) as ParserException 3237 3424 return .recordError(.last, msg) 3238 3425 -
Source/SharpGenerator.cobra
2275 2275 _ifInheritsVar.ifInheritsStack.pop 2276 2276 if close 2277 2277 sw.dedent 2278 sw.write(' }\n')2278 sw.write(' }\n') 2279 2279 2280 2280 2281 2281 class BreakStmt -
Tests/110-basics-two/911-multi-assign.cobra
1 # 2 class 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 2 class 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
1 1 Post 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 3 7 * Added support for "extended initializers" which allow you to set properties of the object in the same call being used to create the object: 4 8 .code 5 9 r = Rectangle(p1=Point(x=0, y=1), p2=Point(x=2, y=3))