Changeset 1728

Show
Ignore:
Timestamp:
11/03/08 23:34:16 (2 months ago)
Author:
Chuck.Esterbrook
Message:

Latest patch for multi-assignment.
ticket:38
credit:hopscc

Location:
cobra/trunk
Files:
2 added
3 modified

Legend:

Unmodified
Added
Removed
  • cobra/trunk/Developer/IntermediateReleaseNotes.text

    r1720 r1728  
    3131    %% error 'Cannot build this file till mondo-patch installed' 
    3232     
    33 * Added support for "multi-arg assignment" which allows you to assign to a number of variables from contents of a list in one statement 
     33* 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 
     34.code 
     35        a,b,c = 'val1', 999, c'\t' 
     36        d,e,f = 99+1, 100*1, 101-1 
     37        a,b = b,a   # one line swap 
     38or from contents of a list or array (any integer indexable item) in one statement 
    3439.code 
    3540        a,b,c = ['val1', 'val2', 'val3'] 
     41        d,e,f = @[99, 100, 101] 
    3642Also support such assignment in for statements for (generic) dictionaries and lists 
    3743.code 
  • cobra/trunk/Source/CobraParser.cobra

    r1723 r1728  
    27522752                        .throwError('Comma-separated assignment targets must end with "=", or this is a general syntax error.') 
    27532753                assignTkn = .last to ! 
    2754                 rhs = .expression 
    2755                 return MultiTargetAssignStatement(assignTkn, args, rhs) 
     2754                rhs as Expr? = .expression 
     2755                if .optional('COMMA') 
     2756                        rhsList = .commaSepExprs('EOL') 
     2757                        assert .last.which=='EOL' 
     2758                        .undo  # need EOL 
     2759                        rhsList.insert(0, rhs) 
     2760                        rhs = nil 
     2761                return MultiTargetAssignStatement(assignTkn, args, rhs, rhsList) 
    27562762                 
    27572763        def callExpr as Expr 
  • cobra/trunk/Source/Statements.cobra

    r1716 r1728  
    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 
    1405          
    1406         def init(opToken as IToken, args as List<of Expr>, rhs as Expr) 
     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 
     1411         
     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 
     
    14141422                .addField('targets', _targets) 
    14151423                .addField('source', _source) 
     1424                .addField('rvals', _rvals) 
    14161425                .addField('block', _block) 
    14171426 
     
    14201429        get source from var 
    14211430 
     1431        get rvals from var 
     1432 
    14221433        get block from var 
    14231434 
    1424         def _writeBlock 
    1425                 """ 
    1426                 Construct expanded assignment block for a multi target assignment. 
     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 
     1492                """ 
     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 
     
    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> 
     
    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