Changeset 1716

Show
Ignore:
Timestamp:
10/31/08 05:41:19 (2 months ago)
Author:
Chuck.Esterbrook
Message:

Multi-assignment improvements.
ticket:38
credit:hopscc

Location:
cobra/trunk
Files:
2 added
5 modified

Legend:

Unmodified
Added
Removed
  • cobra/trunk/Source/CobraParser.cobra

    r1711 r1716  
    18841884                                        if .looksLikeType(0) and .looksLikeVarNameIsNext(1) 
    18851885                                                .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) 
    18921890                if expectEOL 
    18931891                        if .verbosity>=5 
     
    19931991                token = .expect('FOR') 
    19941992                varr = .nameExpr 
    1995                 if .optional('COMMA')     # forStmt multiarg    for v1,...  
    1996                         args = .commaSepNameIds(['IN' ,'EOL']) 
     1993                if .optional('COMMA')  # forStmt multiarg for v1,... in  
     1994                        args = .commaSepExprsPartial(['IN' ,'EOL'], 'IN') 
    19971995                        if .last.which <> 'IN' 
    19981996                                .throwError("Comma separated nameId list in forStatement needs to terminate with an IN token ('in')") 
     
    27142712                                        throw 
    27152713 
    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) 
    27192718                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 ! 
    27222721                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) 
    27462723                 
    2747                 tmpId = IdentifierExpr(idToken, idToken.text) 
    2748                 s = AssignExpr(assignTkn, assignTkn.which, tmpId, rhs)  
    2749                 stmts.add(s)   
    2750  
    2751                 count = 0 
    2752                 for id in args 
    2753                         intToken = token.copy('INTEGER_LIT', '[count]') 
    2754                         intToken.value = count 
    2755                         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 += 1 
    2762                 return BlockStmt(token.copy('INDENT', ''), stmts)        
    2763  
    27642724        def callExpr as Expr 
    27652725                """ 
     
    28132773                return expr 
    28142774 
     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                         
    28152786        def commaSepExprs(terminator as String) as List<of Expr> 
    28162787                return .commaSepExprs([terminator], false, false) 
     
    28632834                        expectSep = true 
    28642835                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 NameIds  
    2875                 Example source 
    2876                         ... idexpr, TERMINATOR 
    2877                         ... idexpr, idexpr ...  TERMINATOR 
    2878                         ... idexpr, idexpr, ... TERMINATOR 
    2879                 Returns 
    2880                         A list of Identifier Expressions ([DotExpr] IdentifiersExpr) or NameIds  
    2881                 """ 
    2882                 expectSep = false 
    2883                 sep = 'COMMA' 
    2884                 exprs = List<of Expr>() 
    2885                 while true 
    2886                         if .peek.which in terminators 
    2887                                 .grab 
    2888                                 break 
    2889                         if expectSep 
    2890                                 .expect(sep) 
    2891                         if .peek.which in terminators 
    2892                                 .grab 
    2893                                 break 
    2894                         .newOpStack 
    2895                         try 
    2896                                 if expectingNameIds 
    2897                                         exprs.add(.nameExpr) 
    2898                                 else 
    2899                                         exprs.add(.idExpression(0,nil)) 
    2900                         finally 
    2901                                 .delOpStack 
    2902                         expectSep = true 
    2903                 return exprs 
    2904  
    2905         def idExpression(precedence as int, left as Expr?) as Expr 
    2906                 # Expression tree walker for Identifier or DotExpr Identifier Stream  
    2907                 # This method and next are simplified versions of expression and expression2 
    2908                 # this code has to deal with scanning for tokens fo an expression that may be an identifier  
    2909                 # (and to stop on anything else) 
    2910                 if left is nil 
    2911                         left = .idExpression2 
    2912                 while true 
    2913                         peek = .peek.which 
    2914                         # 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 stream 
    2917                                 break    
    2918  
    2919                         opToken = .grab 
    2920                         op = opToken.which 
    2921                         assert _binaryOpPrec.containsKey(op) 
    2922                         _leftStack.push(left to !) 
    2923                         .opStack.push(op) 
    2924                         try 
    2925                                 # get the right hand side of a binary operator expression 
    2926                                 prec = if(OperatorSpecs.rightAssoc.containsKey(op), binaryOpPrec, binaryOpPrec+1) 
    2927                                 if op == 'DOT' and .peek and .peek.isKeyword 
    2928                                         # support foo.bar where bar is a keyword. Ex: foo.this 
    2929                                         right = MemberExpr(.grab) to Expr 
    2930                                 else 
    2931                                         right = .idExpression(prec, nil) 
    2932                                         left = DotExpr(opToken, op, left, right, false) 
    2933                         finally 
    2934                                 .opStack.pop 
    2935                                 _leftStack.pop 
    2936                 assert left 
    2937                 return left to ! 
    2938                  
    2939         def idExpression2 as Expr 
    2940                 peekToken = .peek 
    2941                 peek = peekToken.which 
    2942                 if peek=='DOT'          # leading dot 
    2943                         token = .grab 
    2944                         .opStack.push('DOT') 
    2945                         try 
    2946                                 peekToken = .peek 
    2947                                 peek = peekToken.which 
    2948                                 if peek=='ID' or peekToken.isKeyword 
    2949                                         memberToken = .idOrKeyword 
    2950                                         expr = MemberExpr(memberToken) to Expr 
    2951                                 else 
    2952                                         .throwError('Syntax error after "." Expecting variable Id ') 
    2953                         finally 
    2954                                 .opStack.pop 
    2955                         return BinaryOpExpr.make(token to !, 'DOT', ThisLit(token), expr) 
    2956                 else if peek=='ID' 
    2957                         return .identifierExpr 
    2958                 else if .opStack.count and .opStack.peek=='DOT' and .peek.isKeyword 
    2959                         return .identifierExpr 
    2960                          
    2961                 msg = 'Expecting an Id expression.' 
    2962                 if peekToken.isKeyword 
    2963                         msg += ' "[peekToken.text]" is a reserved keyword that is not expected here.' 
    2964                 .throwError(msg) 
    2965                 assert false 
    2966                 return ThisLit(token)  # To shut c# compiler up re paths without return values - notreached 
    29672836                 
    29682837        def forExpr as ForExpr 
     
    34393308                if token.which == 'ID' 
    34403309                        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']) 
    34443311 
    34453312        def looksLikeVarNameIsNext(peekAhead as int) as bool 
    34463313                token = .peek(peekAhead) 
    34473314                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                 
    34723320        def recordError(msg as String) as ParserException 
    34733321                return .recordError(.last, msg) 
  • cobra/trunk/Source/SharpGenerator.cobra

    r1711 r1716  
    28742874                        sw.write('yield return;\n') 
    28752875 
     2876 
     2877class MultiTargetAssignStatement 
     2878        is partial 
     2879 
     2880        def writeSharpDef(sw as SharpWriter) 
     2881                base.writeSharpDef(sw) 
     2882                _block.writeSharpDef(sw) 
    28762883 
    28772884## 
  • cobra/trunk/Source/Statements.cobra

    r1704 r1716  
    556556                        _var = .bindVar(_varExpr) 
    557557                        if _multiArgs 
     558                                _validateMultiArgs 
    558559                                _unpackMultiArgStmts 
    559560                        base._bindImp 
     
    561562                        _block.bindImp 
    562563                 
     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 
    563571        def _unpackMultiArgStmts 
    564572                """ 
     
    13761384                        pass 
    13771385                curCodeMember.hasYieldStmt = true 
     1386 
     1387 
     1388class 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  
    1010        pro p0 from _i0 
    1111        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 
    1319        def main is shared 
    1420                ma = MultiAssign() 
    1521                ma.nonLocalVar 
    1622                ma.forIEnum 
     23                ma.forIndexor 
    1724                ma.forSwap 
    1825 
     
    2229                assert _i1 == 4 
    2330                 
    24                 .x0, .x1 = ['03','04'] 
     31                .x0, .x1 = ['03', '04'] 
    2532                assert .x0 == '03' 
    2633                assert .x1 == '04' 
     
    3138                 
    3239                s = this 
    33                 s.x0, s.x1 = ['031','041'] 
     40                s.x0, s.x1 = ['031', '041'] 
    3441                assert .x0 == '031' 
    3542                assert .x1 == '041' 
     
    4047                 
    4148        def methRetList as List<of String> 
    42                 return ['a', 'b', 'c', 'd','e'] 
     49                return ['a', 'b', 'c', 'd', 'e'] 
    4350                 
    4451        def forIEnum 
     
    4855                assert b == 2 
    4956                 
     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                 
    5072        def forSwap 
    51                 # common variable swap idiom ( python a,b = b,a) 
     73                # common variable swap idiom (python a, b = b, a) 
    5274                a = 99 
    5375                b = 100 
  • cobra/trunk/Tests/320-misc-two/810-multi-assignment/210-multi-arg-for-list-fail.cobra

    r1705 r1716  
    55        def main is shared 
    66                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 
    89                        assert l1 in [1, 3, 5] 
    910                        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