Changeset 1736

Show
Ignore:
Timestamp:
11/07/08 01:58:38 (2 months ago)
Author:
Chuck.Esterbrook
Message:

Code cleanup.
Break up large methods in Expr.cobra.
ticket:58
credit:hopscc

Location:
cobra/trunk/Source
Files:
3 modified

Legend:

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

    r1734 r1736  
    18331833                finally 
    18341834                        _popCodePart 
     1835                        # warn on horrendously long methods, enabling this and cutoff should be user configurable 
     1836                        if false and codePart.statements.count > 0  # disabled until option controlled 
     1837                                startLine = codePart.statements[0].token.lineNum 
     1838                                numLines = codePart.statements[codePart.statements.count-1].token.lineNum - startLine + 1 
     1839                                fullName = '[codePart.parentBox.name].[codePart.name]' 
     1840                                if numLines > 100 
     1841                                        # TODO: is the toplevel statement really useful? 
     1842                                        _warning('More than 100 lines of code ([codePart.statements.count] toplevel statements) in method [fullName].')  
     1843                                #print '[fullName.padLeft(50)]\t[nLines]\t[codePart.statements.count]' 
    18351844 
    18361845        def _statementsFor(codePart as AbstractMethod) 
  • cobra/trunk/Source/CobraTokenizer.cobra

    r1681 r1736  
    881881 
    882882        def match(input as String) as TokenMatch? is override 
    883                 s = ._match(input) 
     883                s = _match(input) 
    884884                if s 
    885885                        return TokenMatch(s) 
  • cobra/trunk/Source/Expr.cobra

    r1733 r1736  
    398398        def _bindImp is override 
    399399                base._bindImp 
    400  
     400                assert _superNode inherits DotExpr  # otherwise, for something like List<of int>(), the parser creates a PostCallExpr 
    401401                if _genericArgProxies 
    402                         assert _superNode inherits DotExpr  # otherwise, for something like List<of int>(), the parser creates a PostCallExpr 
    403                         _genericArgTypes = List<of IType>() 
    404                         num = 1 
    405                         for typeProxy in _genericArgProxies 
    406                                 try 
    407                                         _genericArgTypes.add(typeProxy.realType) 
    408                                 catch ne as NodeException 
    409                                         ne.prefixMessage('For "[_name]" type arg [num]: ') 
    410                                         .compiler.recordError(ne) 
    411                                 num += 1 
    412                         # TODO: Check types are compatible with call 
    413  
    414                 assert _superNode inherits DotExpr 
     402                        _setGenericArgTypes 
    415403                dotNode = _superNode to DotExpr 
    416404                assert this is dotNode.right 
     
    419407                        enumDefi = dotNode.left.memberForName(_name) 
    420408                        if enumDefi inherits EnumDecl 
    421                                 # Foo.EnumType(MemberA, MemberB) 
    422                                 # Change to an EnumCallExpr 
    423                                 # Roll up Foo.Bar.Baz() into one EnumCallExpr 
    424                                 transformTarget = dotNode 
    425                                 while transformTarget.superNode inherits DotExpr 
    426                                         transformTarget = transformTarget.superNode to DotExpr 
    427                                 enumCall = EnumCallExpr(.token, _name, _args, enumDefi).bindImp 
    428                                 _type = enumCall.type to IType 
    429                                 transformTarget._transformTo(enumCall) 
     409                                _transformToEnumDecl(dotNode, enumDefi) 
    430410                                return 
    431411 
     412                _bindImpArgs 
     413                definition as INamedNode? 
     414                type as IType? 
     415 
     416                if _definition is nil or _type is nil 
     417                        # handle foo.bar() where this is the `bar()` part 
     418                        if not dotNode.left.didBindImp 
     419                                assert dotNode.left.hasError, dotNode.left 
     420                                # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found 
     421                                _type = .compiler.passThroughType 
     422                                return  # TODO: I don't think I want a return here 
     423 
     424                        definition = _inferDefinitionAndType(dotNode, definition, out type) 
     425                        assert type 
     426                        if definition inherits IType    # Box, IVar or GenericParam 
     427                                _transformToPostCallExprOnType(dotNode, definition) 
     428                                return 
     429 
     430                        if definition 
     431                                # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above 
     432                                # there is a FallThroughException down below that would report this if it happened 
     433                                if definition inherits BoxMember 
     434                                        if not .hasError 
     435                                                args = _args 
     436                                                if definition inherits MemberOverload 
     437                                                        # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. 
     438                                                        # Plenty of info here: 
     439                                                        # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution 
     440                                                        # TODO: handle type inference for generic members. See the C# spec for details. 
     441                                                 
     442                                                        winner = _computeBestOverload(definition, args) 
     443                                                        sharp'definition = winner' 
     444                                                        type = winner.resultType 
     445                                                else 
     446                                                        type = definition.resultType 
     447                                                        if not _didVariArgs(definition) 
     448                                                                definition = _checkGenerics(definition, inout type) 
     449                                                                if not definition.hasParams 
     450                                                                        _checkNoArgs(definition, args) 
     451                                                                else 
     452                                                                        params = definition.params 
     453                                                                        if _checkParamsCount(definition, args, params) 
     454                                                                                _checkArgsAssignable(definition, args, params) 
     455                                else 
     456                                        throw FallThroughException(definition) 
     457                        if type is nil, type = .compiler.passThroughType 
     458                        _definition = definition 
     459                        if _definition, _definition.isUsed = true 
     460                        _type = type 
     461                assert _type, _definition 
     462                if .args.count == 0 and .hasParens 
     463                        if (.definition inherits Method or .definition inherits MemberOverload) and not dotNode.isImplicit 
     464                                .compiler.warning(this, 'Unnecessary parentheses. You can remove them.') 
     465                else 
     466                        for arg in _args 
     467                                if arg inherits AssignExpr 
     468                                        .recordError('Cannot make assignments in arguments. This syntax is reserved in the future for keyword arguments.') 
     469 
     470        ## _bindImp support 
     471                                 
     472        def _setGenericArgTypes 
     473                _genericArgTypes = List<of IType>() 
     474                num = 1 
     475                for typeProxy in _genericArgProxies 
     476                        try 
     477                                _genericArgTypes.add(typeProxy.realType) 
     478                        catch ne as NodeException 
     479                                ne.prefixMessage('For "[_name]" type arg [num]: ') 
     480                                .compiler.recordError(ne) 
     481                        num += 1 
     482                # TODO: Check types are compatible with call 
     483 
     484        def _transformToEnumDecl(dotNode as DotExpr, enumDefi as EnumDecl) 
     485                # Foo.EnumType(MemberA, MemberB) 
     486                # Change to an EnumCallExpr 
     487                # Roll up Foo.Bar.Baz() into one EnumCallExpr 
     488                transformTarget = dotNode 
     489                while transformTarget.superNode inherits DotExpr 
     490                        transformTarget = transformTarget.superNode to DotExpr 
     491                enumCall = EnumCallExpr(.token, _name, _args, enumDefi).bindImp 
     492                _type = enumCall.type to IType 
     493                transformTarget._transformTo(enumCall) 
     494 
     495        def _inferDefinitionAndType(dotNode as DotExpr, definition as INamedNode?, type as out IType?) as INamedNode? 
     496                type = nil 
     497                possibleDefinition = dotNode.left.memberForName(_name) 
     498                if possibleDefinition is nil 
     499                        lrt = dotNode.left.receiverType 
     500                        if lrt.isDynamic 
     501                                type = .compiler.nilableDynamicType 
     502                        else if lrt is .compiler.passThroughType 
     503                                if _name == 'toString' 
     504                                        type = .compiler.stringType 
     505                                else 
     506                                        type = .compiler.passThroughType 
     507                        else 
     508                                left = dotNode.left 
     509                                suggs = left.suggestionsForBadMemberName(_name) 
     510                                isBoxAccess = left inherits IdentifierExpr and (left to IdentifierExpr).definition inherits Box  # like "Console.writeLine" where the receiver is a literal class or struct reference 
     511                                whoseTypeIs = if(isBoxAccess, '', ' whose type is "[left.receiverType.name]"') 
     512                                .throwError('Cannot find a definition for "[_name]" in "[left.toCobraSource]"[whoseTypeIs].[_suggestionsMessage(suggs)]') 
     513                else 
     514                        if not possibleDefinition.isCallable 
     515                                .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".') 
     516                        definition = possibleDefinition 
     517                        type = possibleDefinition.resultType 
     518                return definition                
     519 
     520        def _transformToPostCallExprOnType(dotNode, definition as IType?) 
     521                # for Foo.Bar() where Bar is a box/type 
     522                # Change to a PostCallExpr on the type 'Foo.Bar' 
     523                         
     524                # Roll up Foo.Bar.Baz() into one PostCallExpr 
     525                transformTarget = dotNode 
     526                while transformTarget.superNode inherits DotExpr 
     527                        transformTarget = transformTarget.superNode to DotExpr 
     528 
     529                postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindImp 
     530                _type = postCall.type to IType 
     531                transformTarget._transformTo(postCall) 
     532 
     533        def _bindImpArgs                         
    432534                num = 1 
    433535                for arg in _args 
     
    442544                        num += 1 
    443545 
    444                 definition as INamedNode? 
    445                 type as IType? 
    446  
    447                 if _definition is nil  or  _type is nil 
    448                         checkName = _name 
    449                         while checkName.startsWith('_') 
    450                                 checkName = checkName.substring(1) 
    451                         # handle foo.bar() where this is the `bar()` part 
    452                         if not dotNode.left.didBindImp 
    453                                 assert dotNode.left.hasError, dotNode.left 
    454                                 # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found 
    455                                 _type = .compiler.passThroughType 
    456                                 return  # TODO: I don't think I want a return here 
    457                         possibleDefinition = dotNode.left.memberForName(_name) 
    458                         if possibleDefinition is nil 
    459                                 lrt = dotNode.left.receiverType 
    460                                 if lrt.isDynamic 
    461                                         type = .compiler.nilableDynamicType 
    462                                 else if lrt is .compiler.passThroughType 
    463                                         if _name == 'toString' 
    464                                                 type = .compiler.stringType 
     546        def _computeBestOverload(definition as MemberOverload, args as List<of Expr>) as BoxMember 
     547                # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous  
     548                # implementation of um, doing nothing. 
     549                # But this likely needs to be rewritten. 
     550                candidates = [] 
     551                # handle generic arguments to the method 
     552                genericArgTypes = .genericArgTypes 
     553                if genericArgTypes and genericArgTypes.count 
     554                        members = List<of BoxMember>() 
     555                        for member as BoxMember? in definition.members 
     556                                if member inherits Method 
     557                                        if member.containsGenericParameters 
     558                                                if member.genericParams.count == genericArgTypes.count 
     559                                                        member = member.constructedMethodWith(genericArgTypes to !) 
     560                                                else 
     561                                                        member = nil 
     562                                if member, members.add(member) 
     563                else 
     564                        members = definition.members 
     565 
     566                for member in members 
     567                        score = -1000 
     568                        if member.params.count == args.count 
     569                                score = 0 
     570                                i = 0 
     571                                for param in member.params 
     572                                        arg = args[i] 
     573                                        if not arg.didBindImp 
     574                                                trace arg 
     575                                                trace arg.hasError 
     576                                        if arg.type == param.type 
     577                                                score += 20 
     578                                        else if arg.canBeAssignedTo(param.type) 
     579                                                score += 10 
     580                                        else if arg.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type)  
     581                                                # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore 
     582                                                # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now 
     583                                                # compensate here. 
     584                                                score += 1 
    465585                                        else 
    466                                                 type = .compiler.passThroughType 
     586                                                score -= 100 
     587                                        i += 1 
     588                        # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) 
     589                        candidates.add([score, member]) 
     590 
     591                maxScore = -10_000 
     592                winner = nil to BoxMember? 
     593                for pair in candidates 
     594                        if pair[0] to int > maxScore 
     595                                maxScore = pair[0] to int 
     596                                winner = pair[1] to BoxMember 
     597                if false 
     598                        print 
     599                        trace .token.fileName 
     600                        trace maxScore, _name 
     601                        trace .token.toTechString 
     602                        trace winner 
     603                        print 'args:' 
     604                        for arg in args 
     605                                print '   [arg]' 
     606                        print 'params:' 
     607                        for param in winner.params 
     608                                print '   [param]' 
     609                        print 'overloads:' 
     610                        for member in definition.members 
     611                                print '   [member]' 
     612                # print 'winner:', score, winner 
     613                assert winner 
     614                return winner to ! 
     615 
     616        def _didVariArgs(definition as BoxMember) as bool 
     617                hasVari = false 
     618                for param in definition.params 
     619                        if param.type inherits VariType  
     620                                hasVari = true 
     621                                break 
     622                if hasVari 
     623                        # TODO handle variable number of args (4) 
     624                        return true 
     625                else 
     626                        return false 
     627 
     628        def _checkGenerics(definition as BoxMember, type as inout IType?) as BoxMember 
     629                if .genericArgTypes and .genericArgTypes.count 
     630                        if definition inherits Method 
     631                                definition = definition.constructedMethodWith(.genericArgTypes to !) 
     632                                type = definition.resultType 
     633                        else 
     634                                .throwError('Cannot pass type arguments to "[definition.name]" because it is a [definition.getType.name].') # @@ _definition.englishName; also might need this error in other locations 
     635                return definition 
     636                                         
     637        def _checkNoArgs(definition as BoxMember, args as List<of Expr>) 
     638                if .name == 'toString' 
     639                        # HACK 
     640                        # this enables someFloat.toString('0.0') 
     641                        # can remove when primitives know their CLR types and look up their methods 
     642                        pass 
     643                else if args.count 
     644                        .throwError('The method "[definition.name]" is expecting 0 arguments, but [args.count] are being supplied in this call.') 
     645         
     646        def _checkParamsCount(definition as BoxMember, args as List<of Expr>, params as List<of Param>) as bool 
     647                if args.count <> params.count 
     648                        if _name=='toString'  # TODO because many structs like Decimal have a toString() overload which cannot currently be expressed in SystemInterfaces.cobra 
     649                                return false 
     650                        else 
     651                                .throwError('The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.') 
     652                return true 
     653                                 
     654        def _checkArgsAssignable(definition as BoxMember, args as List<of Expr>, params as List<of Param>)  
     655                for i in args.count 
     656                        arg = args[i] 
     657                        param = params[i] 
     658                        if arg.hasError 
     659                                break 
     660                        if arg inherits AssignExpr  # assignments in arguments have special treatment 
     661                                break 
     662                        assert arg.didBindImp, arg 
     663                        assert param.didBindInt, param 
     664                        if arg.canBeAssignedTo(param.type) 
     665                                arg.contextType = param.type 
     666                        else 
     667                                if false 
     668                                        print 
     669                                        print '<> definition = [definition]' 
     670                                        print '<> arg = ' stop 
     671                                        arg.writeDeepString 
     672                                        print '<> arg.type =', arg.type 
     673                                        print '<> param = ' stop 
     674                                        param.writeDeepString 
     675                                        print '<> param.type =', param.type 
     676                                        print '<> param.ifInheritsStack.count =', param.ifInheritsStack.count 
     677                                if arg.type inherits NilableType and not param.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type) 
     678                                        .throwError('Argument [i+1] of method "[_name]" expects a non-nilable type ([param.type.name]), but the call is supplying a nilable type ([arg.type.name]).') 
    467679                                else 
    468                                         left = dotNode.left 
    469                                         suggs = left.suggestionsForBadMemberName(_name) 
    470                                         isBoxAccess = left inherits IdentifierExpr and (left to IdentifierExpr).definition inherits Box  # like "Console.writeLine" where the receiver is a literal class or struct reference 
    471                                         whoseTypeIs = if(isBoxAccess, '', ' whose type is "[left.receiverType.name]"') 
    472                                         .throwError('Cannot find a definition for "[_name]" in "[left.toCobraSource]"[whoseTypeIs].[_suggestionsMessage(suggs)]') 
    473                         else 
    474                                 if not possibleDefinition.isCallable 
    475                                         .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".') 
    476                                 definition = possibleDefinition 
    477                                 type = possibleDefinition.resultType 
    478                         assert type 
    479                         # assert definition 
    480                         if definition inherits IType 
    481                                 # Foo.Bar() where Bar is a box/type 
    482                                 # Change to a PostCallExpr on the type 'Foo.Bar' 
    483                                  
    484                                 # Roll up Foo.Bar.Baz() into one PostCallExpr 
    485                                 transformTarget = dotNode 
    486                                 while transformTarget.superNode inherits DotExpr 
    487                                         transformTarget = transformTarget.superNode to DotExpr 
    488                                  
    489                                 postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindImp 
    490                                 _type = postCall.type to IType 
    491                                 transformTarget._transformTo(postCall) 
    492                                 return 
    493                         if definition 
    494                                 # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above 
    495                                 # there is a FallThroughException down below that would report this if it happened 
    496                                 if definition inherits BoxMember 
    497                                         if not .hasError 
    498                                                 args = _args 
    499                                                 if definition inherits MemberOverload 
    500                                                         # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. 
    501                                                         # Plenty of info here: 
    502                                                         # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution 
    503  
    504                                                         # TODO: handle type inference for generic members. See the C# spec for details. 
    505                                                  
    506                                                         # I cooked up the algorithm below as a quick way to fix some bugs caused by the previous implementation of um, doing nothing. 
    507                                                         # But this likely needs to be rewritten. 
    508                                                         candidates = [] 
    509  
    510                                                         # handle generic arguments to the method 
    511                                                         genericArgTypes = .genericArgTypes 
    512                                                         if genericArgTypes and genericArgTypes.count 
    513                                                                 members = List<of BoxMember>() 
    514                                                                 for member as BoxMember? in definition.members 
    515                                                                         if member inherits Method 
    516                                                                                 if member.containsGenericParameters 
    517                                                                                         if member.genericParams.count == genericArgTypes.count 
    518                                                                                                 member = member.constructedMethodWith(genericArgTypes to !) 
    519                                                                                         else 
    520                                                                                                 member = nil 
    521                                                                         if member, members.add(member) 
    522                                                         else 
    523                                                                 members = definition.members 
    524  
    525                                                         for member in members 
    526                                                                 if member.params.count <> args.count 
    527                                                                         score = -1000 
    528                                                                 else 
    529                                                                         score = 0 
    530                                                                         i = 0 
    531                                                                         for param in member.params 
    532                                                                                 arg = args[i] 
    533                                                                                 if not arg.didBindImp 
    534                                                                                         trace arg 
    535                                                                                         trace arg.hasError 
    536                                                                                 if arg.type == param.type 
    537                                                                                         score += 20 
    538                                                                                 else if arg.canBeAssignedTo(param.type) 
    539                                                                                         score += 10 
    540                                                                                 else if arg.type inherits NilableType and (arg.type to NilableType).theWrappedType.isAssignableTo(param.type)  
    541                                                                                         # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore 
    542                                                                                         # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now 
    543                                                                                         # compensate here. 
    544                                                                                         score += 1 
    545                                                                                 else 
    546                                                                                         score -= 100 
    547                                                                                 i += 1 
    548                                                                 # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) 
    549                                                                 candidates.add([score, member]) 
    550                                                         maxScore = -10_000 
    551                                                         winner = nil to BoxMember? 
    552                                                         for pair in candidates 
    553                                                                 if pair[0] to int > maxScore 
    554                                                                         maxScore = pair[0] to int 
    555                                                                         winner = pair[1] to BoxMember 
    556                                                         if false 
    557                                                                 print 
    558                                                                 trace .token.fileName 
    559                                                                 trace maxScore, _name 
    560                                                                 trace .token.toTechString 
    561                                                                 trace winner 
    562                                                                 print 'args:' 
    563                                                                 for arg in args 
    564                                                                         print '   [arg]' 
    565                                                                 print 'params:' 
    566                                                                 for param in winner.params 
    567                                                                         print '   [param]' 
    568                                                                 print 'overloads:' 
    569                                                                 for member in definition.members 
    570                                                                         print '   [member]' 
    571