Ticket #38: multiArg-comma-sep.patch
File multiArg-comma-sep.patch, 11.4 KB (added by hopscc, 16 years ago) |
---|
-
Source/Statements.cobra
1383 1383 # TODO: can there just be "yield return"? 1384 1384 pass 1385 1385 curCodeMember.hasYieldStmt = true 1386 1387 1386 1388 1387 class MultiTargetAssignStatement 1389 1388 is partial 1390 1389 inherits Stmt 1391 1390 """ 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 1391 Handle statements of form 1392 a,[b,...] = <List-Generating-Expr> 1393 or 1394 a,[b,...] = <expr>,[<expr>,...] 1395 Gets turned into various sequences of assignExpr. 1396 tmp = <List-Generating-Expr> 1397 a = tmp[0] 1398 b = tmp[1] 1399 ... 1400 a = <expr> 1401 b = <expr> 1402 ... 1398 1403 TODO: support a, b[,...] = <Enumerable> 1399 1404 TODO: support a, b[,...] = <KeyValuePair|DictionaryEntry> 1400 TODO: support a, b[,...] = c,d[,...] form1401 1405 """ 1406 1402 1407 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 1408 var _source as Expr? # evaluate to IList/Array/String or otherwise indexable by int 1409 var _rvals as List<of Expr>? # list of source expressions 1410 var _block as BlockStmt? # the rewritten/expanded code 1405 1411 1406 def init(opToken as IToken, args as List<of Expr>, rhs as Expr )1412 def init(opToken as IToken, args as List<of Expr>, rhs as Expr?, rhsList as List<of Expr>?) 1407 1413 base.init(opToken) 1408 1414 assert opToken.which == 'ASSIGN' 1415 assert (rhs or rhsList) and not (rhs and rhsList) # either but not both 1409 1416 _targets = args 1410 1417 _source = rhs 1418 _rvals = rhsList 1411 1419 1412 1420 def addSubFields 1413 1421 base.addSubFields 1414 1422 .addField('targets', _targets) 1415 1423 .addField('source', _source) 1424 .addField('rvals', _rvals) 1416 1425 .addField('block', _block) 1417 1426 1418 1427 get targets from var 1419 1428 1420 1429 get source from var 1421 1430 1431 get rvals from var 1432 1422 1433 get block from var 1423 1434 1424 def _writeBlock 1435 def _bindImp 1436 assert _source or _rvals 1437 base._bindImp 1438 for target in _targets 1439 if not _isLValue(target) 1440 .throwError('"[target.toCobraSource]" is not an assignable lvalue (identifier, var, property or indexer).') 1441 if _rvals 1442 if _targets.count <> _rvals.count 1443 .throwError('The number of targets ([_targets.count]) must be the same as the number of expressions ([_rvals.count]) assigned to them') 1444 #If rvals are all safe to assign (no rerefs) we can do the simplest thing and turn 1445 # them into a sequence of individual assignments. i.e a,b = 1,2 or a,b = c,d cases 1446 # If not, we need to turn the rvals list into a literal list, pinning the current values, 1447 # and do the expansion on the list as a single item i.e a,b = b,a -> a,b = [b,a] 1448 if _isSafeToSimpleAssign() 1449 _writeBlockForList 1450 else 1451 _source = ListLit(.lastToken.copy('LBRACKET', r'['), _rvals) 1452 if _source 1453 #TODO: chk _source supports an int indexer somehow... 1454 #if not .can_be_indexed_by_int_indexer(_source) 1455 # .throwError(r"rhs expression [_source.toCobraSource] must support being indexed by an integer offset (e.g rhs[0]") 1456 _writeBlockForItem 1457 1458 _block.bindImp 1459 1460 def _isLValue(id as Expr) as bool 1461 # TODO: push to Expr.isLValue. probably require .didBindImp 1462 # TODO: for DotExpr and IndexExpr check if there is a setter 1463 # TODO: for MemberExpr check that its a setter property or visible var (not a method) 1464 return id inherits IdentifierExpr or id inherits DotExpr or id inherits IndexExpr 1465 1466 def _isSafeToSimpleAssign as bool 1467 # conservatively we just presume its safe if the rvals list is all Literals 1468 if all for e in _rvals get e inherits Literal 1469 return true 1470 # or if identifiers in rvals are not also in target 1471 if _disjointIdentifierLists() 1472 return true 1473 # There are possibly others but above expected to be most common usage 1474 # TODO: expand this for more wide ranging simple assignment e.g MemberExprs non matching 1475 return false 1476 1477 def _disjointIdentifierLists as bool 1478 # Specifically all of targets list are IdentifierExprs and rvals are Literals, DotExprs, 1479 # MemberExprs or Indentifiers and any Identifiers are not also in target list 1480 tnames = Set<of String>() 1481 for t in _targets 1482 if t inherits IdentifierExpr or t inherits AsExpr 1483 tnames.add((t to NameExpr).name) 1484 else 1485 return false 1486 if all for e in _rvals get e inherits Literal or e inherits DotExpr or e inherits MemberExpr or _ 1487 (e inherits IdentifierExpr and (e to IdentifierExpr).name not in tnames) 1488 return true 1489 return false 1490 1491 def _writeBlockForList 1425 1492 """ 1426 Construct expanded assignment block for a multi target assignment. 1493 Construct expanded assignment block for a multi target assignment from a list of expressions. 1494 Assignment block contains 1495 id0 = rvals[0] # id0 is contents of targets[0] 1496 id1 = rvals[1] # id1 is contents of targets[1] 1497 (... repeated for number of items in targets specifying ids (lvalues)) 1498 """ 1499 assert _targets.count == _rvals.count 1500 stmts = List<of Stmt>() 1501 assignTkn = .lastToken # the one we stored in init 1502 count = 0 1503 for id in _targets 1504 expr = _rvals[count] 1505 stmts.add(AssignExpr(assignTkn, assignTkn.which, id, expr)) 1506 count += 1 1507 _block = BlockStmt(.lastToken.copy('INDENT', ''), stmts) 1508 1509 def _writeBlockForItem 1510 """ 1511 Construct expanded assignment block for a multi target assignment from a single source expression. 1427 1512 if fewer targets than exprs in _source - extra exprs (silently) ignored 1428 1513 TODO: give error when rvalues are too many 1429 1514 if more targets than exprs in _source - will emit runtime exception 1430 1515 above differ fm python - both give errors 1431 1516 Unchecked assumptions; 1432 source is int indexable (x[n]) - c# compiler pick up 1517 source is int indexable (x[n]) - c# compiler pick up currently 1433 1518 Assignment block contains 1434 1519 lh_mt_serno = <source> 1435 1520 id0 = lh_mt_serno[0] # id0 is contents of targets[0] … … 1459 1544 stmts.add(AssignExpr(assignTkn, assignTkn.which, id, idxExpr)) 1460 1545 count += 1 1461 1546 _block = BlockStmt(ttoken.copy('INDENT', ''), stmts) 1462 1463 def _isLValue(id as Expr) as bool1464 # TODO: push to Expr.isLValue. probably require .didBindImp1465 # TODO: for DotExpr and IndexExpr check if there is a setter1466 # 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 IndexExpr1468 1469 def _bindImp1470 base._bindImp1471 for target in _targets1472 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 _writeBlock1476 _block.bindImp -
Source/CobraParser.cobra
2718 2718 if .last.which <> 'ASSIGN' 2719 2719 .throwError('Comma-separated assignment targets must end with "=", or this is a general syntax error.') 2720 2720 assignTkn = .last to ! 2721 rhs = .expression 2722 return MultiTargetAssignStatement(assignTkn, args, rhs) 2721 rhs as Expr? = .expression 2722 if .optional('COMMA') 2723 rhsList = .commaSepExprs('EOL') 2724 assert .last.which=='EOL' 2725 .undo # need EOL 2726 rhsList.insert(0, rhs) 2727 rhs = nil 2728 return MultiTargetAssignStatement(assignTkn, args, rhs, rhsList) 2723 2729 2724 2730 def callExpr as Expr 2725 2731 """ -
Tests/320-misc-two/810-multi-assignment/300-multiAssign-comma-sep.cobra
1 # tests for multiTarget assignment from commasep expression stream 2 class MAssign 3 4 var avar = 99 5 var bvar = 88 6 var _avar1 = 77 7 8 get prop 9 return 66 10 11 def main is shared 12 ma = MAssign() 13 ma.literals 14 ma.variables 15 ma.exprs 16 ma.asExpected 17 ma.swap 18 19 def literals 20 a,b,c = 1,2,3 21 assert a==1 22 assert b==2 23 assert c==3 24 25 x,y,z = 'this','is','silly' 26 assert x == 'this' 27 assert y == 'is' 28 assert z == 'silly' 29 30 x,y = 'a','b' 31 assert x == 'a' 32 assert y == 'b' 33 assert z == 'silly' 34 35 #mixed types 36 a,x,ch = 99,'wokka',c'x' 37 assert a == 99 38 assert x == 'wokka' 39 assert ch == c'x' 40 41 def variables 42 a = 22 43 b = 47 44 c = 76 45 46 d,e = a,b 47 assert d == 22 48 assert e == 47 49 50 d,e = .avar, .bvar 51 assert d == 99 52 assert e == 88 53 54 d,e = _avar1, 10 55 assert d == 77 56 assert e == 10 57 58 d,e = -1, _avar1 59 assert d == -1 60 assert e == 77 61 62 d,c = .prop, a 63 assert d == 66 64 assert c == 22 65 66 def exprs 67 a,b = 99+1,100-99 68 assert a == 100 69 assert b == 1 70 71 a = .geti(0) # OK 72 assert a ==2 73 74 a, = .geti(1), # OK - note trailing commas 75 assert a == 3 76 77 #a, = .geti(1) # typo missing trailing ',': peculiar error: cannot find an indexer 78 #assert a == 3 79 80 a,b = .geti(2), .geti(10) 81 assert a == 4 82 assert b == 12 83 84 a,b = 0,0 85 a,b = .geti(2), .geti(10) 86 assert a == 4 87 assert b == 12 88 89 def geti(i as int) as int 90 return i+2 91 92 def asExpected 93 # test expression values are pinned outside assignment stream 94 a,b = c=2,3 95 assert a == 2 96 assert c == 2 97 assert b == 3 98 99 a=100 100 a,b = a+1, a+2 101 assert a == 101 102 assert b == 102 103 104 c,c = 0,1 105 assert c == 1 106 107 def swap 108 a = 10 109 b = 20 110 assert a == 10 111 assert b == 20 112 113 a,b = b,a 114 assert a == 20 115 assert b == 10 116 117 118 119 -
Tests/320-misc-two/810-multi-assignment/310-multiAssign-comma-sep-fail.cobra
1 # failure cases for multiTarget assignment from commasep expression stream 2 class MAssign 3 def main is shared 4 5 a,b = 1,2,3 #.error. number of targets (2) must be the same as the number of expressions (3) 6 7 a,b,c = 1,2 #.error. number of targets (3) must be the same as the number of expressions (2) -
Developer/IntermediateReleaseNotes.text
17 17 .code 18 18 %% error 'Cannot build this file till mondo-patch installed' 19 19 20 * Added support for "multi-arg assignment" which allows you to assign to a number of variables from contents of a listin one statement20 * Added support for "multi-arg assignment" which allows you to assign to a number of variables from a compatible list of expressions in one statement 21 21 .code 22 a,b,c = 'val1', 999, c'\t' 23 d,e,f = 99+1, 100*1, 101-1 24 a,b = b,a # one line swap 25 or from contents of a list or array (any integer indexable item) in one statement 26 .code 22 27 a,b,c = ['val1', 'val2', 'val3'] 28 d,e,f = @[99, 100, 101] 23 29 Also support such assignment in for statements for (generic) dictionaries and lists 24 30 .code 25 31 for key, value in aDictionary