Wiki

Ticket #325: optional-params.patch

File optional-params.patch, 19.2 KB (added by hopscc, 11 years ago)
  • Source/Expr.cobra

     
    281281        """ 
    282282        return ' whose type is "[.receiverType.name]"' 
    283283     
    284     def _paramsCountMsg(defnName as String, params as IList<of Param>, args as IList<of Expr>) as String 
     284    def _paramsCountMsg(defnName as String, params as IList<of Param>, nOptParams as int, args as IList<of Expr>) as String 
    285285        item = if(defnName == r'[]', 'indexer', 'method') 
    286286        countStr = if(args.count == 0, 'no arguments', '[args.count]')  
    287287        countStr += if(args.count == 1,  ' is', ' are')  
    288288        detail = _paramsVsArgsString(defnName, params, args) 
    289         msg = 'The [item] "[defnName]" is expecting [params.count] argument[Utils.plural(params)], but [countStr] being supplied in this call. [detail]' 
     289        optPart = if(nOptParams, ' ([nOptParams] optional)','') 
     290        expectingPart = 'is expecting [params.count] argument[Utils.plural(params)][optPart]' 
     291        suppliedPart = 'but [countStr] being supplied in this call' 
     292        msg = 'The [item] "[defnName]" [expectingPart], [suppliedPart]. [detail]' 
    290293        return msg 
    291294 
    292295    def _paramsVsArgsString(defnName as String, params as IList<of Param>, args as IList<of Expr>) as String     
     
    301304        sb = StringBuilder('The [item]declaration is "[defnName](') 
    302305        sep = '' 
    303306        for param in params 
    304             sb.append('[sep][param.name] as [param.type.name]') 
     307            optInit = if(param.optValue, ' = [param.optValue.toCobraSource]', '') 
     308            sb.append('[sep][param.name] as [param.type.name][optInit]') 
    305309            sep = ', ' 
    306310        sb.append(')"') 
    307311        # to-do: append definition return Type (if any)? 
     
    910914            if _name=='toString'  # to-do: hack: because many structs like Decimal have a toString() overload which cannot currently be expressed in SystemInterfaces.cobra 
    911915                return false 
    912916            else 
    913                 .throwError(_paramsCountMsg(definition.name, params, args)) #'The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [countStr] being supplied in this call.' 
     917                haveErr = args.count > params.count  
     918                if not haveErr 
     919                    nOptionalParams =0 
     920                    for p in params, if p.optValue, nOptionalParams += 1 
     921                    #print '[params.count] Params [nOptParams] Optional'     
     922                    haveErr = args.count < params.count - nOptionalParams 
     923                if haveErr 
     924                    .throwError(_paramsCountMsg(definition.name, params, nOptionalParams, args)) #'The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [countStr] being supplied in this call.' 
    914925        return true 
    915926         
    916927    def _inferOutArgs(args as IList<of Expr>, params as IList<of Param>, warn as bool)  
     
    15791590            return 
    15801591             
    15811592        if not hasVari and args.count <> params.count 
    1582             .throwError(_paramsCountMsg(r'[]', params, args))  # 'The indexer is expecting [params.count] argument[Utils.plural(params)], but [countStr] being supplied in this call.[msg]' 
     1593            .throwError(_paramsCountMsg(r'[]', params, 0, args))  # 'The indexer is expecting [params.count] argument[Utils.plural(params)], but [countStr] being supplied in this call.[msg]' 
    15831594        for i in 0 : args.count 
    15841595            arg = args[i] 
    15851596            param = params[i] 
     
    17961807                    .throwError('Could not find an overload for "[.name]" with zero arguments.') 
    17971808            else if defn inherits AbstractMethod 
    17981809                params = defn.params 
    1799                 if params.count <> 0 and not (params.count == 1 and params[0].type inherits VariType) 
    1800                     .throwError(_paramsCountMsg(defn.name, params, List<of Expr>()))    # 'The method "[defn.name]" is expecting [params.count] argument[Utils.plural(params)], but no arguments are being supplied in this call.' 
     1810                nOptionalParams =0   
     1811                if params.count <> 0 and not _allParamsOptional(params, out nOptionalParams) 
     1812                    .throwError(_paramsCountMsg(defn.name, params, nOptionalParams, List<of Expr>()))   # 'The method "[defn.name]" is expecting [params.count] argument[Utils.plural(params)], but no arguments are being supplied in this call.' 
    18011813                else if .name <> 'getType'  # ug, more special handling for getType 
    18021814                    box = .binarySuperNode.left.type to? Box 
    18031815                    if box 
     
    18141826                                    _type = mbr.resultType 
    18151827                                    break 
    18161828 
     1829    def _allParamsOptional(params as IList<of Param>, nOptParams as out int) as bool 
     1830        """VariArg single param  or all params in paramList optional.""" 
     1831        nOptParams=0 
     1832        if params.count == 1 and params[0].type inherits VariType 
     1833            return true 
     1834        for p in params, if  p.optValue,  nOptParams += 1 
     1835        return  nOptParams == params.count 
     1836        #return false 
     1837         
    18171838    def toCobraSource as String is override 
    18181839        return _name 
    18191840     
  • Source/BackEndClr/SharpGenerator.cobra

     
    544544        d = writer.curlyToCobraLineNum 
    545545 
    546546        # Output 
     547        #print 'Output: ', writer.output 
    547548        (.compiler to Compiler).createSharpFile(.sharpFileName, writer.output) 
    548549 
    549550        return d 
     
    14891490            on Direction.InOut, sw.write('ref ') 
    14901491            on Direction.Out, sw.write('out ') 
    14911492        sw.write('[.type.sharpParamRef] [.sharpName]') 
    1492  
     1493        if .optValue    # Net4 optional param 
     1494            sw.write('=') 
     1495            _optValue.writeSharpDef(sw) 
     1496             
    14931497    def writeSharpDefSansReferenceLabels(sw as CurlyWriter) 
    14941498        """ 
    14951499        Writes the parameter declaration but without C# "out" or "ref"--these are not available for 
  • Source/CobraParser.cobra

     
    18531853            foo as List<of int> 
    18541854            foo   # default type (dynamic?) 
    18551855            foo as Type has Attribute(arg1, arg2) 
     1856            foo [as Type] = optionalValue [has Attribute] 
    18561857            foo has Attribute(arg1, arg2) 
    18571858        """ 
    18581859        if .peek.which == 'OPEN_GENERIC' 
     
    19001901        else 
    19011902            type = TypeProxy(_typeProvider.defaultType) 
    19021903            isMissingType = true 
     1904                 
     1905        if .optional('ASSIGN')  # default value for optional param 
     1906            optExpr = .expression to ? # a constant, value Type or default(Type) 
     1907        else 
     1908            optExpr = nil 
     1909            if type is nil 
     1910                type = .typeProvider.defaultType 
     1911             
    19031912        attribs = AttributeList() 
    19041913        if .optional('HAS') 
    19051914            # TODO: multiple attribs like: x as Type has (Attrib1, Attrib2) 
    19061915            attribs.add(AttributeDecl(.attribExpr(0))) 
    19071916        # note: isMissingType is currently used to generate a warning in .paramDecls above 
    19081917        # and may be used for anonymous method parameter type inference at some point 
    1909         return Param(token, type, isMissingType=isMissingType, direction=dir, isDeclaredAsUnused=declaredAsUnused, attributes=attribs) 
     1918        return Param(token, type, isMissingType=isMissingType, direction=dir,  
     1919            isDeclaredAsUnused=declaredAsUnused, optValue=optExpr, attributes=attribs) 
    19101920 
    19111921 
    19121922    ## 
  • Source/Vars.cobra

     
    172172 
    173173 
    174174class Param inherits AbstractLocalVar is partial 
    175  
     175     
     176    var _optValue as Expr?  
     177        """Default value for an optional parameter.""" 
     178     
    176179    cue init(token as IToken, type as IType) 
    177180        base.init(token, type) 
    178181 
     
    207210 
    208211    pro direction from var as Direction 
    209212 
     213    pro optValue from var  
     214        """ Value for optional param. Marks param as optional""" 
     215     
    210216    get isInOut as bool 
    211217        return .direction == Direction.InOut 
    212218 
     
    239245 
    240246    def _bindInt 
    241247        base._bindInt 
     248        if _optValue 
     249            # TODO: check optValue is constant, valueType ctor or 'default(valueType') 
     250            #print _optValue 
     251            _optValue.bindImp  # that's bindImp intentionally 
     252            #print _optValue.type 
     253            if not _optValue.canBeAssignedTo(_type to !) 
     254                .throwError('Incompatible types. Cannot assign value of type [_optValue.type.name] on the right to [_type.name] on the left.') 
    242255        for attrib in .attributes 
    243256            try 
    244257                attrib.bindInt 
     
    247260 
    248261    def _bindImp 
    249262        base._bindImp 
     263        if  .isMissingType and _optValue and _optValue.type and not _optValue inherits NilLiteral 
     264            #infer param type from optional param default value 
     265            #if NilLiteral just leave it alone 
     266            _type = _optValue.type 
    250267        for attrib in .attributes 
    251268            try 
    252269                attrib.bindImp 
  • Source/BackEndJvm/JavaGenerator.cobra

     
    13091318        base.writeJavaDef(sw) 
    13101319        # TODO 
    13111320        #if .type.isReference and not .type inherits NilableType 
    1312         #   sw.write(r'[Cobra.Core.NotNull] ')  
     1321        #   sw.write(r'[Cobra.Core.NotNull] ') # TODO: annotations on params avail in Java8 
    13131322        branch .direction 
    13141323            on Direction.In, pass 
    13151324            on Direction.InOut, sw.write('/*inOut*/')   # default 
    13161325            on Direction.Out, sw.write('/*out*/')       # TODO 
    13171326        sw.write('[.type.javaParamRef] [.javaName]') 
     1327        if .optValue    # Net4 optional param 
     1328            detail='For java at the moment you will need to recode the optional parameter method as a set of overloaded (possibly cascaded) methods with the desired number of parameters.' 
     1329            .compiler.warning(this, 'Java does not support optional parameters. Default parameter value setting is ignored. [detail]') 
     1330            #sw.write('=') 
     1331            #_optValue.writeSharpDef(sw) 
    13181332 
    13191333    def writeJavaDefSansReferenceLabels(sw as CurlyWriter) 
    13201334        """ 
    13211335        Writes the parameter declaration but without Java "out" or "ref"--these are not available for 
    13221336        contract methods (require_foo and ensure_foo) because contracts cannot modify arguments. 
    13231337        """ 
    1324         # skip the [NotNull] as well since it's not really needed 
    13251338        sw.write('[.type.javaRef] [.javaName]')      
    13261339 
    13271340 
  • Tests/160-methods/630-opt-param-string.cobra

     
     1# String default 
     2class OptParam 
     3    def main is shared 
     4        x = OptParam() 
     5        x.val = 'none' 
     6        x.foo('default') 
     7        assert x.val == 'defaultVal' 
     8        x.foo('provided', 'xyzzy') 
     9        assert x.val == 'xyzzy' 
     10 
     11    var val = '' 
     12     
     13    def foo(f as String, s = 'defaultVal')  
     14        if f == 'default' 
     15            assert s == 'defaultVal' 
     16        .val = s 
  • Tests/160-methods/600-opt-param-inferred-type.cobra

     
     1# single optional param 
     2class OptParam 
     3    def main is shared 
     4        x = OptParam() 
     5        x.call = 0 
     6        x.foo('default') 
     7        assert x.call ==1 
     8        x.foo('provided',10) 
     9        assert x.call == 10 
     10 
     11    var call =0 
     12     
     13    def foo(s as String, i = nil) # optional single (dynamic) param 
     14        #print s,i 
     15        if s == 'default' 
     16            assert i == nil 
     17            i = 1 
     18        .call = i 
  • Tests/160-methods/620-opt-param-bool.cobra

     
     1# boolean default 
     2class OptParam 
     3    def main is shared 
     4        x = OptParam() 
     5        x.val = true 
     6        x.foo('default') 
     7        assert x.val == false 
     8        x.foo('provided', true) 
     9        assert x.val 
     10 
     11    var val = false 
     12     
     13    def foo(s as String, b = false)  
     14        if s == 'default' 
     15            assert b == false 
     16        .val = b 
  • Tests/160-methods/610-opt-param-inferred-type.cobra

     
     1# single optional param 
     2class OptParam 
     3    def main is shared 
     4        x = OptParam() 
     5        x.call = 0 
     6        x.foo('default') 
     7        assert x.call ==1 
     8        x.foo('provided',10) 
     9        assert x.call == 10 
     10 
     11    var call = 0 
     12     
     13    def foo(s as String, i = 1) # optional single param 
     14        #print s,i 
     15        if s == 'default' 
     16            assert i ==1 
     17        .call = i 
  • Tests/160-methods/640-opt-param-list.cobra

     
     1# single optional param 
     2class OptParam 
     3     
     4    def main is shared 
     5        x = OptParam() 
     6        x.call = [0] 
     7        x.foo('default') 
     8        assert x.call == nil 
     9        x.foo('provided',[1,4,9]) 
     10        assert x.call == [1,4,9] 
     11 
     12    var call as List<of int>? =[0]   
     13    def foo(s as String, l as List<of int>? = nil)  
     14        #print s,i 
     15        .call = l 
  • Tests/160-methods/641-opt-param-list.cobra

     
     1# single optional param 
     2class OptParam 
     3     
     4    def main is shared 
     5        x = OptParam() 
     6        x.call = [0] 
     7        x.foo('default') 
     8        assert x.call == nil 
     9        x.foo('provided',[1,4,9]) 
     10        assert x.call == [1,4,9] 
     11 
     12    var call as List<of int>? =[0]   
     13    def foo(s as String, l as List<of int>? = nil)  
     14        #print s,i 
     15        .call = l 
  • Tests/160-methods/400-optional-ctor-arg.cobra

     
     1class OptCtor 
     2    def main is shared 
     3        x = OptCtor() 
     4        assert x.x == 'default' 
     5 
     6        x1 = OptCtor('foo') 
     7        assert x1.x == 'foo' 
     8 
     9    var x = '' 
     10     
     11    cue init(s as String = 'default')    
     12        base.init 
     13        .x = s 
     14        print 'OptCtor [.x]' 
     15         
  • Tests/160-methods/800-no-args-error.cobra

     
     1# count of optparams in error message 
     2class OptionalExample 
     3    def main is shared 
     4        eg = EClass() 
     5        eg.twoOpt() #.error. expecting 3 arguments (2 optional) 
     6         
     7class EClass 
     8    var name as String 
     9     
     10    cue init(name as String) 
     11        base.init 
     12        .name = name 
     13 
     14    def twoOpt(required as int, optionalstr as String = "default",  optionalint as int = 10) 
     15        pass 
  • Tests/160-methods/100-optional-params.cobra

     
     1# fixed and single optional param 
     2class OptParam 
     3    def main is shared 
     4        x = OptParam() 
     5        x.call = 0 
     6        x.foo('default') 
     7        assert x.call ==1 
     8        x.foo('provided',10) 
     9        assert x.call == 10 
     10 
     11    var call = 0 
     12     
     13    def foo(s as String, i as int = 1) # optional single param 
     14        #print s,i 
     15        if s == 'default' 
     16            assert i ==1 
     17        .call = i 
  • Tests/160-methods/641-opt-param-list.cobra#

     
     1# Cannot pass non compile const as default values 
     2class OptParam 
     3     
     4    def main is shared 
     5        x = OptParam() 
     6        x.call = [0] 
     7        x.foo('default') 
     8        assert x.call == [1,2,3] 
     9        x.foo('provided',[1,4,9,16 ]) 
     10        assert x.call == [1,4,9,16] 
     11 
     12    var call = List<of int>() 
     13     
     14    def foo(s as String, l as List<of int>? = nil)  
     15        #print s,i 
     16        if not l 
     17            l = [1,2,3] 
     18        .call = l 
  • Tests/160-methods/200-optional-params.cobra

     
     1class OptParam 
     2    def main is shared 
     3        x = OptParam() 
     4        x.call = 0 
     5        x.foo 
     6        assert x.call ==1 
     7        x.foo(10) 
     8        assert x.call == 10 
     9 
     10    var call = 0 
     11     
     12    def foo(i as int = 1) # optional single param 
     13        print i 
     14        .call = i 
  • Tests/160-methods/300-optional-params.cobra

     
     1class OptParam 
     2    def main is shared 
     3        x = OptParam() 
     4        assert x.val ==0 and x.isOn == false 
     5        x.isOn = true 
     6        x.foo('this') 
     7        assert x.val ==1 and x.isOn == false 
     8        x.isOn = true 
     9        x.foo('is', 10) 
     10        assert x.val == 10 and x.isOn == false 
     11        x.foo('sparta', 20, true) 
     12        assert x.val == 20 and x.isOn == true 
     13 
     14    var val = 0 
     15    var isOn = false 
     16     
     17    def foo(s as String, i as int = 1, isOn as bool = false) 
     18        .val = i 
     19        .isOn = isOn 
  • Tests/160-methods/700-optional.cobra

     
     1class OptionalExample 
     2    def main is shared 
     3        # Instance eg does not send an argument for the constructor's  
     4        # optional parameter. 
     5        eg = ExampleClass() 
     6        assert eg.name =='Default name' 
     7        eg.exampleMethod(1, "One", 1) 
     8        assert eg.required ==1 and eg.optionalStr == 'One' and eg.optionalInt ==1 
     9        eg.exampleMethod(2, "Two") 
     10        assert eg.required ==2 and eg.optionalStr == 'Two' and eg.optionalInt ==10 
     11        eg.exampleMethod(3) 
     12        assert eg.required ==3 and eg.optionalStr == 'default' and eg.optionalInt ==10 
     13 
     14        # Instance eg2 sends an argument for the constructor's  optional parameter. 
     15        eg2 = ExampleClass("Provided name") 
     16        assert eg2.name =='Provided name' 
     17        eg2.exampleMethod(1, "One", 1) 
     18        assert eg2.required ==1 and eg2.optionalStr == 'One' and eg2.optionalInt ==1 
     19        eg2.exampleMethod(2, "Two") 
     20        assert eg2.required ==2 and eg2.optionalStr == 'Two' and eg2.optionalInt ==10 
     21        eg2.exampleMethod(3) 
     22        assert eg2.required ==3 and eg2.optionalStr == 'default' and eg2.optionalInt ==10 
     23 
     24        # The following statements produce compiler errors.  
     25        # An argument must be supplied for the first parameter, and it  
     26        # must be an integer.  
     27        #eg.exampleMethod("One", 1) 
     28        #eg.exampleMethod()  
     29 
     30        # You cannot leave a gap in the provided arguments.   
     31        #eg.exampleMethod(3, ,4)  
     32        #eg.exampleMethod(3, 4)  
     33 
     34        # You can use a named parameter to make the previous   
     35        # statement work. 
     36        #eg.exampleMethod(3, optionalint: 4) 
     37         
     38class ExampleClass 
     39    var name as String 
     40     
     41    var required =0 
     42    var optionalStr = '' 
     43    var optionalInt =0 
     44     
     45    cue init(name as String = "Default name") 
     46        base.init 
     47        .name = name 
     48 
     49    # The first parameter, required, has no default value assigned  
     50    # to it. Therefore, it is not optional. Both optionalstr and   
     51    # optionalint have default values assigned to them. They are optional.  
     52    def exampleMethod(required as int, optionalstr as String = "default",  optionalint as int = 10) 
     53        #print String.format("{0}: {1}, {2}, and {3}.", .name, required,    optionalstr, optionalint) 
     54        .required = required 
     55        .optionalStr = optionalstr 
     56        .optionalInt = optionalint