Wiki

Ticket #38: multiArg-comma-sep.patch

File multiArg-comma-sep.patch, 11.4 KB (added by hopscc, 16 years ago)
  • Source/Statements.cobra

     
    13831383            # TODO: can there just be "yield return"? 
    13841384            pass 
    13851385        curCodeMember.hasYieldStmt = true 
    1386  
    1387  
     1386         
    13881387class MultiTargetAssignStatement 
    13891388    is partial 
    13901389    inherits Stmt 
    13911390    """ 
    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        ... 
    13981403    TODO: support a, b[,...] = <Enumerable>  
    13991404    TODO: support a, b[,...] = <KeyValuePair|DictionaryEntry> 
    1400     TODO: support a, b[,...] = c,d[,...] form 
    14011405    """ 
     1406 
    14021407    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 
    14051411     
    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>?) 
    14071413        base.init(opToken) 
    14081414        assert opToken.which == 'ASSIGN' 
     1415        assert (rhs or rhsList) and not (rhs and rhsList) # either but not both 
    14091416        _targets = args 
    14101417        _source = rhs 
     1418        _rvals = rhsList 
    14111419         
    14121420    def addSubFields 
    14131421        base.addSubFields 
    14141422        .addField('targets', _targets) 
    14151423        .addField('source', _source) 
     1424        .addField('rvals', _rvals) 
    14161425        .addField('block', _block) 
    14171426 
    14181427    get targets from var 
    14191428 
    14201429    get source from var 
    14211430 
     1431    get rvals from var 
     1432 
    14221433    get block from var 
    14231434 
    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 
    14251492        """ 
    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. 
    14271512        if fewer targets than exprs in _source - extra exprs (silently) ignored 
    14281513        TODO: give error when rvalues are too many 
    14291514        if more targets than exprs in _source   - will emit runtime exception 
    14301515            above differ fm python - both give errors 
    14311516        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 
    14331518        Assignment block contains 
    14341519            lh_mt_serno = <source> 
    14351520            id0 = lh_mt_serno[0] # id0 is contents of targets[0] 
     
    14591544            stmts.add(AssignExpr(assignTkn, assignTkn.which, id, idxExpr)) 
    14601545            count += 1 
    14611546        _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 
  • Source/CobraParser.cobra

     
    27182718        if .last.which <> 'ASSIGN' 
    27192719            .throwError('Comma-separated assignment targets must end with "=", or this is a general syntax error.') 
    27202720        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) 
    27232729         
    27242730    def callExpr as Expr 
    27252731        """ 
  • Tests/320-misc-two/810-multi-assignment/300-multiAssign-comma-sep.cobra

     
     1# tests for multiTarget assignment from commasep expression stream 
     2class 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 
     2class 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

     
    1717.code 
    1818    %% error 'Cannot build this file till mondo-patch installed' 
    1919     
    20 * Added support for "multi-arg assignment" which allows you to assign to a number of variables from contents of a list in one statement 
     20* 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 
    2121.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 
     25or from contents of a list or array (any integer indexable item) in one statement 
     26.code 
    2227    a,b,c = ['val1', 'val2', 'val3'] 
     28    d,e,f = @[99, 100, 101] 
    2329Also support such assignment in for statements for (generic) dictionaries and lists 
    2430.code 
    2531    for key, value in aDictionary