Changeset 1716
- Timestamp:
- 10/31/08 05:41:19 (2 months ago)
- Location:
- cobra/trunk
- Files:
-
- 2 added
- 5 modified
-
Source/CobraParser.cobra (modified) (6 diffs)
-
Source/SharpGenerator.cobra (modified) (1 diff)
-
Source/Statements.cobra (modified) (3 diffs)
-
Tests/320-misc-two/810-multi-assignment/110-multi-assign.cobra (modified) (5 diffs)
-
Tests/320-misc-two/810-multi-assignment/120-multi-assign-error.cobra (added)
-
Tests/320-misc-two/810-multi-assignment/122-multi-assign-error.cobra (added)
-
Tests/320-misc-two/810-multi-assignment/210-multi-arg-for-list-fail.cobra (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
cobra/trunk/Source/CobraParser.cobra
r1711 r1716 1884 1884 if .looksLikeType(0) and .looksLikeVarNameIsNext(1) 1885 1885 .throwError('The correct local variable syntax is "name as Type" or "name = initValue". Try "[.peek(1).text] as [.peek(0).text]."') 1886 if .looksLikeMultiArgIsNext 1887 s = .multiArgAssign 1888 else 1889 s = .expression 1890 s.afterParserRecognizesStatement 1891 1886 s = .expression 1887 s.afterParserRecognizesStatement 1888 if .optional('COMMA') 1889 s = .multiTargetAssign(s to Expr) 1892 1890 if expectEOL 1893 1891 if .verbosity>=5 … … 1993 1991 token = .expect('FOR') 1994 1992 varr = .nameExpr 1995 if .optional('COMMA') # forStmt multiarg for v1,...1996 args = .commaSep NameIds(['IN' ,'EOL'])1993 if .optional('COMMA') # forStmt multiarg for v1,... in 1994 args = .commaSepExprsPartial(['IN' ,'EOL'], 'IN') 1997 1995 if .last.which <> 'IN' 1998 1996 .throwError("Comma separated nameId list in forStatement needs to terminate with an IN token ('in')") … … 2714 2712 throw 2715 2713 2716 def multiArgAssign as Stmt 2717 # id, [id,]... = <expr> 2718 args = .commaSepIds(['ASSIGN', 'EOL']) 2714 def multiTargetAssign(arg0 as Expr) as Stmt 2715 # id, |[id,]... = <expr> 2716 args = .commaSepExprsPartial(['ASSIGN', 'EOL'], 'ASSIGN') 2717 args.insert(0, arg0) 2719 2718 if .last.which <> 'ASSIGN' 2720 .throwError('Comma separated identifier list needs to end with an assignment ("=").')2721 assignTkn = .last 2719 .throwError('Comma-separated assignment targets must end with "=", or this is a general syntax error.') 2720 assignTkn = .last to ! 2722 2721 rhs = .expression 2723 return .multiArgBlock(assignTkn, args, rhs) 2724 2725 def multiArgBlock(assignTkn as IToken?, args as List<of Expr>, rhs as Expr) as Stmt 2726 """ 2727 Construct expanded assignment block for a multi arg assignment. 2728 if less args than exprs in rhs - extra exprs (silently) ignored 2729 if more args than exprs in rhs - additional args are (silently) ignored (not changed) 2730 above differ fm python - both give errors 2731 Unchecked assumptions; 2732 rhs is indexable (x[n] == IList) - c# compiler pick up 2733 Assignment block contains 2734 try 2735 tmpId = <rhsExpr> 2736 id0 = tmpId[0] # id is contents of args[0] 2737 id1 = tmpId[1] # id is contents of args[0] 2738 (... repeated for number of items in args specifying ids) 2739 catch ArgumentOutOfRangeException 2740 pass 2741 """ 2742 stmts = List<of Stmt>() 2743 tmpName = 'lh_ma_[rhs.serialNum]' 2744 token = assignTkn.copy('XXX', 'xxx') # invalid token template for copying 2745 idToken = token.copy('ID', tmpName) 2722 return MultiTargetAssignStatement(assignTkn, args, rhs) 2746 2723 2747 tmpId = IdentifierExpr(idToken, idToken.text)2748 s = AssignExpr(assignTkn, assignTkn.which, tmpId, rhs)2749 stmts.add(s)2750 2751 count = 02752 for id in args2753 intToken = token.copy('INTEGER_LIT', '[count]')2754 intToken.value = count2755 countIntLit = IntegerLit(intToken)2756 exprs = List<of Expr>()2757 exprs.add(countIntLit)2758 tmpIdn = IdentifierExpr(token.copy('ID', tmpName), tmpName)2759 idxExpr = IndexExpr(token.copy('LBRACKET', r'['), tmpIdn, exprs)2760 stmts.add(AssignExpr(assignTkn, assignTkn.which, id , idxExpr))2761 count += 12762 return BlockStmt(token.copy('INDENT', ''), stmts)2763 2764 2724 def callExpr as Expr 2765 2725 """ … … 2813 2773 return expr 2814 2774 2775 def commaSepExprsPartial(terminators as IList<of String>, binOpBrk as String) as List<of Expr> 2776 # As commaSepExprs but setup to break out of middle of binOpExpression on terminating token 2777 assert _binaryOpPrec.containsKey(binOpBrk) 2778 realPrec = _binaryOpPrec[binOpBrk] 2779 _binaryOpPrec[binOpBrk] = -1 # reset precedence to exit expression parser when hit this op 2780 try 2781 exprs = .commaSepExprs(terminators) 2782 finally 2783 _binaryOpPrec[binOpBrk] = realPrec 2784 return exprs 2785 2815 2786 def commaSepExprs(terminator as String) as List<of Expr> 2816 2787 return .commaSepExprs([terminator], false, false) … … 2863 2834 expectSep = true 2864 2835 return exprs 2865 2866 def commaSepNameIds(terminators as IList<of String>) as List<of Expr>2867 return _commaSepLValues(terminators, true)2868 2869 def commaSepIds(terminators as IList<of String>) as List<of Expr>2870 return _commaSepLValues(terminators, false)2871 2872 def _commaSepLValues(terminators as IList<of String>, expectingNameIds as bool ) as List<of Expr>2873 """2874 pickup comma sep stream of Identifiers or NameIds2875 Example source2876 ... idexpr, TERMINATOR2877 ... idexpr, idexpr ... TERMINATOR2878 ... idexpr, idexpr, ... TERMINATOR2879 Returns2880 A list of Identifier Expressions ([DotExpr] IdentifiersExpr) or NameIds2881 """2882 expectSep = false2883 sep = 'COMMA'2884 exprs = List<of Expr>()2885 while true2886 if .peek.which in terminators2887 .grab2888 break2889 if expectSep2890 .expect(sep)2891 if .peek.which in terminators2892 .grab2893 break2894 .newOpStack2895 try2896 if expectingNameIds2897 exprs.add(.nameExpr)2898 else2899 exprs.add(.idExpression(0,nil))2900 finally2901 .delOpStack2902 expectSep = true2903 return exprs2904 2905 def idExpression(precedence as int, left as Expr?) as Expr2906 # Expression tree walker for Identifier or DotExpr Identifier Stream2907 # This method and next are simplified versions of expression and expression22908 # this code has to deal with scanning for tokens fo an expression that may be an identifier2909 # (and to stop on anything else)2910 if left is nil2911 left = .idExpression22912 while true2913 peek = .peek.which2914 # get precedence (and detect non-binary operators)2915 binaryOpPrec = Utils.getSI(_binaryOpPrec, peek, -1)2916 if peek <> 'DOT' # DOT is the only binary op allowed in Id stream2917 break2918 2919 opToken = .grab2920 op = opToken.which2921 assert _binaryOpPrec.containsKey(op)2922 _leftStack.push(left to !)2923 .opStack.push(op)2924 try2925 # get the right hand side of a binary operator expression2926 prec = if(OperatorSpecs.rightAssoc.containsKey(op), binaryOpPrec, binaryOpPrec+1)2927 if op == 'DOT' and .peek and .peek.isKeyword2928 # support foo.bar where bar is a keyword. Ex: foo.this2929 right = MemberExpr(.grab) to Expr2930 else2931 right = .idExpression(prec, nil)2932 left = DotExpr(opToken, op, left, right, false)2933 finally2934 .opStack.pop2935 _leftStack.pop2936 assert left2937 return left to !2938 2939 def idExpression2 as Expr2940 peekToken = .peek2941 peek = peekToken.which2942 if peek=='DOT' # leading dot2943 token = .grab2944 .opStack.push('DOT')2945 try2946 peekToken = .peek2947 peek = peekToken.which2948 if peek=='ID' or peekToken.isKeyword2949 memberToken = .idOrKeyword2950 expr = MemberExpr(memberToken) to Expr2951 else2952 .throwError('Syntax error after "." Expecting variable Id ')2953 finally2954 .opStack.pop2955 return BinaryOpExpr.make(token to !, 'DOT', ThisLit(token), expr)2956 else if peek=='ID'2957 return .identifierExpr2958 else if .opStack.count and .opStack.peek=='DOT' and .peek.isKeyword2959 return .identifierExpr2960 2961 msg = 'Expecting an Id expression.'2962 if peekToken.isKeyword2963 msg += ' "[peekToken.text]" is a reserved keyword that is not expected here.'2964 .throwError(msg)2965 assert false2966 return ThisLit(token) # To shut c# compiler up re paths without return values - notreached2967 2836 2968 2837 def forExpr as ForExpr … … 3439 3308 if token.which == 'ID' 3440 3309 return Utils.startsNonLower(token.value to String) 3441 if token.isKeyword and token.text in ['bool', 'char', 'decimal', 'int', 'uint', 'float', 'number'] 3442 return true 3443 return false 3310 return .isOneOfKeywords(token, ['bool', 'char', 'decimal', 'int', 'uint', 'float', 'number']) 3444 3311 3445 3312 def looksLikeVarNameIsNext(peekAhead as int) as bool 3446 3313 token = .peek(peekAhead) 3447 3314 return token is not nil and token.which=='ID' and Utils.startsWithLowerLetter(token.text) 3448 3449 def looksLikeMultiArgIsNext as bool 3450 """ 3451 peek ahead for ID (local or instance variable) COMMA or 3452 {DOT, ID}... (memberVar) COMMA 3453 """ 3454 state = 0 3455 peekAhead =0 3456 match = false 3457 while not match 3458 token = .peek(peekAhead) 3459 if token is nil 3460 break 3461 if token.which == 'DOT' 3462 state=1 3463 else if token.which == 'ID' 3464 state = 2 3465 else if token.which == 'COMMA' 3466 match = state == 2 # comma after [.]ID 3467 else 3468 break 3469 peekAhead += 1 3470 return match 3471 3315 3316 def isOneOfKeywords(nToken as IToken?, keywords as List<of String>) as bool 3317 token = nToken to ! 3318 return token.isKeyword and token.text in keywords 3319 3472 3320 def recordError(msg as String) as ParserException 3473 3321 return .recordError(.last, msg) -
cobra/trunk/Source/SharpGenerator.cobra
r1711 r1716 2874 2874 sw.write('yield return;\n') 2875 2875 2876 2877 class MultiTargetAssignStatement 2878 is partial 2879 2880 def writeSharpDef(sw as SharpWriter) 2881 base.writeSharpDef(sw) 2882 _block.writeSharpDef(sw) 2876 2883 2877 2884 ## -
cobra/trunk/Source/Statements.cobra
r1704 r1716 556 556 _var = .bindVar(_varExpr) 557 557 if _multiArgs 558 _validateMultiArgs 558 559 _unpackMultiArgStmts 559 560 base._bindImp … … 561 562 _block.bindImp 562 563 564 def _validateMultiArgs 565 n = 1 566 for arg in _multiArgs 567 if not (arg inherits IdentifierExpr or arg inherits AsExpr) 568 .throwError('Item "[arg.toCobraSource]" of comma-separated list is not an identifier or identifier-as-type.') 569 n += 1 570 563 571 def _unpackMultiArgStmts 564 572 """ … … 1376 1384 pass 1377 1385 curCodeMember.hasYieldStmt = true 1386 1387 1388 class MultiTargetAssignStatement 1389 is partial 1390 inherits Stmt 1391 """ 1392 Handle atatement of form 1393 a, b[,...] = <List> 1394 Gets turned into a sequence of assignExpr. 1395 a = <List>[0] 1396 b = <List>[1] 1397 1398 TODO: support a, b[,...] = <Enumerable> 1399 TODO: support a, b[,...] = <KeyValuePair|DictionaryEntry> 1400 TODO: support a, b[,...] = c,d[,...] form 1401 """ 1402 var _targets as List<of Expr> # Lvalues 1403 var _source as Expr # evaluate to IList/Array/String or otherwise indexable by int 1404 var _block as BlockStmt? # the rewritten/expanded code 1405 1406 def init(opToken as IToken, args as List<of Expr>, rhs as Expr) 1407 base.init(opToken) 1408 assert opToken.which == 'ASSIGN' 1409 _targets = args 1410 _source = rhs 1411 1412 def addSubFields 1413 base.addSubFields 1414 .addField('targets', _targets) 1415 .addField('source', _source) 1416 .addField('block', _block) 1417 1418 get targets from var 1419 1420 get source from var 1421 1422 get block from var 1423 1424 def _writeBlock 1425 """ 1426 Construct expanded assignment block for a multi target assignment. 1427 if fewer targets than exprs in _source - extra exprs (silently) ignored 1428 TODO: give error when rvalues are too many 1429 if more targets than exprs in _source - will emit runtime exception 1430 above differ fm python - both give errors 1431 Unchecked assumptions; 1432 source is int indexable (x[n]) - c# compiler pick up 1433 Assignment block contains 1434 lh_mt_serno = <source> 1435 id0 = lh_mt_serno[0] # id0 is contents of targets[0] 1436 id1 = lh_mt_serno[1] # id1 is contents of targets[1] 1437 (... repeated for number of items in targets specifying ids (lvalues)) 1438 """ 1439 stmts = List<of Stmt>() 1440 assignTkn = .lastToken # the one we stored in init 1441 serNo = .compiler.curBox.makeNextPrivateSerialNumber 1442 tmpName = 'lh_mt_[serNo]' # lh=local handler, mt = multiTarget 1443 ttoken = .lastToken.copy('XXX', 'xxx') # invalid template token for copying 1444 idToken = ttoken.copy('ID', tmpName) 1445 1446 tmpId = IdentifierExpr(idToken, idToken.text) 1447 s = AssignExpr(assignTkn, assignTkn.which, tmpId, _source) 1448 stmts.add(s) 1449 1450 count = 0 1451 for id in _targets 1452 intToken = ttoken.copy('INTEGER_LIT', '[count]') 1453 intToken.value = count 1454 countIntLit = IntegerLit(intToken) 1455 exprs = List<of Expr>() 1456 exprs.add(countIntLit) 1457 tmpIdn = IdentifierExpr(ttoken.copy('ID', tmpName), tmpName) 1458 idxExpr = IndexExpr(ttoken.copy('LBRACKET', r'['), tmpIdn, exprs) 1459 stmts.add(AssignExpr(assignTkn, assignTkn.which, id, idxExpr)) 1460 count += 1 1461 _block = BlockStmt(ttoken.copy('INDENT', ''), stmts) 1462 1463 def _isLValue(id as Expr) as bool 1464 # TODO: push to Expr.isLValue. probably require .didBindImp 1465 # TODO: for DotExpr and IndexExpr check if there is a setter 1466 # TODO: for MemberExpr check that its a setter property or visible var (not a method) 1467 return id inherits IdentifierExpr or id inherits DotExpr or id inherits IndexExpr 1468 1469 def _bindImp 1470 base._bindImp 1471 for target in _targets 1472 if not _isLValue(target) 1473 .throwError('"[target.toCobraSource]" is not an assignable lvalue (identifier, var, property or indexer).') 1474 # TODO: check _source supports an int indexer ?? 1475 _writeBlock 1476 _block.bindImp -
cobra/trunk/Tests/320-misc-two/810-multi-assignment/110-multi-assign.cobra
r1705 r1716 10 10 pro p0 from _i0 11 11 pro p1 from _i1 12 12 13 set [i as int] 14 branch i 15 on 0, .x0 = value 16 on 1, .x1 = value 17 else, throw ArgumentOutOfRangeException('[i] out of range 0,1') 18 13 19 def main is shared 14 20 ma = MultiAssign() 15 21 ma.nonLocalVar 16 22 ma.forIEnum 23 ma.forIndexor 17 24 ma.forSwap 18 25 … … 22 29 assert _i1 == 4 23 30 24 .x0, .x1 = ['03', '04']31 .x0, .x1 = ['03', '04'] 25 32 assert .x0 == '03' 26 33 assert .x1 == '04' … … 31 38 32 39 s = this 33 s.x0, s.x1 = ['031', '041']40 s.x0, s.x1 = ['031', '041'] 34 41 assert .x0 == '031' 35 42 assert .x1 == '041' … … 40 47 41 48 def methRetList as List<of String> 42 return ['a', 'b', 'c', 'd', 'e']49 return ['a', 'b', 'c', 'd', 'e'] 43 50 44 51 def forIEnum … … 48 55 assert b == 2 49 56 57 def forIndexor 58 this[0] = 'x' 59 this[1] = 'y' 60 assert .x0 == 'x' 61 assert .x1 == 'y' 62 63 this[0],this[1] = ['aa', 'bb'] 64 assert .x0 == 'aa' 65 assert .x1 == 'bb' 66 67 tx = this 68 tx[0], tx[1] = ['xa', 'xb'] 69 assert .x0 == 'xa' 70 assert .x1 == 'xb' 71 50 72 def forSwap 51 # common variable swap idiom ( python a,b = b,a)73 # common variable swap idiom (python a, b = b, a) 52 74 a = 99 53 75 b = 100 -
cobra/trunk/Tests/320-misc-two/810-multi-assignment/210-multi-arg-for-list-fail.cobra
r1705 r1716 5 5 def main is shared 6 6 ls = [1, 2, 3, 4, 5, 6] 7 for l1, l2 in ls # .error. Cannot find an indexer 7 # ls needs to be pairs i.e [[1,2],[3,4],[5,6]] 8 for l1, l2 in ls # .error. Cannot find an indexer 8 9 assert l1 in [1, 3, 5] 9 10 assert l2 in [2, 4, 6] 11 12 for a, 2 in [1, 2] # .error. is not 13 pass 14 15 for a, b, 'xx' in [1, 2] # .error. is not 16 pass
