Wiki

Ticket #226: big-or-complex.patch

File big-or-complex.patch, 37.9 KB (added by hopscc, 8 years ago)
  • Source/Members.cobra

     
    14831483        else 
    14841484            members = .members 
    14851485 
    1486         for member in members 
    1487             score = -1000 
    1488             if member.params.count == args.count 
    1489                 score = 0 
    1490                 if member inherits Method and (member to Method).genericParams.count > 0, score += 1 
    1491                 for i, param in member.params.numbered 
    1492                     arg = args[i] 
    1493                     if strictBindChecking and not arg.didBindImp 
    1494                         trace arg 
    1495                         trace arg.hasError 
    1496                     if strictBindChecking and (arg.type == param.type or arg.type == param.type.nonNil) 
    1497                         score += 20 
    1498                     else if strictBindChecking and arg.canBeAssignedTo(param.type) 
    1499                         score += 10 
    1500                     else if strictBindChecking and arg.type.nonNil.isAssignableTo(param.type)  
    1501                         # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore 
    1502                         # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now 
    1503                         # compensate here. 
    1504                         score += 1 
    1505                     else if not strictBindChecking 
    1506                         if arg.type is not nil 
    1507                             if arg.type == param.type 
    1508                                 score += 20 
    1509                             else if arg.type.isDynamic 
    1510                                 score += 10 
    1511                         else 
    1512                             score -= 100 
    1513                     else 
    1514                         score -= 100 
    1515             # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) 
    1516             candidates.add([score, member]) 
     1486        _calcMembersScore(members, args, strictBindChecking, candidates) 
    15171487             
    15181488        maxScore = -10_000 
    15191489        winner = nil to BoxMember? 
     
    15591529        assert winner 
    15601530        return winner to ! 
    15611531     
     1532    def _calcMembersScore(members as List<of BoxMember>, args as List<of Expr>, strictBindChecking as bool, candidates) 
     1533        for member in members 
     1534            score = -1000 
     1535            if member.params.count == args.count 
     1536                score = 0 
     1537                if member inherits Method and (member to Method).genericParams.count > 0, score += 1 
     1538                for i, param in member.params.numbered 
     1539                    arg = args[i] 
     1540                    if strictBindChecking and not arg.didBindImp 
     1541                        trace arg 
     1542                        trace arg.hasError 
     1543                    if strictBindChecking and (arg.type == param.type or arg.type == param.type.nonNil) 
     1544                        score += 20 
     1545                    else if strictBindChecking and arg.canBeAssignedTo(param.type) 
     1546                        score += 10 
     1547                    else if strictBindChecking and arg.type.nonNil.isAssignableTo(param.type)  
     1548                        # Cobra's code and data flow analysis sometimes leaves us with a nilable type that's not actually nil anymore 
     1549                        # due to an assignment, possibly wrapped in an if statement. Eventually this will be corrected, but for now 
     1550                        # compensate here. 
     1551                        score += 1 
     1552                    else if not strictBindChecking 
     1553                        if arg.type is not nil 
     1554                            if arg.type == param.type 
     1555                                score += 20 
     1556                            else if arg.type.isDynamic 
     1557                                score += 10 
     1558                        else 
     1559                            score -= 100 
     1560                    else 
     1561                        score -= 100 
     1562            # print 'candidate:', score, member.name, member.serialNum, Utils.join(', ', (for param in member.params get param.type.name)) 
     1563            candidates.add([score, member]) 
     1564 
    15621565    def _bindInt is override 
    15631566        base._bindInt 
    15641567        # sanity check that members all have the right name 
  • Source/TypeProxies.cobra

     
    168168         
    169169        # Must return the Cobra primitive types in place of System.Boolean, System.Char, System.Int16, etc. 
    170170        # because it's the primitives that are used all over the compiler. 
    171  
    172171        clrPrimitiveToIType = .compiler.clrPrimitiveToITypeCache 
    173172        if clrPrimitiveToIType is nil or clrPrimitiveToIType.count == 0 
    174             clrPrimitiveToIType = Dictionary<of System.Type, IType>() 
    175             for bt in .compiler.basicTypes 
    176                 if bt.systemAliasProxy 
    177                     key = (.compiler.nativeType((bt.systemAliasProxy to LibraryTypeProxy).qualifiedName) to ClrNativeType).clrType # TODO: cleanup 
    178                     clrPrimitiveToIType[key] = bt 
    179             assert clrPrimitiveToIType.count == 0 or clrPrimitiveToIType.count > 9 
     173            clrPrimitiveToIType = _makePrimitiveTypesDict 
    180174        basicType as IType? 
    181175        if clrPrimitiveToIType.tryGetValue(clrType, out basicType) 
    182176            return basicType to ! 
     
    211205        if clrType.isGenericParameter 
    212206            return GenericParam(ClrNativeType(clrType)) 
    213207 
    214         # compute type name 
    215         typeName = clrType.name 
    216         if '`' in typeName 
    217             # generic like IComparable`1 
    218             assert r'[' not in typeName 
    219             typeName = .cobraNameForSharpBoxName(typeName) 
    220         else if typeName[typeName.length-1].isLetterOrDigit 
    221             pass 
    222         else 
    223             .throwError('Cannot locate CLR type "[clrType]".') 
    224  
    225         # compute namespace 
     208        typeName = _computeTypeName(clrType) 
    226209        missing = false 
    227         nameParts = clrType.namespace.split(c'.') 
    228         member = .compiler.globalNS.symbolForName(nameParts[0]) 
    229         if member inherits NameSpace, curNS = member 
    230         else, missing = true 
    231         if not missing 
    232             i = 1 
    233             while i < nameParts.length 
    234                 namePart = nameParts[i] 
    235                 possible = curNS.declForName(namePart) 
    236                 if possible is nil 
    237                     missing = true 
    238                     break 
    239                 else if possible inherits NameSpace 
    240                     curNS = possible 
    241                 else 
    242                     .throwError('Found "[namePart]" at component [i+1] of CLR type "[clrType.fullName]", but it is a [possible.englishName].') 
    243                 i += 1 
    244  
     210        curNS = _computeNameSpace(clrType, level, out missing) 
    245211        if missing 
    246212            # since the CLR type exists, but cannot be located in our namespaces, 
    247213            # it must be pulled from a dependent DLL that was not directly referenced 
    248              
    249214            # but maybe it was already attempted 
    250215            if level > 0 
    251216                .throwError('Cannot read CLR type "[clrType.fullName]" or its assembly "[clrType.assembly]". Please report to the Cobra discussion forums (http://cobra-language.com/).') 
    252217 
    253218            (.compiler to dynamic).readAssembly(clrType.assembly) 
    254             return _realTypeWithoutCache(clrType, level+1)  # recurse. guard is just above. 
     219            return _realTypeWithoutCache(clrType, level+1) # recurse. guard is just above. 
    255220 
    256221        # return namespace member 
    257         member = curNS.declForName(typeName) 
     222        member as IMember? = curNS.declForName(typeName) 
    258223        if member inherits IType 
    259224            if member inherits Box 
    260225                if clrType.isGenericType and not clrType.isGenericTypeDefinition 
    261226                    # So we have something like ICollection<of KeyValuePair<of TKey,TValue>> which is all CLR types. 
    262227                    # We need the Cobra types of those args so we can construct the Cobra type from the generic cobra type 
    263228                    # otherwise, we would just end up returning the generic definition. 
    264                     args = List<of IType>() 
    265                     for genArg in clrType.getGenericArguments 
    266                         args.add(_realTypeWithCache(genArg)) 
    267                     if member.qualifiedName == 'System.Nullable<of>' 
    268                         assert args.count == 1 
    269                         member = .typeProvider.nilableType(args[0]) 
    270                     else 
    271                         member = (member to Box).constructedTypeFor(args) 
     229                    member = _typeForArgsOfGeneric(clrType, member) 
    272230            return member 
    273231        else 
    274232            msg = 'Cannot locate CLR type "[clrType]".' 
     
    284242                .throwError(msg) 
    285243        return .compiler.intType  # CC: to make C# code gen happy. 
    286244 
     245    def _makePrimitiveTypesDict as Dictionary<of System.Type, IType> 
     246        clrPrimitiveToIType = Dictionary<of System.Type, IType>() 
     247        for bt in .compiler.basicTypes 
     248            if bt.systemAliasProxy 
     249                key = (.compiler.nativeType((bt.systemAliasProxy to LibraryTypeProxy).qualifiedName) to ClrNativeType).clrType # TODO: cleanup 
     250                clrPrimitiveToIType[key] = bt 
     251        assert clrPrimitiveToIType.count == 0 or clrPrimitiveToIType.count > 9 
     252        return clrPrimitiveToIType 
     253 
     254    def _computeTypeName(clrType as Type) as String 
     255        typeName = clrType.name 
     256        if '`' in typeName 
     257            # generic like IComparable`1 
     258            assert r'[' not in typeName 
     259            typeName = .cobraNameForSharpBoxName(typeName) 
     260        else if typeName[typeName.length-1].isLetterOrDigit 
     261            pass 
     262        else 
     263            .throwError('Cannot locate CLR type "[clrType]".') 
     264        return typeName 
     265 
     266    def _computeNameSpace(clrType as Type, level as int, missing as out bool) as NameSpace 
     267        missing = false 
     268        nameParts = clrType.namespace.split(c'.') 
     269        member = .compiler.globalNS.symbolForName(nameParts[0]) 
     270        if member inherits NameSpace, curNS = member 
     271        else, missing = true 
     272        if not missing 
     273            i = 1 
     274            while i < nameParts.length 
     275                namePart = nameParts[i] 
     276                possible = curNS.declForName(namePart) 
     277                if possible is nil 
     278                    missing = true 
     279                    break 
     280                else if possible inherits NameSpace 
     281                    curNS = possible 
     282                else 
     283                    .throwError('Found "[namePart]" at component [i+1] of CLR type "[clrType.fullName]", but it is a [possible.englishName].') 
     284                i += 1 
     285        return curNS     
     286 
     287    def _typeForArgsOfGeneric(clrType as Type, member as IType) as IType 
     288        args = List<of IType>() 
     289        for genArg in clrType.getGenericArguments 
     290            args.add(_realTypeWithCache(genArg)) 
     291        boxMember = member to Box 
     292        if boxMember.qualifiedName == 'System.Nullable<of>' 
     293            assert args.count == 1 
     294            member = .typeProvider.nilableType(args[0]) 
     295        else 
     296            member = boxMember.constructedTypeFor(args) 
     297        return member    
     298 
    287299    def _hack(clrType as Type) as IType 
    288300        if clrType.isInterface 
    289301            return ClrTypeProxy(sharp'typeof(System.ICloneable)').realType 
  • Source/BackEndClr/SharpGenerator.cobra

     
    135135            .modules.count 
    136136        body 
    137137            v, options = .verbosity, .options 
    138  
    139138            optChar = if(.platform==PlatformEnum.Microsoft, '/', '-')  # option prefix character 
    140139 
    141140            # exe names 
     
    184183                # CS1718: A comparison made to same variable. 
    185184 
    186185            if .mainMethodTypeName <> '', backEndOptions.add('[optChar]main:[.mainMethodTypeName]') 
    187              
    188186            backEndOptions.addRange(extraCscOptions) 
    189187 
    190188            sharpArgs = .options.getDefault('native-compiler-args', List<of String>()) to List<of String> 
     
    204202            backEndOptions.add('-lib:[libPath]') 
    205203 
    206204            # Passing -pkg: to the C# compiler seems unnecessary. 
    207             # Compiler.refsForPackage already gets the .dll refs and 
    208             # including this was reported as a problem at 
    209             # http://cobra-language.com/forums/viewtopic.php?f=4&t=263 
    210             # 2008-12 / 2009-01 
     205            # Compiler.refsForPackage already gets the .dll refs and including this was reported at 
     206            # http://cobra-language.com/forums/viewtopic.php?f=4&t=263  2008-12 / 2009-01 
    211207            # for pkgName in .options.getStringList('pkg') 
    212208            #   backEndOptions.add('-pkg:[pkgName]') 
    213209 
  • Source/Statements.cobra

     
    686686                left = cond.left 
    687687                if left inherits IdentifierExpr 
    688688                    leftVar = left.namedDefinition 
    689                     if leftVar inherits IVar  # if-inherits smarts only work on variables 
    690                         # if x inherits Y ... 
    691                         if (right = cond.right) inherits IPotentialTypeExpr 
    692                             assert right.potentialType 
    693                             leftVar.ifInheritsStack.push(right.potentialType to !) 
    694                         else 
    695                             throw FallThroughException(right) 
    696                         ifInherits = true 
    697                         _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) 
     689                    ifInherits = _ivarIfInherits(cond, leftVar) 
     690                    #if leftVar inherits IVar  # if-inherits smarts only work on variables 
     691                    #   # if x inherits Y ... 
     692                    #   if (right = cond.right) inherits IPotentialTypeExpr 
     693                    #       assert right.potentialType 
     694                    #       leftVar.ifInheritsStack.push(right.potentialType to !) 
     695                    #   else 
     696                    #       throw FallThroughException(right) 
     697                    #   ifInherits = true 
     698                    #   _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) 
    698699                else if left inherits AssignExpr 
    699700                    if left.left inherits IdentifierExpr 
    700701                        leftVar = left.left.definition 
    701                         if leftVar inherits IVar 
    702                             # if (x = expr) inherits Y ... 
    703                             if (right = cond.right) inherits IPotentialTypeExpr 
    704                                 leftVar.ifInheritsStack.push(right.potentialType to !) 
    705                             else 
    706                                 throw FallThroughException(right) 
    707                             ifInherits = true 
    708                             _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) 
     702                        ifInherits = _ivarIfInherits(cond, leftVar) 
     703                        #if leftVar inherits IVar 
     704                        #   # if (x = expr) inherits Y ... 
     705                        #   if (right = cond.right) inherits IPotentialTypeExpr 
     706                        #       leftVar.ifInheritsStack.push(right.potentialType to !) 
     707                        #   else 
     708                        #       throw FallThroughException(right) 
     709                        #   ifInherits = true 
     710                        #   _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) 
    709711            if not ifInherits 
    710712                # check for an if-not-nil 
    711713                if cond inherits TruthExpr 
     
    755757            _falseStmts.parent = this 
    756758            _falseStmts.bindImp 
    757759 
     760    def _ivarIfInherits(cond as InheritsExpr, leftVar as INamedNode?) as bool 
     761        ifInherits = false 
     762        if leftVar inherits IVar  # if-inherits smarts only work on variables 
     763            # if x inherits Y ... 
     764            if (right = cond.right) inherits IPotentialTypeExpr 
     765                assert right.potentialType 
     766                leftVar.ifInheritsStack.push(right.potentialType to !) 
     767            else 
     768                throw FallThroughException(right) 
     769            ifInherits = true 
     770            _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) 
     771        return ifInherits    
     772 
    758773    def doNotPopIfInheritsStack 
    759774        _doNotPopIfInheritsStack = true 
    760775 
  • Source/Compiler.cobra

     
    812812            if .referenceVerbosity, print '[fle] in _loadReference("[reference]")' 
    813813            return false 
    814814 
     815    sig RVPrintStringSig( s as String) 
     816             
    815817    def __loadReference(reference as String) as bool 
    816818        require 
    817819            reference.endsWith('.dll') or reference.endsWith('.exe') 
     
    821823            not result implies .loadedReferences.count == old .loadedReferences.count 
    822824        body 
    823825            rv = .referenceVerbosity 
     826            rvPrint as RVPrintStringSig = ref _rvNull 
     827            if rv, rvPrint = ref _rvPrint 
    824828            assert not (reference.startsWith('-r') and '-r' in reference[2:]) 
    825829            if false 
    826830                # Does not work on Novell Mono. See notes above. 
     
    829833            else 
    830834                if File.exists(reference) 
    831835                    # try current directory 
    832                     if rv, '"[reference]" found as file. Will Assembly.loadFrom().' 
     836                    rvPrint('"[reference]" found as file. Will Assembly.loadFrom().') 
    833837                    referredAss = Assembly.loadFrom(reference) 
    834                     if rv, 'Assembly.loadFrom() returned: [CobraCore.toTechString(referredAss)]' 
     838                    rvPrint('Assembly.loadFrom() returned: [CobraCore.toTechString(referredAss)]') 
    835839                else 
    836840                    # TODO: the problem with -lib: in both Cobra and C# is that it has no effect on runtime, 
    837841                    # you must still register the DLLs in the GAC or copy them into the same dir as the .exe 
    838842                    # because the -lib: paths are not passed into executable. 
    839843                    # So should Cobra copy the .dll's into the target directory (if their paths are not in MONO_PATH)? 
    840                     if rv, print 'File does not exist.' 
     844                    rvPrint('File does not exist.') 
    841845                    searchPaths = .options.getDefault('library-directory', List<of String>()) to List<of String> 
    842846                    # TODO: ?: searchPaths.add(Path.getDirectoryName(Assembly.getExecutingAssembly.location))  # try Cobra's directory - also should be added to .options.'library-directory' not to a local var 
    843847                    found = false 
    844848                    for searchPath in searchPaths 
    845                         if rv, 'Checking lib path: "[searchPath]"' 
     849                        rvPrint('Checking lib path: "[searchPath]"') 
    846850                        combinedPath = Path.combine(searchPath, reference) 
    847851                        if File.exists(combinedPath) 
    848                             if rv, '"[reference]" found as file. Will Assembly.loadFrom().' 
     852                            rvPrint('"[reference]" found as file. Will Assembly.loadFrom().') 
    849853                            referredAss = Assembly.loadFrom(combinedPath) 
    850                             if rv, 'Assembly.loadFrom() returned: [CobraCore.toTechString(referredAss)]' 
     854                            rvPrint('Assembly.loadFrom() returned: [CobraCore.toTechString(referredAss)]') 
    851855                            found = true 
    852856                            break 
    853857                    if rv 
     
    858862                    if not found 
    859863                        # try system wide (GAC) 
    860864                        if reference.endsWith('.dll'), reference = reference[:-4] 
    861                         if rv, print 'Will load with partial name "[reference]"' 
     865                        rvPrint('Will load with partial name "[reference]"') 
    862866                        referredAss = Utils.loadWithPartialName(reference) 
    863                         if rv, print 'Load with partial name returned: [CobraCore.toTechString(referredAss)]' 
     867                        rvPrint( 'Load with partial name returned: [CobraCore.toTechString(referredAss)]') 
    864868                        if referredAss is nil, return false 
    865869                        # 2009-08-03 CE: Removed this code: 
    866870                        # reference = referredAss.location 
     
    885889                                print '<< Loading dependency: [dependency]' 
    886890                        else 
    887891                            .loadAssembly(dependency) 
    888                 if rv, 'Will read assembly: [referredAss]' 
     892                rvPrint('Will read assembly: [referredAss]') 
    889893                try 
    890894                    .readAssembly(referredAss, reference <> 'Cobra.Lang.dll') 
    891895                catch readExc as Exception 
    892                     if rv, print 'Caught exception during read assembly: [readExc]' 
     896                    rvPrint('Caught exception during read assembly: [readExc]') 
    893897                    throw 
    894                 if rv, 'Did read assembly: [referredAss]' 
     898                rvPrint('Did read assembly: [referredAss]') 
    895899                # reassert the preconditions. there have been bugs in the past 
    896900                assert reference.endsWith('.dll') or reference.endsWith('.exe') 
    897901                assert reference not in ['.dll', '.exe'] 
    898902                .loadedReferences.add(reference) 
    899                 if rv, 'Returning true for __loadReference("[reference]").' 
     903                rvPrint('Returning true for __loadReference("[reference]").') 
    900904                return true 
    901905            else 
    902                 if rv, 'Returning false for __loadReference("[reference]").' 
     906                rvPrint('Returning false for __loadReference("[reference]").') 
    903907                return false 
    904908 
     909    def _rvPrint(s as String) 
     910        print s 
     911 
     912    def _rvNull(s as String) 
     913        pass 
     914         
    905915    var _didLoadAssemblies = Set<of String>() 
    906916 
    907917    def loadAssembly(assName as AssemblyName) 
  • Source/BinaryOpExpr.cobra

     
    320320                    _type = .compiler.numberType 
    321321                else 
    322322                    _type = tgcd 
    323             else if rightType.isDescendantOf(tdecimal) 
    324                 _type = tdecimal 
    325             else if rightType.isDescendantOf(tfloat) 
    326                 _type = tgcd 
     323            else 
     324                cannotMix = _ifRightTypeThen(tdecimal, tdecimal, tfloat, tgcd) 
     325            #else if rightType.isDescendantOf(tdecimal) 
     326            #   _type = tdecimal 
     327            #else if rightType.isDescendantOf(tfloat) 
     328            #   _type = tgcd 
    327329        else if leftType.isDescendantOf(tdecimal) 
    328             if rightType.isDescendantOf(tdecimal) 
    329                 _type = tdecimal 
    330             else if rightType.isDescendantOf(tint) 
    331                 _type = tdecimal 
    332             else if rightType.isDescendantOf(tfloat) 
    333                 cannotMix = true 
    334             else 
    335                 cannotMix = true 
     330            cannotMix = _ifRightTypeThen(tdecimal, tdecimal, tint, tdecimal) 
     331            #if rightType.isDescendantOf(tdecimal) 
     332            #   _type = tdecimal 
     333            #else if rightType.isDescendantOf(tint) 
     334            #   _type = tdecimal 
     335            #else if rightType.isDescendantOf(tfloat) 
     336            #   cannotMix = true 
     337            #else 
     338            #   cannotMix = true 
    336339        else if leftType.isDescendantOf(tfloat) 
    337             if rightType.isDescendantOf(tfloat) 
    338                 _type = tgcd 
    339             else if rightType.isDescendantOf(tint) 
    340                 _type = tgcd 
    341             else if rightType.isDescendantOf(tdecimal) 
    342                 cannotMix = true 
    343             else 
    344                 cannotMix = true 
     340            cannotMix = _ifRightTypeThen(tfloat, tgcd, tint, tgcd) 
     341            #if rightType.isDescendantOf(tfloat) 
     342            #   _type = tgcd 
     343            #else if rightType.isDescendantOf(tint) 
     344            #   _type = tgcd 
     345            #else if rightType.isDescendantOf(tdecimal) 
     346            #   cannotMix = true 
     347            #else 
     348            #   cannotMix = true 
    345349        else if leftType.isDescendantOf(tstring) 
    346             if rightType.isDescendantOf(tstring) 
    347                 _type = tstring 
    348             else 
    349                 cannotMix = true 
     350            cannotMix = _ifRightTypeThen(tstring, tstring) 
     351            #if rightType.isDescendantOf(tstring) 
     352            #   _type = tstring 
     353            #else 
     354            #   cannotMix = true 
    350355        else if leftType inherits Box 
    351356            if leftType.isGeneric and leftType.isDescendantOf(.compiler.collectionOfType.constructedTypeFor([leftType.genericParams[0]])) 
    352357                if leftType.genericParams.count > 1 
     
    385390                    .throwError('Cannot apply [_op] to [leftType.name]. [sugg]') 
    386391                else 
    387392                    .throwError('Cannot apply [_op] to [leftType.name] and [rightType.name]. [sugg]') 
     393 
     394    def _ifRightTypeThen(ifThenPairs as vari IType) as bool 
     395        rightType = _right.type.nonNil 
     396        n =0 
     397        while n < ifThenPairs.length 
     398            matchType = ifThenPairs[n] 
     399            resultType = ifThenPairs[n+1] 
     400            n += 2 
     401            if rightType.isDescendantOf(matchType) 
     402                _type = resultType 
     403                return false 
     404        return true      
     405                         
    388406     
    389407 
    390408class AugAssignMathExpr inherits NumericPromoExpr is partial 
  • Source/CobraParser.cobra

     
    584584                    .genericConstraints(token, genericParams) 
    585585                on 'INHERITS' 
    586586                    if .optional('INHERITS') 
    587                         expectComma = false 
    588                         while true 
    589                             if expectComma 
    590                                 if .peek.isKeyword, break 
    591                                 .expect('COMMA') 
    592                             inheritsProxies.add(.typeId) 
    593                             expectComma = true 
    594                             if .peek.which == 'EOL' 
    595                                 .grab 
    596                                 break 
    597                             else if .peek.isKeyword 
    598                                 break 
     587                        _commaSepTypes(inheritsProxies) 
    599588                on 'IMPLEMENTS' 
    600589                    if .optional('IMPLEMENTS') 
    601                         # TODO: use a nested def to eliminate duplication with on 'INHERITS' just above 
    602                         expectComma = false 
    603                         while true 
    604                             if expectComma 
    605                                 if .peek.isKeyword, break 
    606                                 .expect('COMMA') 
    607                             implementsProxies.add(.typeId) 
    608                             expectComma = true 
    609                             if .peek.which == 'EOL' 
    610                                 .grab 
    611                                 break 
    612                             else if .peek.isKeyword 
    613                                 break 
     590                        _commaSepTypes(implementsProxies) 
    614591                on 'ADDS' 
    615592                    if .optional('ADDS') 
    616                         expectComma = false 
    617                         while true 
    618                             if expectComma 
    619                                 if .peek.isKeyword, break 
    620                                 .expect('COMMA') 
    621                             addsProxies.add(.typeId) 
    622                             expectComma = true 
    623                             if .peek.which == 'EOL' 
    624                                 .grab 
    625                                 break 
    626                             else if .peek.isKeyword 
    627                                 break 
     593                        _commaSepTypes(addsProxies) 
    628594                else 
    629595                    isDone = true 
    630596            if last.isOneOf('EOL.INDENT.'), continue 
     
    635601        if not didIndent, .indent 
    636602        return TypeSpecs(isNames, attribs, inheritsProxies, implementsProxies, addsProxies) 
    637603 
     604    def _commaSepTypes(proxies as List<of ITypeProxy>) 
     605        expectComma = false 
     606        while true 
     607            if expectComma 
     608                if .peek.isKeyword, break 
     609                .expect('COMMA') 
     610            proxies.add(.typeId) 
     611            expectComma = true 
     612            if .peek.which == 'EOL' 
     613                .grab 
     614                break 
     615            else if .peek.isKeyword 
     616                break 
     617 
    638618    def endOfTypeSpecClause 
    639619        """ 
    640620        Ends a type clause on EOL or a keyword. 
     
    13431323        opener = .grab to ! 
    13441324        if not opener.which.isOneOf('ID.OPEN_CALL.OPEN_GENERIC.') and not opener.isKeyword 
    13451325            .throwError('Encountered [opener.which] when expecting an identifier.') 
    1346         if opener.which == 'OPEN_GENERIC' 
    1347             genericParams = .declGenericParams(opener) 
    1348         else 
    1349             genericParams = List<of GenericParam>() 
     1326        genericParams = _methodGenericParams(opener) 
     1327 
    13501328        name = opener.value to String 
    13511329        curBox = .curBox 
    13521330        if name == curBox.name or name.capitalized == curBox.name 
    13531331            .throwError('Method names cannot be the same as their enclosing [curBox.englishName]. Use `cue init` for creating an initializer/constructor or choose another name.')  # TODO list the enclosing types location 
    13541332 
    1355         unnecessary = false 
    1356         if opener.which == 'OPEN_CALL' 
    1357             params = .paramDecls(/#skipParen=#/true) 
    1358             if params.count == 0, unnecessary = true 
    1359         else if opener.which == 'OPEN_GENERIC' 
    1360             if .optional('LPAREN') 
    1361                 params = .paramDecls(/#skipParen=#/true) 
    1362                 if params.count == 0, unnecessary = true 
    1363             else 
    1364                 params = List<of Param>() 
    1365         else if opener.which == 'ID'  and  .optional('LPAREN') 
    1366             # def id (... - misplaced space after name   
    1367             .throwError('Misplaced space between method name and opening brace "("')  
    1368         else 
    1369             params = List<of Param>() 
    1370         if unnecessary 
    1371             _warning(opener, 'Unnecessary parentheses. You can remove them.') 
     1333        params = _methodParams(opener) 
     1334        returnType = _methodReturnType 
    13721335 
    1373         if .optional('AS') 
    1374             returnType = .typeId to ITypeProxy? 
    1375         else 
    1376             returnType = _typeProvider.voidType 
    1377         assert returnType 
    1378  
    13791336        isNames = List<of String>(_isNamesStack) 
    13801337        attribs = AttributeList() 
    13811338        implementsType = nil to ITypeProxy? 
    1382  
    13831339        method as AbstractMethod? 
    13841340 
    13851341        # Rules: 
     
    14261382            encountered.add(last) 
    14271383        assert .last.which.isOneOf('EOL.INDENT.') 
    14281384 
    1429         if _spaceAgnosticIndentLevel 
    1430             sail = _spaceAgnosticIndentLevel 
    1431             if sail > 0, _spaceAgnosticIndentLevel -= 1  # for missing indent 
    1432             if sail < 0, _spaceAgnosticIndentLevel += 1  # for missing dedent 
    1433             _finishSpaceAgnostic 
    1434             if sail > 0 
    1435                 nothingMore = false  # TODO: feels wrong 
    1436             else 
    1437                 nothingMore = .last.which == 'INDENT' and .peek.which <> 'INDENT'  # _spaceAgnosticIndentLevel == 0 
    1438         else 
    1439             nothingMore = not didIndent and .last.which == 'EOL' 
    1440  
     1385        nothingMore = _endMethodDecl(didIndent) 
    14411386        docString = .docString 
    14421387        if token.which == 'CUE' and name == 'init' 
    1443             if curBox inherits Interface 
    1444                 .throwError('Cannot declare an initializer for an interface.') 
    1445             if returnType is not .typeProvider.voidType 
    1446                 .throwError('Cannot declare a return type for an initializer.') 
    1447             if implementsType 
    1448                 .throwError('Cannot specify `implements` for an initializer.') 
     1388            _verifyInitializer(returnType, implementsType) 
    14491389            method = Initializer(token, opener, .curBox, params, isNames, attribs, docString) 
    14501390        else 
    14511391            method = Method(token, opener, .curBox, name, .makeList<of IType>(genericParams), params, returnType, implementsType, isNames, attribs, docString) 
     
    14551395        else 
    14561396            .statementsFor(method) 
    14571397 
     1398        _verifyMethodStatements(method to !, isNames, curBox) 
     1399        assert method 
     1400        overload = _overloadIfNeeded(method.token, curBox, method.name)  # also checks for various errors 
     1401        if overload 
     1402            overload.addMember(method to !) 
     1403            return nil 
     1404        return method 
     1405 
     1406    #Helper for declareMethod; get List of GenericParam or empty GenericParam List       
     1407    def _methodGenericParams(opener as IToken) as List<of GenericParam> 
     1408        if opener.which == 'OPEN_GENERIC' 
     1409            genericParams = .declGenericParams(opener) 
     1410        else 
     1411            genericParams = List<of GenericParam>() 
     1412        return genericParams     
     1413         
     1414    #Helper for declareMethod; return declared method returnType or voidType 
     1415    def _methodReturnType as ITypeProxy 
     1416        if .optional('AS') 
     1417            returnType = .typeId to ITypeProxy? 
     1418        else 
     1419            returnType = _typeProvider.voidType 
     1420        assert returnType 
     1421        return returnType to ! 
     1422 
     1423    #Helper for declareMethod; get List of Param or empty Param List         
     1424    def _methodParams(opener as IToken) as List<of Param>        
     1425        unnecessary = false 
     1426        if opener.which == 'OPEN_CALL' 
     1427            params = .paramDecls(/#skipParen=#/true) 
     1428            if params.count == 0, unnecessary = true 
     1429        else if opener.which == 'OPEN_GENERIC' 
     1430            if .optional('LPAREN') 
     1431                params = .paramDecls(/#skipParen=#/true) 
     1432                if params.count == 0, unnecessary = true 
     1433            else 
     1434                params = List<of Param>() 
     1435        else if opener.which == 'ID'  and  .optional('LPAREN') 
     1436            # def id (... - misplaced space after name   
     1437            .throwError('Misplaced space between method name and opening brace "("')  
     1438        else 
     1439            params = List<of Param>() 
     1440        if unnecessary 
     1441            _warning(opener, 'Unnecessary parentheses. You can remove them.') 
     1442        return params    
     1443 
     1444    #Helper for declareMethod; Error checks on statements 
     1445    def _verifyMethodStatements(method as AbstractMethod, isNames as List<of String>, curBox as Box) 
    14581446        if method.statements.count 
    14591447            if 'abstract' in isNames 
    14601448                .throwError('Cannot have statements for an abstract method.') 
     
    14641452            if not 'abstract' in isNames and not curBox inherits Interface 
    14651453                .throwError('Missing statements. Use "pass" or other statements.') 
    14661454 
    1467         assert method 
    1468         overload = _overloadIfNeeded(method.token, curBox, method.name)  # also checks for various errors 
    1469         if overload 
    1470             overload.addMember(method to !) 
    1471             return nil 
     1455    #Helper for declareMethod; slurp up indentation and indicate if end of declaration 
     1456    def _endMethodDecl(didIndent as bool) as bool    
     1457        if _spaceAgnosticIndentLevel 
     1458            sail = _spaceAgnosticIndentLevel 
     1459            if sail > 0, _spaceAgnosticIndentLevel -= 1  # for missing indent 
     1460            if sail < 0, _spaceAgnosticIndentLevel += 1  # for missing dedent 
     1461            _finishSpaceAgnostic 
     1462            if sail > 0 
     1463                nothingMore = false  # TODO: feels wrong 
     1464            else 
     1465                nothingMore = .last.which == 'INDENT' and .peek.which <> 'INDENT'  # _spaceAgnosticIndentLevel == 0 
    14721466        else 
    1473             return method 
     1467            nothingMore = not didIndent and .last.which == 'EOL' 
     1468        return nothingMore 
     1469         
     1470    #Helper for declareMethod; Error checks on initializer 
     1471    def _verifyInitializer(returnType as ITypeProxy, implementsType as ITypeProxy?) 
     1472        if .curBox inherits Interface 
     1473            .throwError('Cannot declare an initializer for an interface.') 
     1474        if returnType is not .typeProvider.voidType 
     1475            .throwError('Cannot declare a return type for an initializer.') 
     1476        if implementsType 
     1477            .throwError('Cannot specify `implements` for an initializer.') 
     1478             
    14741479 
    14751480    def declareMethodSig as MethodSig 
    14761481        require _typeProvider 
     
    20492054                    s.afterParserRecognizesStatement 
    20502055                    if .optional('COMMA') 
    20512056                        s = .multiTargetAssign(s to Expr) 
    2052         if expectEOL 
    2053             if .verbosity>=5 
    2054                 print '<> last statement start token=[token]' 
    2055                 print '<> s = [s]' 
    2056             try 
    2057                 .expect('EOL') 
    2058             catch pe as ParserException 
    2059                 # example: puts 5 
    2060                 token = .last(1) 
    2061                 if token <> nil 
    2062                     sugg = if(token.text.length, Compiler.suggestionFor(token.text), nil) 
    2063                     sugg = if(sugg, ' Try "[sugg]" instead of "[token.text]".', nil) 
    2064                     if sugg, pe = pe.cloneWithMessage(pe.message + sugg) 
    2065                 throw pe 
     2057        if expectEOL  
     2058            _handleStmtEOL(token, s) 
     2059 
    20662060        _finishSpaceAgnostic 
    20672061        return s 
     2062         
     2063    def _handleStmtEOL(token as IToken?, s as Stmt?)     
     2064        if .verbosity>=5 
     2065            print '<> last statement start token=[token]' 
     2066            print '<> s = [s]' 
     2067        try 
     2068            .expect('EOL') 
     2069        catch pe as ParserException 
     2070            # example: puts 5 
     2071            token = .last(1) 
     2072            if token <> nil 
     2073                sugg = if(token.text.length, Compiler.suggestionFor(token.text), nil) 
     2074                sugg = if(sugg, ' Try "[sugg]" instead of "[token.text]".', nil) 
     2075                if sugg, pe = pe.cloneWithMessage(pe.message + sugg) 
     2076            throw pe 
    20682077 
    20692078 
    20702079    ## 
     
    27902799        return left to ! 
    27912800 
    27922801    def expression2 as Expr 
    2793         if _spaceAgnosticExprLevel > 0 
    2794             _spaceAgnostic 
     2802        if _spaceAgnosticExprLevel > 0, _spaceAgnostic 
    27952803        peekToken = .peek 
    27962804        peek = peekToken.which 
    27972805        if _unaryOpPrec.containsKey(peek) 
     
    28042812                on 'OLD', return OldExpr(token, unaryExpr) 
    28052813                on 'REF', return RefExpr(token, unaryExpr) 
    28062814                else, return UnaryOpExpr(token, peek, unaryExpr) 
    2807         # TODO: make a branch statement 
    2808         else if peek=='LPAREN' 
    2809             .grab 
    2810             _spaceAgnosticExprLevel += 1 
    2811             node = .expression(0, nil) 
    2812             .expect('RPAREN') 
    2813             _spaceAgnosticExprLevel -= 1 
    2814             node.isParened = true 
    2815             return node 
    2816         else if peek=='DOT' 
    2817             # leading dot 
    2818             token = .grab 
    2819             .opStack.push('DOT') 
    2820             try 
    2821                 peekToken = .peek 
    2822                 peek = peekToken.which 
    2823                 if peek=='ID' or peekToken.isKeyword 
    2824                     memberToken = .idOrKeyword 
    2825                     expr = MemberExpr(memberToken) to Expr 
    2826                 else if peek=='OPEN_CALL' 
    2827                     expr = .callExpr 
    2828                 else if peek=='OPEN_GENERIC' 
    2829                     expr = .callExpr 
     2815        branch peek 
     2816            on 'LPAREN' 
     2817                .grab 
     2818                _spaceAgnosticExprLevel += 1 
     2819                node = .expression(0, nil) 
     2820                .expect('RPAREN') 
     2821                _spaceAgnosticExprLevel -= 1 
     2822                node.isParened = true 
     2823                return node 
     2824            on 'DOT'    # leading dot 
     2825                token = .grab 
     2826                .opStack.push('DOT') 
     2827                try 
     2828                    peekToken = .peek 
     2829                    peek = peekToken.which 
     2830                    if peek=='ID' or peekToken.isKeyword 
     2831                        memberToken = .idOrKeyword 
     2832                        expr = MemberExpr(memberToken) to Expr 
     2833                    else if peek=='OPEN_CALL' 
     2834                        expr = .callExpr 
     2835                    else if peek=='OPEN_GENERIC' 
     2836                        expr = .callExpr 
     2837                    else 
     2838                        .throwError('Syntax error after "."') 
     2839                finally 
     2840                    .opStack.pop 
     2841                return BinaryOpExpr.make(token to !, 'DOT', ThisLit(token, isImplicit=true), expr) 
     2842            on 'NIL',               return NilLiteral(.grab) 
     2843            on 'TRUE' or 'FALSE',   return BoolLit(.grab) 
     2844            on 'THIS',              return ThisLit(.grab) 
     2845            on 'BASE',              return BaseLit(.grab) 
     2846            on 'VAR' 
     2847                assert _curCodePart 
     2848                if _curCodePart inherits ProperDexerXetter 
     2849                    return VarLit(.grab, _curCodePart) 
    28302850                else 
    2831                     .throwError('Syntax error after "."') 
    2832             finally 
    2833                 .opStack.pop 
    2834             return BinaryOpExpr.make(token to !, 'DOT', ThisLit(token, isImplicit=true), expr) 
    2835         else if peek=='NIL' 
    2836             return NilLiteral(.grab) 
    2837         else if peek=='TRUE' 
    2838             return BoolLit(.grab) 
    2839         else if peek=='FALSE' 
    2840             return BoolLit(.grab) 
    2841         else if peek=='THIS' 
    2842             return ThisLit(.grab) 
    2843         else if peek=='BASE' 
    2844             return BaseLit(.grab) 
    2845         else if peek=='VAR' 
    2846             assert _curCodePart 
    2847             if _curCodePart inherits ProperDexerXetter 
    2848                 return VarLit(.grab, _curCodePart) 
    2849             else 
    2850                 .throwError('Cannot refer to `var` in expressions outside of a property `get` or `set`.') 
    2851                 throw FallThroughException() # stop a warning 
    2852         else if peek=='CHAR_LIT_SINGLE' 
    2853             return CharLit(.grab) 
    2854         else if peek=='CHAR_LIT_DOUBLE' 
    2855             return CharLit(.grab) 
    2856         else if peek=='STRING_START_SINGLE' 
    2857             return .stringWithSubstitutionLit('STRING_START_SINGLE', 'STRING_PART_SINGLE', 'STRING_STOP_SINGLE') 
    2858         else if peek=='STRING_START_DOUBLE' 
    2859             return .stringWithSubstitutionLit('STRING_START_DOUBLE', 'STRING_PART_DOUBLE', 'STRING_STOP_DOUBLE') 
    2860         else if peek=='STRING_SINGLE' 
    2861             return StringLit(.grab) 
    2862         else if peek=='STRING_DOUBLE' 
    2863             return StringLit(.grab) 
    2864         else if peek=='INTEGER_LIT' 
    2865             return IntegerLit(.grab) 
    2866         else if peek=='DECIMAL_LIT' 
    2867             return DecimalLit(.grab) 
    2868         else if peek=='FRACTIONAL_LIT' 
    2869             return FractionalLit(.grab) 
    2870         else if peek=='FLOAT_LIT' 
    2871             return FloatLit(.grab) 
    2872         else if peek=='LBRACKET' 
    2873             return .literalList 
    2874         else if peek=='ARRAY_OPEN' 
    2875             return .literalArray 
    2876         else if peek=='LCURLY' 
    2877             return .literalDictOrSet 
    2878         else if peek.isOneOf('OPEN_DO.DO.') 
    2879             return .doExpr 
    2880         else if peek=='OPEN_IF' 
    2881             return .ifExpr 
    2882         else if peek=='FOR' 
    2883             return .forExpr 
    2884         else if peek=='OPEN_CALL' 
    2885             return .callExpr 
    2886         else if peek=='OPEN_GENERIC' 
    2887             if .opStack.count and .opStack.peek == 'DOT' 
    2888                 return .callExpr 
    2889             else 
    2890                 return TypeExpr(.typeId) 
    2891         else if peek=='ID' 
    2892             return .identifierExpr 
    2893         else if peek.isOneOf('SHARP_OPEN.SHARP_SINGLE.SHARP_DOUBLE.') 
    2894             return .sharpExpr 
    2895         else if .opStack.count and .opStack.peek=='DOT' and .peek.isKeyword 
    2896             return .identifierExpr 
    2897         else 
    2898             try 
    2899                 return .typeExpr 
    2900             catch pe as ParserException 
    2901                 if .allowKeywordAssignment and peekToken.isKeyword and .peek is not nil and .peek.which == 'ASSIGN' 
    2902                     assert .last is peekToken 
    2903                     word, op = .last, .grab 
    2904                     if word.text.startsWithNonLowerLetter 
    2905                         .recordError(ErrorMessages.localVariablesMustStartLowercase) 
    2906                     return AssignExpr(op, 'ASSIGN', IdentifierExpr(word), .expression) 
    2907                 else if pe.message.contains('Unrecognized type') 
    2908                     msg = 'Expecting an expression.' 
    2909                     if peekToken.isKeyword 
    2910                         msg += ' "[peekToken.text]" is a reserved keyword that is not expected here.' 
    2911                     .throwError(msg) 
    2912                     throw FallThroughException() 
     2851                    .throwError('Cannot refer to `var` in expressions outside of a property `get` or `set`.') 
     2852                    throw FallThroughException() # stop a warning 
     2853            on 'CHAR_LIT_SINGLE' or 'CHAR_LIT_DOUBLE'  
     2854                return CharLit(.grab) 
     2855            on 'STRING_START_SINGLE' 
     2856                return .stringWithSubstitutionLit('STRING_START_SINGLE', 'STRING_PART_SINGLE', 'STRING_STOP_SINGLE') 
     2857            on 'STRING_START_DOUBLE' 
     2858                return .stringWithSubstitutionLit('STRING_START_DOUBLE', 'STRING_PART_DOUBLE', 'STRING_STOP_DOUBLE') 
     2859            on 'STRING_SINGLE' or 'STRING_DOUBLE' 
     2860                return StringLit(.grab) 
     2861            on 'INTEGER_LIT',       return IntegerLit(.grab) 
     2862            on 'DECIMAL_LIT',       return DecimalLit(.grab) 
     2863            on 'FRACTIONAL_LIT',    return FractionalLit(.grab) 
     2864            on 'FLOAT_LIT',         return FloatLit(.grab) 
     2865            on 'LBRACKET',          return .literalList 
     2866            on 'ARRAY_OPEN',        return .literalArray 
     2867            on 'LCURLY',            return .literalDictOrSet 
     2868            on 'OPEN_DO' or 'DO',   return .doExpr 
     2869            on 'OPEN_IF',           return .ifExpr 
     2870            on 'FOR',               return .forExpr 
     2871            on 'OPEN_CALL',         return .callExpr 
     2872            on 'OPEN_GENERIC' 
     2873                if .opStack.count and .opStack.peek == 'DOT' 
     2874                    return .callExpr 
    29132875                else 
    2914                     throw 
     2876                    return TypeExpr(.typeId) 
     2877            on 'ID', return .identifierExpr 
     2878            on 'SHARP_OPEN' or 'SHARP_SINGLE' or 'SHARP_DOUBLE'  
     2879                return .sharpExpr 
     2880            else  
     2881                if .opStack.count and .opStack.peek=='DOT' and .peek.isKeyword 
     2882                    return .identifierExpr 
     2883                try 
     2884                    return .typeExpr 
     2885                catch pe as ParserException 
     2886                    if .allowKeywordAssignment and peekToken.isKeyword and .peek is not nil and .peek.which == 'ASSIGN' 
     2887                        assert .last is peekToken 
     2888                        word, op = .last, .grab 
     2889                        if word.text.startsWithNonLowerLetter 
     2890                            .recordError(ErrorMessages.localVariablesMustStartLowercase) 
     2891                        return AssignExpr(op, 'ASSIGN', IdentifierExpr(word), .expression) 
     2892                    else if pe.message.contains('Unrecognized type') 
     2893                        msg = 'Expecting an expression.' 
     2894                        if peekToken.isKeyword 
     2895                            msg += ' "[peekToken.text]" is a reserved keyword that is not expected here.' 
     2896                        .throwError(msg) 
     2897                        throw FallThroughException() 
     2898                    else 
     2899                        throw 
    29152900 
    29162901    def multiTargetAssign(arg0 as Expr) as Stmt 
    29172902        # id, |[id,]... = <expr>