Wiki

root/cobra/trunk/Source/Expr.cobra

Revision 2657, 94.6 KB (checked in by Chuck.Esterbrook, 9 days ago)

Installer: Build and install the Cobra.Lang.Compiler library by default when installing Cobra.

  • Property svn:eol-style set to native
Line 
1"""
2Rules:
3    * Do not invoke .bindFoo on any type that an expression discovers during its own .bindImp.
4"""
5
6interface IExpr inherits ISyntaxNode
7    """
8    This is mainly used for categorization.
9    To see what really makes an expression, see the Expr class.
10    """
11
12    get definition
13        """
14        Returns the definition corresponding to this expression.
15        Dynamically typed.
16        Returns nil by default.
17        Not all expressions have definitions.
18        """
19
20    pro type as IType?
21
22    pro receiverType as IType?  # TODO: consider renaming to typeForReceiver
23        """
24        This property returns the .type if there was no receiver type explicitly set because the type of an expression is its receiver type by default.
25        """
26
27    pro contextType as IType?
28        """
29        The type expected where this expression is being used.
30        Normally this is the same type as the expression, but it can be different, for example when
31        assigning a dynamic-typed expression to a static-typed variable, the type of the variable
32        becomes the context type.
33        """
34
35    def toCobraSource as String
36
37
38class Expr inherits Stmt implements IExpr is abstract, partial
39
40    var _type as IType?
41    var _receiverType as IType?
42        """
43        The type for receiving messages which can be different than the
44        _type in a static situation such as `ChessPiece.Color` (where
45        Color is an enum). The type of that expression, if assigned to a
46        local var, is Type. But if accessed with a further dot
47        `ChessPiece.Color.Black`, then the type is the Color
48        enumeration itself--the receiverType.
49
50        For a runtime access (ex: `user.name`) the two types are
51        conceptually the same (ex: `String`) and _receiverType is left
52        nil.
53
54        _receiverType is used in memberForName() to look up members such
55        as "Black".
56
57        TODO: rename to _typeForMemberAccess
58        """
59    var _contextType as IType?
60    var _isParened as bool
61    var _direction = Direction.In
62
63    cue init(token as IToken)
64        base.init(token)
65
66    def addRefFields is override
67        base.addRefFields
68        .addField('type', _type)
69        if .definition, .addField('definition', .definition)
70
71    get allExprs as Expr*
72        """ Subclasses should override to yield the .allExprs of their direct sub-expressions. """
73        yield this
74
75    pro direction from var
76        """
77        Indicates the label for an argument such as 'out' or 'inout'.
78        """
79
80    get definition
81        return nil
82
83    get hasError as bool is override
84        for expr in .allExprs, if expr._innerHasError, return true
85        return false
86
87    get _innerHasError as bool
88        return .errors and .errors.count > 0
89
90    get willChangeVar as bool
91        """
92        Return true if this expression will change a variable.
93        Used by `assert` and others that won't accept expressions with such side effects from assignment, `out` parameters, etc.
94        Subclasses that contain subexpressions must override to check their subexpressions.
95        """
96        return false
97
98    get canBeStatement as bool
99        return false
100
101    def afterStatementBindImp is override
102        base.afterStatementBindImp
103        if not .hasError and not .canBeStatement
104            if .isHelpRequested
105                .compiler.warning(this, '@help expression ignored due to not being a viable statement.')
106                _transformTo(DotExpr(.token, 'DOT', IdentifierExpr(.token, 'CobraCore'), MemberExpr(.token, 'noOp'), isImplicit=true).bindAll)
107            else
108                .recordError('Only assignment, call and new object expressions can be used as a statement.')
109
110    pro type from var
111
112    pro receiverType as IType?
113        get
114            return _receiverType ? _type
115        set
116            _receiverType = value
117
118    pro contextType from var
119
120    get curBox as Box? is protected
121        """
122        Returns the current code member during "bind imp" and "write sharp".
123        """
124        return if(.compiler.boxStack.count, .compiler.boxStack.peek, nil)
125
126    get curCodeMember as AbstractMethod? is protected
127        """
128        Returns the current code member during "bind imp" and "write sharp".
129        """
130        return if(.compiler.codeMemberStack.count, .compiler.codeMemberStack.peek, nil)
131
132    def canBeAssignedTo(type as IType) as bool
133        require
134            .didBindImp
135            .type
136        body
137            return .type.isAssignableTo(type)
138
139    def toCobraSource as String
140        return '[.getType.name].toCobraSource'
141
142    get isCalling as bool
143        """
144        Return true is this expression is making a call.
145        The default implementation returns false.
146        You might think "inherits CallExpr" would suffice, but MemberExpr and IdentifierExpr can
147        also return true when they refer to methods or inits.
148        """
149        return false
150
151    def isKindOf(type as IType) as bool
152        require
153            .type
154            .compiler
155        body
156            return .type.isDescendantOf(type)
157
158    def isKindOfCollection as bool
159        require
160            .type
161            .compiler
162        body
163            return .isKindOf(.compiler.collectionType) or .isKindOf(.compiler.collectionOfType)
164
165    def isKindOfNonNil(type as IType) as bool
166        require
167            .type
168            .compiler
169        body
170            return .type.nonNil.isDescendantOf(type)
171       
172    get isObjectLiteral as bool
173        return false
174
175    pro isParened from var
176        """
177        Set by the parser if this expression was wrapped in parenthesis.
178        TODO: Consider enhancing toCobraSource to make use of this!
179        """
180
181    def isEquivalentTo(expr as Expr) as bool
182        # TODO: should compare reductions of expressions (or be used after a reduction phase)
183        return expr is this or (expr.typeOf == .typeOf and expr.serialNum == .serialNum)
184
185    def memberForName(name as String) as IMember?
186        require
187            .didBindImp
188            .type
189        body
190            return .receiverType.memberForName(name)
191
192    def suggestionsForBadMemberName(name as String) as List<of String>
193        require
194            .didBindImp
195            .type
196        body
197            return .receiverType.suggestionsForBadMemberName(name)
198
199    get binarySuperNode as BinaryOpExpr
200        require
201            .superNode inherits BinaryOpExpr
202        body
203            return .superNode to BinaryOpExpr
204
205    def postBindImp is override
206        .postBindImpAssertType
207        .help
208       
209    def postBindImpAssertType
210        """
211        This is broken out because in rare cases, it doesn't apply. (Those classes override this to
212        do nothing.)
213        """
214        if not .hasError
215            assert .type or .transformedTo
216
217    def help is override
218        if not .hasError and .isHelpRequested
219            hg = HelpGenerator(.toCobraSource, this, .compiler.curBoxMember, .curBox)
220            _help(hg)
221            hg.generate
222            .compiler.warning(this, '@help at "[hg.path]".')
223
224    def _help(hg as HelpGenerator)
225        """ Subclasses can override this method to augment the help generator's information. """
226        if .receiverType
227            _helpType(hg, .receiverType)
228        if .type and not .type == .receiverType
229            _helpType(hg, .type)
230
231    def _helpType(hg as HelpGenerator, type as IType?)
232        if type
233            hg.types.add(type)
234            hg.searchTerms.add(type.name)
235            ns = type.parentNameSpace
236            if ns
237                if not ns.isRoot, hg.searchTerms.add(ns.fullName + ' ' + type.name)
238            else if type inherits Box
239                pb = type.parentBox
240                if pb, hg.searchTerms.add(pb.qualifiedName + ' ' + type.name)
241
242    def _bindImp is override
243# TODO:
244#       ensure
245#           .type
246        body
247            base._bindImp
248
249    def _suggestionsMessage(suggs as List<of String>) as String
250        """
251        Shared by MemberExpr, CallExpr and IdentifierExpr to give suggestions in an error message about an unknown member.
252        """
253        if suggs.count
254            if suggs.count == 1
255                sugg = suggs[0]
256                prefix = if(sugg.startsWith('_'), '', '.')
257                msg = ' There is a member named "[prefix][sugg]" with a similar name.'
258            else
259                msg = ' There are members with similar names including '
260                sep = ''
261                for i, sugg in suggs.numbered
262                    prefix = if(sugg.startsWith('_'), '', '.')
263                    msg += '[sep]"[prefix][sugg]"'
264                    sep = if(i<suggs.count-2, ', ', ' and ')
265                msg += '.'
266        else
267            msg = ''
268        return msg
269
270    def whoseTypeIsMessage as String
271        """
272        Returns a suffix for member-not-found error messages.
273        Returns ' whose type is "[.receiverType.name]"'
274        Overridden by IdentifierExpr to return '' when accessing types.
275        """
276        return ' whose type is "[.receiverType.name]"'
277   
278    def throwCannotFindMemberError(receiverExpr as Expr, memberName as String)
279        __throwOrRecordCannotFindMemberError(receiverExpr, memberName, 1)
280
281    def recordCannotFindMemberError(receiverExpr as Expr, memberName as String)
282        __throwOrRecordCannotFindMemberError(receiverExpr, memberName, 2)
283
284    def __throwOrRecordCannotFindMemberError(receiverExpr as Expr, memberName as String, throwOrRecord as int) is nonvirtual
285        require throwOrRecord in [1, 2]
286        suggs = _suggestionsMessage(receiverExpr.suggestionsForBadMemberName(memberName))
287        errorMsg = 'Cannot find a definition for "[memberName]" in "[receiverExpr.toCobraSource]"[receiverExpr.whoseTypeIsMessage].[suggs]'
288        branch throwOrRecord
289            on 1, .throwError(errorMsg)
290            on 2, .recordError(errorMsg)
291            else, throw FallThroughException(throwOrRecord)
292
293
294interface IPotentialTypeExpr inherits IExpr
295    """
296    An implementor of IPotentialType *could* be a representation of a type.
297    This is implemented by expressions such as DotExpr, IdentifierExpr and TypeExpr.
298    It "inherits IExpr" to emphasize and verify that fact. This interface is not for statements, classes, etc.
299    """
300
301    get potentialType as IType?
302        """
303        Returns the type that this object represents, or nil if it does not represent a type.
304        Should be asked for only during "bind implementation" or later.
305        """
306
307
308class NameExpr
309    is abstract, partial
310    inherits Expr
311    """
312    The base class for IdentifierExpr and AsExpr.
313    """
314
315    var _name as String
316    var _definition as INamedNode?
317        """
318        In practice, definitions include:
319            * Class, Struct, Interface
320            * NameSpace, EnumDecl
321            * ClassVar, LocalVar, Param
322        There may be others, but at least those have been observed in practice.
323        """
324
325    cue init(token as IToken)
326        base.init(token)
327        _name = token.text
328
329    get idString as String is override
330        if .token.isEmpty
331            return '[.getType.name]([.serialNum], [.token.shortLocationString])'
332        else
333            return '[.getType.name]([.serialNum], "[.name]", [.token.shortLocationString])'
334
335    def addMinFields
336        base.addMinFields
337        .addField('name', _name)
338
339    get definition is override
340        return _definition
341
342    get namedDefinition from _definition
343
344    get name from var
345
346    def memberForName(name as String) as IMember? is override
347        or require
348            .namedDefinition
349        body
350            return _definition.typeForReceiver.memberForName(name)
351
352
353class AnonymousExpr
354    is abstract, partial
355    inherits Expr
356    """
357    The abstract base class for LambdaExpr and AnonymousMethodExpr.
358    """
359   
360    var _returnTypeProxy as ITypeProxy?
361
362    cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?)
363        base.init(token)
364        _params = params
365        _returnTypeProxy = returnTypeProxy
366   
367    get params from var as List<of Param>
368
369    def addSubFields
370        base.addSubFields
371        .addField('params', .params)
372
373    def toCobraSource as String is override
374        sb = StringBuilder('do(')
375        sep = ''
376        for param in .params
377            sb.append(sep)
378            #sb.append(param.toCobraSource)  # TODO
379            sb.append(param.name)
380            sep = ', '
381        sb.append(')')
382        return sb.toString
383
384
385class AnonymousMethodExpr inherits AnonymousExpr is partial
386    implements HasAddStmt
387    """
388    Also called "closures".
389    """
390   
391    var _method as AnonymousMethod?
392
393    cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?)
394        base.init(token, params, returnTypeProxy)
395        _stmts = List<of Stmt>()
396
397    get stmts from var as List<of Stmt>
398   
399    def addStmt(stmt as Stmt)
400        _stmts.add(stmt)
401
402    get _innerHasError as bool
403        return base._innerHasError or _method.hasError
404
405    def _bindImp is override
406        base._bindImp
407        _method = AnonymousMethod(.token, .curCodeMember, .params, _returnTypeProxy, if(.curCodeMember.isShared, ['shared'], List<of String>()))
408        for stmt in .stmts, _method.addStmt(stmt)
409        _method.bindInt
410        _method.bindImp
411        _type = _method.resultType
412
413
414class LambdaExpr inherits AnonymousExpr is partial
415    """
416    ... do(a as int, b as int)=a.compareTo(b) ...
417    """
418
419    cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?, expr as Expr)
420        base.init(token, params, returnTypeProxy)
421        _expr = expr
422
423    get allExprs as Expr*
424        for expr in base.allExprs, yield expr
425        for expr in .expr.allExprs, yield expr
426
427    get expr from var as Expr
428   
429    get method from var as AnonymousMethod?
430        # a reference to the method created in _bindImp is maintained purely for
431        # inspection/debugging purposes
432
433    def addSubFields
434        base.addSubFields
435        .addField('expr', .expr)
436
437    def toCobraSource as String is override
438        sb = StringBuilder(base.toCobraSource)
439        sb.append('=')
440        sb.append(.expr.toCobraSource)
441        return sb.toString
442
443    def _bindImp is override
444        base._bindImp
445        _method = AnonymousMethod(.token, .curCodeMember, .params, .compiler.dynamicType, if(.curCodeMember.isShared, ['shared'], List<of String>()))
446        ret = ReturnStmt(.token.copy('RETURN', 'return'), .expr)
447        _method.addStmt(ret)
448        _method.bindInt
449        _method.bindImp
450        # the expression may have been transformed, but if so, only the containing ReturnStmt got the notice
451        _expr = ret.expr to !
452        _type = .compiler.passThroughType # TODO: Set a real type such as .compiler.delegateType
453        # method.lambdaReturnType
454
455
456class AsExpr
457    is partial
458    inherits NameExpr
459    """
460    i as int = 5
461    The "i as int" is an AsExpr.
462    """
463
464    var _asToken as IToken  # the identifier vs. the `as` keyword which is the main token
465    var _typeNode as ITypeProxy
466
467    cue init(token as IToken, nameToken as IToken, typeNode as ITypeProxy)
468        base.init(nameToken)
469        _asToken = token
470        _typeNode = typeNode
471
472    def addMinFields is override
473        base.addMinFields
474        .addField('name', _name)
475
476    def addSubFields is override
477        base.addSubFields
478        .addField('typeNode', _typeNode)
479
480    get canBeStatement as bool is override
481        return true
482
483    def _bindImp is override
484        base._bindImp
485        _type = _typeNode.realType
486        assert _type, _typeNode
487        if not .name.startsWithLowerLetter, .throwError(ErrorMessages.localVariablesMustStartLowercase)
488        # make the local var if necessary (usually is)
489        definition = .compiler.findLocal(_name)
490        if definition is nil
491            newVar = LocalVar(_token, _type)
492            newVar.bindAll
493            .compiler.codeMemberStack.peek.addLocal(newVar)
494            _definition = newVar
495        else
496            if definition.type <> _type
497                .throwError('Cannot redeclare "[.name]" as a different type. Earlier type is "[definition.type.name]".')
498                # to-do: would be nice to point to earlier location. definition would need to carry source location info
499            else
500                _definition = definition
501
502    def postBindImp is override
503        _computeIsUsed
504        .help
505
506    def _computeIsUsed
507        # don't want to say isUsed=true if the AsExpr is the left hand side like in:
508        #       x as int = 5
509        # but if it is the right hand side like so:
510        #       _x = x as int = 5
511        # then it is, in fact, being used.
512        if _definition and .superNode inherits BinaryOpExpr
513            if .binarySuperNode.right is this
514                # case: x = y as int
515                _definition.isUsed = true
516            else if .binarySuperNode.superNode inherits BinaryOpExpr and .binarySuperNode is .binarySuperNode.binarySuperNode.right
517                # case: x = y as int = 5
518                # recall that assign is right-to-left associative
519                _definition.isUsed = true
520
521    def afterStatementBindImp is override
522        base.afterStatementBindImp
523        if .type.isUninitializedForLocalVars
524            .throwError('Must initialize this non-nil object type, or change the type to nilable (suffix with ?).')
525
526
527class CallOrMemberExpr inherits Expr is abstract, partial
528
529    cue init(token as IToken, name as String)
530        base.init(token)
531        _name = name
532
533    get name from var as String
534   
535    def _mangleName(name as String) as String
536        if .compiler.boxMemberStack.count > 0 and .binarySuperNode.left inherits ThisOrBaseLit
537            return .compiler.curBoxMember.mangleName(name)
538        # may have empty boxMemberStack for attributes like:
539        #   has AttributeUsage(AttributeTargets.Method, allowMultiple=false)
540        return name
541
542    def _checkVisibility(defn as BoxMember, left as Expr)
543        """ Check visibility: public, protected, private, internal. """
544        if defn.isPublic, return
545        # search for 'def pub' and/or 'def prot' for the relevant test cases for this
546        curBox, defnParentBox = .compiler.curBox, defn.parentBox
547        if defn.isProtected
548            if not left inherits BaseLit _
549                and not left inherits ThisLit _
550                and not curBox is defnParentBox _
551                and not defnParentBox.isDescendantOf(curBox) _
552                and not (left.type.isDescendantOf(curBox) and curBox.isDescendantOf(defnParentBox)) _
553                and not _subCheckVisibility(defn, left, curBox)
554                # then
555                msg = 'Cannot access protected "[_name]" in "[left.toCobraSource]"[left.whoseTypeIsMessage].'
556                if curBox.isDescendantOf(defnParentBox) or _
557                    (curBox.isGeneric and defnParentBox.isConstructed and curBox.isIndirectConstructionOf(defnParentBox.genericDef to !))
558                    # then
559                    msg += ' The qualifier must be of type "[curBox.nameWithGenericParams]" or derived from it.'
560                .throwError(msg)
561        else if defn.isPrivate
562            if not curBox is defnParentBox and not curBox is defnParentBox.genericDef
563                .throwError('Cannot access private "[_name]" in "[left.toCobraSource]"[left.whoseTypeIsMessage].')
564        else if defn.isInternal
565            # TODO
566            pass
567
568    def _subCheckVisibility(defn as BoxMember, left as Expr, curBox as Box) as bool
569        leftBox = left.type to? Box
570        if leftBox
571            return leftBox.isGeneric and curBox.isGenericDef and leftBox.isIndirectConstructionOf(curBox) and leftBox.isDescendantOf(defn.parentBox)
572        return false
573
574
575class CallExpr inherits CallOrMemberExpr implements IDotRightExpr is partial
576    """
577    A CallExpr may be on the right side of a dot expression as in `obj.foo(x, y)`.
578
579    This expression will transform into PostCallExpr for the case where in Foo.Bar() the Bar is a type, not a method.
580   
581    The parser can't always know the exact context and semantics of a call. So transformations include:
582        EnumCallExpr
583        DotExpr -- for _foo(args) --> ._foo(args)
584        PostCallExpr
585
586    A valid call expr may have a nil .definition because the receiver is dynamic.
587    """
588
589    var _genericArgProxies as List<of ITypeProxy>?
590    var _genericArgTypes as List<of IType>?
591    var _args as List<of Expr>
592    var _hasParens as bool
593    var _definition as IMember?
594
595    cue init(token as IToken, name as String, args as List<of Expr>, hasParens as bool)
596        .init(token, name, nil, args, hasParens as bool)
597
598    cue init(token as IToken, name as String, genericArgProxies as List<of ITypeProxy>?, args as List<of Expr>, hasParens as bool)
599        base.init(token, name)
600        _genericArgProxies = genericArgProxies
601        _args = args
602        _hasParens = hasParens
603        _definition = nil
604
605    def addMinFields
606        base.addMinFields
607        .addField('name', _name)
608
609    def addSubFields
610        base.addSubFields
611        .addField('args', _args)
612
613    get allExprs as Expr*
614        for expr in base.allExprs, yield expr
615        for arg in .args, for expr in arg.allExprs, yield expr
616
617    get genericArgProxies from var
618   
619    get genericArgTypes from var
620
621    get hasParens from var
622   
623    get willChangeVar as bool is override
624        for arg in _args, if arg.willChangeVar, return true
625        for arg in _args, if arg.direction <> Direction.In, return true
626        return false
627
628    get isCalling as bool is override
629        return true
630
631    get args from var
632        has Subnodes
633
634    get definition is override
635        return _definition
636
637    get memberDefinition from _definition
638
639    def _innerClone
640        assert _definition is nil
641        base._innerClone
642        # to-do: _genericArgProxies
643        _args = _args.toList2
644        for i in _args.count, _args[i] = _args[i].clone
645
646    def _bindImp is override
647        base._bindImp
648        assert _superNode inherits DotExpr  # otherwise, for something like List<of int>(), the parser creates a PostCallExpr
649        if _genericArgProxies, _setGenericArgTypes
650        dotNode = _superNode to DotExpr
651        assert this is dotNode.right
652
653        if dotNode.left.didBindImp
654            enumDefi = dotNode.left.memberForName(_name)
655            if enumDefi inherits EnumDecl
656                _transformToEnumDecl(dotNode, enumDefi)
657                return
658
659        needInferOutArgs = false  # to-do: after next snapshot remove this line. 2010-10-20
660        hasErrors = _bindImpArgs(out needInferOutArgs)
661        definition as IMember?
662        type as IType?
663
664        if _definition is nil or _type is nil
665            # handle foo.bar() where this is the `bar()` part
666            if not dotNode.left.didBindImp
667                assert dotNode.left.hasError, dotNode.left
668                # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found
669                _type = .compiler.passThroughType
670                return  # TODO: I don't think I want a return here
671
672            definition = _inferDefinitionAndType(dotNode, out type)
673            assert type
674            if definition inherits IType    # Box, IVar or GenericParam
675                _transformToPostCallExprOnType(definition)
676                return
677
678            if definition
679                # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above
680                # there is a FallThroughException down below that would report this if it happened
681                if definition inherits BoxMember
682                    if not .hasError and not hasErrors
683                        args = _args
684                        if definition inherits MemberOverload
685                            # to-do: Correct this dependency; Need args bindImp done to calc bestOverload,
686                            # but need best overload to infer (undefined out) variable type
687                            if needInferOutArgs
688                                moDefn = definition.members[0]  # not guaranteed to have the best param list
689                                if moDefn.hasParams
690                                    params = moDefn.params
691                                    _inferOutArgs(args, params, true)   
692                                   
693                            # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math.
694                            # Plenty of info here:
695                            # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution
696                            # TODO: handle type inference for generic members. See the C# spec for details.
697                            winner = definition.computeBestOverload(.args, .genericArgTypes, true)
698                            sharp'definition = winner'
699                            type = winner.resultType
700                        else
701                            type = definition.resultType
702                            if not _didVariArgs(definition)
703                                definition = _checkGenerics(definition, inout type)
704                                if not definition.hasParams
705                                    _checkNoArgs(definition, args)
706                                else
707                                    params = definition.params
708                                    if _checkParamsCount(definition, args, params)
709                                        if needInferOutArgs, _inferOutArgs(args, params, false)
710                                        _checkArgsAssignable(definition, args, params)
711                        _checkInOutArguments
712                        _checkVisibility(definition, dotNode.left)
713                else
714                    throw FallThroughException(definition)
715            if type is nil, type = .compiler.passThroughType
716            _definition = definition
717            if _definition, _definition.isUsed = true
718            _type = type
719        assert _type, _definition
720
721        if .args.count == 0 and .hasParens
722            # TODO:
723            # if .definition inherits BoxMember and .definition.isMethod and not dotNode.isImplicit
724            if (.definition inherits AbstractMethod or .definition inherits MemberOverload) and not dotNode.isImplicit
725                .compiler.warning(this, 'Unnecessary parentheses. You can remove them.')
726        else
727            for arg in _args
728                if arg inherits AssignExpr
729                    .recordError('Cannot make assignments in arguments. This syntax is reserved in the future for keyword arguments.')
730
731    ## _bindImp support
732
733    def _checkNoArgs(definition as BoxMember, args as IList<of Expr>)
734        if .name == 'toString'
735            # HACK
736            # this enables someFloat.toString('0.0')
737            # can remove when primitives know their CLR types and look up their methods
738            pass
739        else if args.count
740            .throwError('The method "[definition.name]" is expecting 0 arguments, but [args.count] are being supplied in this call.')
741   
742    def _setGenericArgTypes
743        _genericArgTypes = List<of IType>()
744        for num, typeProxy in _genericArgProxies.numbered
745            try
746                _genericArgTypes.add(typeProxy.realType)
747            catch ne as NodeException
748                ne.prefixMessage('For "[_name]" type arg [num+1]: ')
749                .compiler.recordError(ne)
750        # TODO: Check types are compatible with call
751
752    def _transformToEnumDecl(dotNode as DotExpr, enumDefi as EnumDecl)
753        # Foo.EnumType(MemberA, MemberB)
754        # Change to an EnumCallExpr
755        # Roll up Foo.Bar.Baz() into one EnumCallExpr
756        transformTarget = dotNode
757        while transformTarget.superNode inherits DotExpr
758            transformTarget = transformTarget.superNode to DotExpr
759        enumCall = EnumCallExpr(.token, _name, _args, enumDefi).bindImp
760        _type = enumCall.type to IType
761        transformTarget._transformTo(enumCall)
762
763    def _inferDefinitionAndType(dotNode as DotExpr, type as out IType?) as IMember?
764        type = nil
765        possibleDefinition = dotNode.left.memberForName(_mangleName(.name))
766        if possibleDefinition is nil
767            lrt = dotNode.left.receiverType
768            if lrt.isDynamic
769                type = .compiler.nilableDynamicType
770            else if lrt is .compiler.passThroughType
771                if _name == 'toString'
772                    type = .compiler.stringType
773                else
774                    type = .compiler.passThroughType
775            else
776                .throwCannotFindMemberError(dotNode.left, _name)
777        else
778            if not possibleDefinition.isCallable
779                .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".')
780               
781            type = possibleDefinition.resultType
782        return possibleDefinition
783
784    def _transformToPostCallExprOnType(definition as IType?)
785        transformTarget = .superNode to DotExpr
786        postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindAll to PostCallExpr
787        _type = postCall.type
788        transformTarget._transformTo(postCall)
789
790    def _undefinedOutArg(arg as Expr) as bool
791        if arg.direction == Direction.Out
792            if arg inherits IdentifierExpr
793                if not arg.findDefinition
794                    return true
795        return false
796               
797    def _bindImpArgs(needInferOutArgs as out bool) as bool
798        """
799        Do bindImp on callExprs args.
800        Defer binding on undefined out args but flag that is inference needed later (see _bindImp).
801        """
802        hasErrors = needInferOutArgs = false
803        for num, arg in _args.clone.numbered
804            try
805                if arg inherits AssignExpr
806                    arg.right.bindImp  # 'x=y' has special treatment in arguments
807                else
808                    if needInferOutArgs == false and _undefinedOutArg(arg)
809                        needInferOutArgs = true
810                        continue
811                    _bindImpArg(num, arg)
812            catch ne as NodeException
813                ne.prefixMessage('For "[_name]" arg [num+1]: ')
814                .compiler.recordError(ne)
815                hasErrors = true
816        return hasErrors
817
818    def _bindImpArg(num as int, arg as Expr)
819        boundArg = arg.bindImp
820        if boundArg is not arg  # transformation
821            _args[num] = boundArg
822        assert boundArg.didBindImp
823
824    def _didVariArgs(definition as BoxMember) as bool
825        hasVari = false
826        for param in definition.params
827            if param.type inherits VariType 
828                hasVari = true
829                break
830        if hasVari
831            # TODO handle variable number of args (4)
832            return true
833        else
834            return false
835
836    def _checkGenerics(definition as BoxMember, type as inout IType?) as BoxMember
837        if .genericArgTypes and .genericArgTypes.count
838            if definition inherits Method
839                definition = definition.constructedMethodWith(.genericArgTypes to !)
840                type = definition.resultType
841            else
842                .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
843        return definition
844   
845    def _checkParamsCount(definition as BoxMember, args as IList<of Expr>, params as IList<of Param>) as bool
846        if args.count <> params.count
847            if _name=='toString'  # TODO because many structs like Decimal have a toString() overload which cannot currently be expressed in SystemInterfaces.cobra
848                return false
849            else
850                .throwError('The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.')
851        return true
852
853    def _inferOutArgs(args as IList<of Expr>, params as IList<of Param>, warn as bool) 
854        for num, arg in args.numbered
855            if _undefinedOutArg(arg)
856                inferredType = params[num].type
857                newDefn = LocalVar(arg.token, inferredType).bindAll to LocalVar
858                .compiler.codeMemberStack.peek.addLocal(newDefn)
859                (arg to IdentifierExpr).setDefinition(newDefn)
860                arg.type = newDefn.type
861                _bindImpArg(num, arg)
862                # the warning below is temporary till we sort out arg.bindImp vs calculating Best overload
863                if warn
864                    sugg = 'You may want to disambiguate this by declaring the variable explicitly.'
865                    .compiler.warning(this, 'Cobra has implicitly declared a variable "[newDefn.name] as [newDefn.type.name]" for an undefined out parameter variable but the type inferred may not be correct as the method called is overloaded. [sugg]')
866           
867    def _checkArgsAssignable(definition as BoxMember, args as IList<of Expr>, params as IList<of Param>) 
868        for i in args.count
869            arg = args[i]
870            param = params[i]
871            if arg.hasError
872                break
873            if arg inherits AssignExpr  # assignments in arguments have special treatment
874                break
875            assert arg.didBindImp
876            assert param.didBindInt
877            # check any in/out/inout expectations are matched
878            if param.direction <> arg.direction             
879                arg.recordError('Argument [i+1] of method "[_name]" expects [_toErrorPhrase(param.direction)] parameter, but the call is supplying [_toErrorPhrase(arg.direction)] one.')
880            if arg.canBeAssignedTo(param.type)
881                arg.contextType = param.type
882            else if param.type inherits GenericParam
883                # ..\Tests\240-generics\400-generic-methods\100-generic-method.cobra
884                # HACK for generic method args which need proper type checking like anything else
885                # Cobra needs to add type inference on generic method args instead of dropping this down to C#
886                pass
887            else
888                if false
889                    print
890                    print '<> definition = [definition]'
891                    print '<> arg = ' stop
892                    arg.writeDeepString
893                    print '<> arg.type =', arg.type
894                    print '<> param = ' stop
895                    param.writeDeepString
896                    print '<> param.type =', param.type
897                    print '<> param.ifInheritsStack.count =', param.ifInheritsStack.count
898                if arg.type inherits NilableType and not param.type inherits NilableType and arg.type.nonNil.isAssignableTo(param.type)
899                    arg.recordError('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]).')
900                else
901                    arg.recordError('Argument [i+1] of method "[_name]" expects type [param.type.name], but the call is supplying type [arg.type.name].')
902
903    def _toErrorPhrase(d as Direction) as String
904        branch d
905            on Direction.In, return 'a regular'
906            on Direction.Out, return 'an "out"'
907            on Direction.InOut, return 'an "inout"'
908            else, throw FallThroughException(d)
909
910    def _checkInOutArguments
911        for arg in .args
912            if arg.direction <> Direction.In
913                if not arg.hasError
914                    error as String?
915                    argDefi = arg.definition
916                    if argDefi inherits Property, error = 'A property'
917                    else if argDefi inherits Indexer, error = 'An indexer'
918                    if error, arg.recordError('[error] cannot be passed as an "[arg.direction.toString.toLower]" parameter.')
919
920    def toCobraSource as String is override
921        sb = StringBuilder()
922        sb.append(_name)
923        sb.append('(')
924        sep = ''
925        for arg in _args
926            sb.append(sep)
927            sb.append(arg.toCobraSource)
928            sep = ', '
929        sb.append(')')
930        return sb.toString
931
932
933class EnumCallExpr
934    is partial
935    inherits Expr
936    """
937    Represents an enumeration call like: AnchorStyle(Left, Right)
938    Created by CallExpr._bindImp
939    """
940
941    var _name as String
942    var _args as List<of Expr>
943    var _definition as EnumDecl?
944    var _members as List<of EnumMember>
945        """
946        Parallels the args with the enum members found during _bindImp.
947        """
948
949    cue init(token as IToken, name as String, args as List<of Expr>, definition as EnumDecl)
950        require name.isCapitalized
951        base.init(token)
952        _name = name
953        _args = args
954        _definition = definition
955        _members = List<of EnumMember>()
956   
957    get allExprs as Expr*
958        for expr in base.allExprs, yield expr
959        for arg in .args, for expr in arg.allExprs, yield expr
960
961    get name from var
962   
963    get args from var
964        has Subnodes
965   
966    get definition is override
967        return _definition
968
969    get enumDefinition from _definition
970   
971    get members from _members
972   
973    def toCobraSource as String is override
974        sb = StringBuilder()
975        sb.append(_name)
976        sb.append('(')
977        sep = ''
978        for arg in _args
979            sb.append(sep)
980            sb.append(arg.toCobraSource)
981            sep = ', '
982        sb.append(')')
983        return sb.toString
984
985    def _bindImp
986        base._bindImp
987        if not _definition
988            possible = .compiler.symbolForName(.name, false)
989            if not possible
990                .throwError('Cannot locate enumeration type "[.name]".')
991            else if possible inherits EnumDecl
992                _definition = possible
993            else
994                .throwError('"[.name]" is not an enumeration type.')
995        if not _type
996            _type = _definition
997        # TODO: support EnumName(someInt) and EnumName(someString) (and multiple args)
998        for arg in _args
999            if arg inherits IdentifierExpr
1000                member = _definition.memberForName(arg.name) to EnumMember?
1001                if member
1002                    arg.setDefinition(member)
1003                    _members.add(member# for use in code generation
1004                else
1005                    arg.recordError('Cannot find "[arg.name]" in enumeration "[.name]"')
1006
1007
1008class ForExpr inherits Expr is partial
1009
1010    var _nameExpr as NameExpr
1011    var _var as IVar?
1012    var _varNumber as int
1013    var _what as Expr
1014    var _whereExpr as Expr?
1015    var _getExpr as Expr
1016    var _start as Expr?
1017    var _stop as Expr?
1018    var _step as Expr?
1019
1020    cue init(token as IToken, nameExpr as NameExpr, what as Expr, stopExpr as Expr?,
1021                stepExpr as Expr?, whereExpr as Expr?, getExpr as Expr)
1022        base.init(token)
1023        _nameExpr = nameExpr
1024        _what = what
1025        _stop = stopExpr
1026        _step = stepExpr
1027        _whereExpr = whereExpr
1028        _getExpr = getExpr
1029
1030    def addSubFields
1031        base.addSubFields
1032        .addField('nameExpr', _nameExpr)
1033        .addField('var', _var)
1034        .addField('what', _what)
1035        .addField('whereExpr', _whereExpr)
1036        .addField('getExpr', _getExpr)
1037        .addField('start', _start)
1038        .addField('stop', _stop)
1039        .addField('step', _step)
1040
1041    get allExprs as Expr*
1042        for expr in base.allExprs, yield expr
1043        for expr in .nameExpr.allExprs, yield expr
1044        for expr in .what.allExprs, yield expr
1045        if .whereExpr, for expr in .whereExpr.allExprs, yield expr
1046        for expr in .getExpr.allExprs, yield expr
1047        if .start, for expr in .start.allExprs, yield expr
1048        if .stop, for expr in .stop.allExprs, yield expr
1049        if .step, for expr in .step.allExprs, yield expr
1050
1051    get nameExpr from _nameExpr
1052
1053    get what from _what
1054   
1055    get whereExpr from _whereExpr
1056
1057    get getExpr from _getExpr
1058
1059    get start from _start
1060
1061    get stop from _stop
1062
1063    get step from _step
1064
1065    get willChangeVar as bool is override
1066        # TODO: ack. kind of weird. if the variable is a new one and its not read afterwards (without first being set) then no side effects.
1067        if _what.willChangeVar, return true
1068        if _whereExpr and _whereExpr.willChangeVar, return true
1069        if _getExpr.willChangeVar, return true
1070        return false
1071
1072    def _bindImp
1073        base._bindImp
1074        _what.bindImp  # bind first because it may be needed for type inference
1075        whatType = _what.type to !
1076        if whatType inherits AnyIntType
1077            if _stop
1078                _start = _what
1079                _stop.bindImp
1080            else
1081                _start = IntegerLit(.token.copy('INTEGER_LIT', '0'), 0).bindImp
1082                _stop = _what
1083            if not _step
1084                _step = IntegerLit(.token.copy('INTEGER_LIT', '1'), 1)
1085            _step.bindImp
1086        else if not whatType.isEnumerable
1087            .throwError('Cannot enumerate values of type "[whatType.name]". You can enumerate anything enumerable (IEnumerable, IList, arrays, strings, etc.).')
1088        _var = .bindVar(_nameExpr)
1089        if _nameExpr.definition
1090            if _nameExpr.definition inherits IVar
1091                _var = _nameExpr.definition
1092            else
1093                .throwError('Expecting a variable not a [_nameExpr.definition.getType.name].')  # TODO: what's the best way to report what was found?
1094        else
1095            assert _nameExpr.hasError, _nameExpr
1096        _varNumber = .compiler.curBox.makeNextPrivateSerialNumber
1097        if _whereExpr
1098            _whereExpr.bindImp
1099            if _whereExpr.type is not .compiler.boolType
1100                _whereExpr = TruthExpr(_whereExpr).bindAll to TruthExpr  # CC: axe cast when have "as same"
1101        _getExpr.bindImp
1102        ilist = .compiler.listOfType
1103        _type = ilist.constructedTypeFor([_getExpr.type to !])
1104
1105    def inferredType as IType? is override
1106        assert _what.type
1107        return if(_what.type inherits AnyIntType, _what.type, _what.type.innerType)
1108
1109
1110class IdentifierExpr
1111    is partial
1112    inherits NameExpr
1113    implements IPotentialTypeExpr
1114
1115    cue init(token as IToken)
1116        .init(token, token.text)
1117
1118    cue init(token as IToken, name as String)
1119        base.init(token)
1120        _name = name
1121
1122    cue init(token as IToken, definition as INamedNode)
1123        base.init(token)
1124        _name = definition.name
1125        _definition = definition
1126
1127    def setDefinition(value as INamedNode?)
1128        # cannot override definition with a set, because base does not do a set
1129        _definition = value
1130
1131    get canBeStatement as bool is override
1132        return _definition inherits Method
1133
1134    get isCalling as bool is override
1135        assert _definition
1136        return _definition.isMethod
1137
1138    get isTypeReference as bool
1139        """
1140        Returns true if this identifier directly names a type.
1141        """
1142        require .didBindImp
1143        return .definition implements IType and .type.isDescendantOf(.compiler.typeType)
1144       
1145    get potentialType as IType?
1146        # overridden to return the type this identifier represents in those cases when it does represent a type such an "int" or a class
1147        assert .didBindImp
1148        assert .type
1149        assert .compiler
1150        if .type.isDescendantOf(.compiler.typeType) or .type inherits Box
1151            assert .definition
1152            defi = .definition
1153            if defi inherits IType
1154                return defi
1155            else if defi inherits BoxEvent
1156                # TODO: does execution ever get here?
1157                return defi.handlerType
1158            else
1159                return nil
1160        else
1161            return nil
1162
1163    def _bindImp is override
1164        base._bindImp
1165        if _definition is nil
1166            _definition = .findDefinition
1167            canBeUndottedMember = _name.canBeUndottedMemberName
1168            if _definition is nil and not canBeUndottedMember
1169                if _superNode inherits BinaryOpExpr
1170                    if _superNode.right is this
1171                        curBox = .compiler.boxStack.peek
1172                        definition = curBox.symbolForName(_name, true)
1173                        if definition
1174                            .throwError('You must refer to non-underscored members with a leading dot (.). Member name is "[_name]".')
1175            #print _name, canBeUndottedMember, _definition  # hops
1176            if _definition is nil and (not _superNode inherits BinaryOpExpr or .binarySuperNode.op <> 'ASSIGN')
1177                .throwUnknownIdError
1178                throw FallThroughException()
1179        if _type is nil
1180            if _definition
1181                if _definition inherits AbstractMethod or _definition inherits MemberOverload
1182                    _transformTo(DotExpr(.token, 'DOT', ThisLit(.token, isImplicit=true), MemberExpr(.token), isImplicit=true).bindAll)
1183                else
1184                    _type = _definition.typeForIdentifier
1185                    _receiverType = _definition.typeForReceiver
1186            else
1187                if .binarySuperNode inherits AssignExpr and this is .binarySuperNode.left
1188                    pass  # let the AssignExpr have its chance at type inference
1189                else
1190                    .throwUnknownIdError
1191                    throw FallThroughException()
1192
1193    def findDefinition as INamedNode?
1194        definition as INamedNode?
1195        canBeUndottedMember = _name.canBeUndottedMemberName
1196        if canBeUndottedMember
1197            # assert .compiler.boxStack.count TODO: identifier expr is being used by PostCallExpr which is used for attribute calls
1198            definition = .compiler.symbolForName(_name, false)
1199        else
1200            # local var ref: foo
1201            if .compiler.codeMemberStack.count  # could be: var _x = y   or: has foo
1202                definition = .compiler.findLocal(_name)
1203            if definition is nil
1204                definition = .compiler.symbolForName(_name, false, true)  # 3rd party DLLs have lowercase class names like iConnection
1205        return definition       
1206
1207    def postBindImp is override
1208        if _definition and not (.superNode inherits AbstractAssignExpr and .binarySuperNode.left is this)
1209            _definition.isUsed = true
1210        .help
1211
1212    def throwUnknownIdError
1213        require .name.length
1214        name = .name
1215        msg = 'Cannot find "[name]".'
1216        sug = .compiler.suggestionForUnknown(name)
1217        if sug and sug.length
1218            msg += ' Maybe you should try "[sug]".'
1219        else
1220            # TODO: check for a local with same name but different case
1221            if .compiler.boxStack.count  # could be empty stack for assembly level attributes
1222                sugg = _suggestionsMessage(.compiler.curBox.suggestionsForBadMemberName(name))
1223                # example suggs:
1224                #     There is a member named "x" with a similar name.
1225                #     There are members with similar names including "x" and "y".
1226                assert sugg.length implies sugg.endsWith('.')
1227                msg += sugg
1228        .throwError(msg)
1229
1230    def postBindImpAssertType is override
1231        # TODO: document why this is disabled
1232        pass
1233
1234    def toCobraSource as String is override
1235        return _name
1236
1237    def whoseTypeIsMessage as String
1238        """
1239        Customized to return '' for boxes.
1240        """
1241        # TODO: should cover other types like `int`. maybe that's a TypeExpr?
1242        return if(.definition inherits Box, '', base.whoseTypeIsMessage)
1243
1244
1245class IfExpr
1246    is partial
1247    inherits Expr
1248
1249    var _cond as Expr
1250    var _tpart as Expr
1251    var _fpart as Expr
1252
1253    cue init(token as IToken, cond as Expr, tpart as Expr, fpart as Expr)
1254        ensure .cond == cond and .tpart == tpart and .fpart == fpart
1255        base.init(token)
1256        _cond, _tpart, _fpart = cond, tpart, fpart
1257
1258    get allExprs as Expr*
1259        for expr in base.allExprs, yield expr
1260        for expr in .cond.allExprs, yield expr
1261        for expr in .tpart.allExprs, yield expr
1262        for expr in .fpart.allExprs, yield expr
1263
1264    get cond from var
1265   
1266    get tpart from var
1267   
1268    get fpart from var
1269
1270    def addSubFields is override
1271        base.addSubFields
1272        .addField('cond', .cond)
1273        .addField('tpart', .tpart)
1274        .addField('fpart', .fpart)
1275
1276    get willChangeVar as bool is override
1277        if base.willChangeVar, return true
1278        if .cond.willChangeVar, return true
1279        if .tpart.willChangeVar, return true
1280        if .fpart.willChangeVar, return true
1281        return false
1282
1283    def _bindImp is override
1284        and ensure
1285            .cond.hasError or .tpart.hasError or .fpart.hasError implies .hasError
1286            .hasError implies .type == .compiler.passThroughType
1287        body
1288            base._bindImp
1289            hadError = false
1290            try
1291                _cond.bindImp
1292            catch ne as NodeException
1293                .compiler.recordError(ne)
1294                hadError = true
1295            if not hadError and not _cond.type.isDescendantOf(.compiler.boolType)
1296                _cond = TruthExpr(_cond).bindAll to TruthExpr  # CC: axe cast when "as same"
1297            try
1298                _tpart.bindImp
1299            catch ne as NodeException
1300                .compiler.recordError(ne)
1301                hadError = true
1302            try
1303                _fpart.bindImp
1304            catch ne as NodeException
1305                .compiler.recordError(ne)
1306                hadError = true
1307            if hadError
1308                _type = .compiler.passThroughType
1309            else
1310                assert _tpart.type
1311                assert _fpart.type
1312                _type = _tpart.type.greatestCommonDenominatorWith(_fpart.type to !)
1313
1314    def toCobraSource as String is override
1315        return 'if([.cond.toCobraSource], [.tpart.toCobraSource], [.fpart.toCobraSource])'
1316
1317
1318class IndexExpr inherits Expr is partial
1319    """
1320    May transform to a TypeExpr for cases like "int[]".
1321    """
1322
1323    var _target as Expr
1324    var _args as List<of Expr>
1325    var _definition as IMember?
1326
1327    cue init(token as IToken, target as Expr, args as List<of Expr>)
1328        base.init(token)
1329        _target, _args = target, args
1330
1331    def addSubFields is override
1332        base.addSubFields
1333        .addField('target', _target)
1334        .addField('args', _args)
1335
1336    get allExprs as Expr*
1337        for expr in base.allExprs, yield expr
1338        for expr in .target.allExprs, yield expr
1339        for arg in .args, for expr in arg.allExprs, yield expr
1340
1341    get definition is override
1342        return _definition
1343
1344    get memberDefinition from _definition
1345
1346    get target from var
1347
1348    get args from var
1349        has Subnodes
1350
1351    get willChangeVar as bool is override
1352        if _target.willChangeVar, return true
1353        for arg in _args, if arg.willChangeVar, return true
1354        return false
1355
1356    def _bindImp is override
1357        base._bindImp
1358        _target.bindImp
1359        args = _args
1360        if args.count == 0
1361            if _target.isKindOf(.compiler.typeType)
1362                if _target inherits IPotentialTypeExpr and (pt = (_target to IPotentialTypeExpr).potentialType)
1363                    _transformTo(TypeExpr(.token, .typeProvider.arrayType(pt to !)))
1364                    return
1365                else
1366                    .throwError('Unknown array type.')
1367            else
1368                .throwError('Invalid index expression or array type.')
1369        hasArgError = false
1370        for arg in args
1371            try
1372                arg.bindImp
1373            catch ne as NodeException
1374                hasArgError = true
1375                .compiler.recordError(ne)
1376        target = .target
1377        if hasArgError or target.hasError
1378            _type = .compiler.passThroughType
1379        else if _type is nil
1380            _definition = target.memberForName(r'[]')
1381            if _definition is nil
1382                if target.receiverType is .compiler.passThroughType
1383                    _type = .compiler.passThroughType
1384                    return
1385                if target.receiverType.isDynamic
1386                    _type = .compiler.nilableDynamicType
1387                    return
1388                .throwError('Cannot find an indexer in "[target.toCobraSource]" whose type is "[target.receiverType.name]".')
1389            assert _definition
1390            if _definition inherits MemberOverload
1391                # to-do
1392                pass
1393            else if _definition inherits Indexer
1394                _bindImpIndexer(args, _definition)
1395            else
1396                throw FallThroughException(_definition)
1397            _type = _definition.resultType
1398            assert _type
1399           
1400    def _bindImpIndexer(args as List<of Expr>, indexer as Indexer)
1401        params = indexer.params
1402        hasVari = any for p in params get p.type inherits VariType
1403        if hasVari
1404            # to-do
1405            return
1406           
1407        if not hasVari and args.count <> params.count
1408            .throwError('The indexer is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.')
1409        for i in 0 : args.count
1410            arg = args[i]
1411            param = params[i]
1412            if arg.hasError, break
1413            assert arg.didBindImp, arg
1414            assert param.didBindInt, param
1415            if arg.canBeAssignedTo(param.type)
1416                arg.contextType = param.type
1417            else
1418                if false
1419                    print '<> arg = ' stop
1420                    arg.writeDeepString
1421                    print '<> param = ' stop
1422                    param.writeDeepString
1423                if arg.type inherits NilableType and not param.type inherits NilableType and arg.type.nonNil.isAssignableTo(param.type)
1424                    .throwError('Argument [i+1] of indexer expects a non-nilable type ([param.type.name]), but the call is supplying a nilable type ([arg.type.name]).')
1425                else
1426                    .throwError('Argument [i+1] of indexer expects type [param.type.name], but the call is supplying type [arg.type.name].')
1427                       
1428    def toCobraSource as String is override
1429        sb = StringBuilder()
1430        sb.append(_target.toCobraSource)
1431        sb.append(r'[')
1432        sep = ''
1433        for arg in _args
1434            sb.append(sep)
1435            sb.append(arg.toCobraSource)
1436            sep = ', '
1437        sb.append(']')
1438        return sb.toString
1439
1440
1441class IsNilExpr
1442    is partial
1443    inherits Expr
1444
1445    var _expr as Expr
1446    get expr from var
1447
1448
1449    cue init(token as IToken, expr as Expr)
1450        base.init(token)
1451        _expr = expr
1452
1453    get allExprs as Expr*
1454        for expr in base.allExprs, yield expr
1455        for expr in .expr.allExprs, yield expr
1456
1457    get willChangeVar as bool is override
1458        if _expr.willChangeVar, return true
1459        return false
1460
1461    def _bindImp is override
1462        base._bindImp
1463        _expr.bindImp
1464        _type = .compiler.boolType
1465
1466
1467class IsNotNilExpr
1468    is partial
1469    inherits Expr
1470
1471    var _expr as Expr
1472
1473    cue init(token as IToken, expr as Expr)
1474        base.init(token)
1475        _expr = expr
1476
1477    get allExprs as Expr*
1478        for expr in base.allExprs, yield expr
1479        for expr in .expr.allExprs, yield expr
1480
1481    get expr from var
1482
1483    get willChangeVar as bool is override
1484        if _expr.willChangeVar, return true
1485        return false
1486
1487    def _bindImp is override
1488        base._bindImp
1489        _expr.bindImp
1490        _type = .compiler.boolType
1491
1492    def toCobraSource as String is override
1493        return '[_expr.toCobraSource] is not nil'
1494
1495
1496class MemberExpr inherits CallOrMemberExpr implements IDotRightExpr is partial
1497    """
1498    Example members are fields, properties and methods without arguments.
1499
1500    A trace of .definiton.getType.name at the end of _bindImp gives the set:
1501        BoxVar
1502        Class
1503        EnumMember
1504        Initializer
1505        Interface
1506        MemberOverload
1507        Method
1508        NameSpace
1509        Property
1510
1511    Presumable Struct could be in there as well.
1512   
1513    Note that DotExpr will do some transformations such as Foo.Bar to a TypeExpr if Bar is a type.
1514    """
1515
1516    var _definition as IMember?
1517    var _isReference as bool
1518
1519    cue init(token as IToken)
1520        .init(token, token.text)
1521
1522    cue init(token as IToken, name as String)
1523        base.init(token, name)
1524
1525    def addMinFields is override
1526        base.addMinFields
1527        .addField('name', _name)
1528        .addField('isReference', _isReference)
1529
1530    get args as List<of Expr>
1531        return List<of Expr>()
1532
1533    get definition is override
1534        return _definition
1535
1536    get memberDefinition from _definition
1537
1538    pro isReference from var
1539        """
1540        Returns true if this member expression is a _reference_ to a member instead of an invocation to it.
1541        This is set by RefExpr and otherwise is false.
1542        """
1543
1544    get isCalling as bool is override
1545        assert _definition
1546        return _definition.isMethod
1547
1548    def _bindImp is override
1549        base._bindImp
1550        assert .superNode inherits DotExpr
1551        assert .binarySuperNode.op == 'DOT'
1552        assert .binarySuperNode.right is this
1553        left = .binarySuperNode.left
1554        if _definition is nil or _type is nil
1555            if not .binarySuperNode.left.didBindImp
1556                assert .binarySuperNode.left.hasError, .binarySuperNode.left
1557                # we get here for Cobra code like "obj.foo.bar" where "foo" is not found
1558                _type = .compiler.passThroughType
1559                return
1560            _definition = .binarySuperNode.left.memberForName(_mangleName(.name))
1561            if _definition
1562                _definition.isUsed = true
1563            else
1564                if .binarySuperNode.left.receiverType is .compiler.passThroughType
1565                    _type = .compiler.passThroughType
1566                    return
1567                if .binarySuperNode.left.receiverType.isDynamic
1568                    _type = .compiler.nilableDynamicType
1569                    return
1570                .throwCannotFindMemberError(left, _name)
1571            assert _definition
1572            if _definition inherits IType
1573                effectiveType as IType = .compiler.typeType  # namespace, class, interface
1574                receiverType as IType? = _definition
1575            else if _definition inherits MemberOverload and _definition.name == 'getType'
1576                # see Box.prepSystemObjectClass
1577                if .binarySuperNode.left.type.isSystemTypeClass
1578                    sysType = .compiler.typeType
1579                    for member in (_definition to MemberOverload).members
1580                        if member.isShared and member.resultType is sysType and member.params.count == 0
1581                            _definition = member
1582                            break
1583                effectiveType = _definition.resultType
1584                receiverType = nil
1585            else
1586                effectiveType = _definition.resultType
1587                receiverType = nil
1588            _type = effectiveType
1589
1590            if _definition inherits BoxMember
1591                _bindImpBoxMember(_definition, left)
1592                _checkVisibility(_definition, left)
1593            else
1594                # TODO: type access like enum, class, delegate
1595                pass
1596
1597            # TODO: there should be a subclass of BinaryOpExpr called DotExpr and it should do the following work and maybe even the work above.
1598            # TODO: _receiverType = receiverType
1599            .binarySuperNode.type = .type  # the type of foo.bar is what bar returns. A MemberExpr is the "bar" part.
1600            .binarySuperNode.receiverType = receiverType
1601           
1602        assert _type
1603
1604    def _bindImpBoxMember(defn as BoxMember, left as Expr)
1605        if .compiler.refExprLevel < 1
1606            # resolve overloads
1607            if defn inherits MemberOverload
1608                for member in defn.members
1609                    if member inherits AbstractMethod
1610                        if member.params.count == 0
1611                            _definition = member
1612                            didResolveOverload = true
1613                            break
1614                if not didResolveOverload
1615                    for member in defn.members
1616                        if member inherits AbstractMethod
1617                            if member.params.count == 1 and member.params[0].type inherits VariType
1618                                _definition = member
1619                                didResolveOverload = true
1620                                break
1621                if not didResolveOverload
1622                    .throwError('Could not find an overload for "[.name]" with zero arguments.')
1623            else if defn inherits AbstractMethod
1624                params = defn.params
1625                if params.count <> 0 and not (params.count == 1 and params[0].type inherits VariType)
1626                    .throwError('The method "[defn.name]" is expecting [params.count] argument[Utils.plural(params)], but no arguments are being supplied in this call.')
1627                else if .name <> 'getType'  # ug, more special handling for getType
1628                    box = .binarySuperNode.left.type to? Box
1629                    if box
1630                        # If there are multiple methods with the name, but this is not an overload,
1631                        # then prefer the one returning a generic type.
1632                        # This is for getEnumerator:
1633                        #     def getEnumerator as IEnumerator
1634                        #     def getEnumerator as IEnumerator<of T>
1635                        members = box.allMembersForName(.name)
1636                        if members.count > 1
1637                            for mbr in members
1638                                if mbr.resultType inherits Box and (mbr.resultType to Box).isGeneric
1639                                    _definition = mbr
1640                                    _type = mbr.resultType
1641                                    break
1642
1643    def toCobraSource as String is override
1644        return _name
1645
1646
1647class OldExpr
1648    is partial
1649    inherits Expr
1650    """
1651    The `old` expression used in contracts such as:
1652        ensure .count == old .count + 1
1653    """
1654
1655    cue init(token as IToken, expr as Expr)
1656        base.init(token)
1657        _expr = expr
1658
1659    get allExprs as Expr*
1660        for expr in base.allExprs, yield expr
1661        for expr in .expr.allExprs, yield expr
1662
1663    get expr from var as Expr
1664   
1665    get name as String
1666        return 'old'
1667
1668    get willChangeVar as bool is override
1669        if _expr.willChangeVar, return true
1670        return false
1671
1672    def addSubFields is override
1673        base.addSubFields
1674        .addField('expr', _expr)
1675
1676    def toCobraSource as String is override
1677        return 'old ' + .expr.toCobraSource
1678
1679    def _bindImp is override
1680        base._bindImp
1681        .curCodeMember.addOldExpr(this)  # will set the sharpVarName
1682        _expr.bindImp
1683        _type = _expr.type
1684        assert _type
1685
1686
1687class PostCallExpr
1688    is partial
1689    inherits Expr
1690    """
1691    Covers cases like:
1692        Car()
1693        someDelegate()
1694        obj[i]('x')
1695        String[](10)
1696        Foo.Bar.Baz[](0)
1697
1698    For "bar.foo('x')" that's a binary dot expression with a CallExpr on the right hand side.
1699   
1700    Some transformations are required. For example, the parser creates a PostCallExpr for these:
1701        EnumType(EnumMember1, EnumMember2) -- should be: EnumCallExpr
1702        _method(arg1, arg2) -- should be: DotExpr containing a CallExpr
1703    """
1704
1705    shared
1706   
1707        def isTargetAcceptable(expr as Expr) as bool
1708            """
1709            Returns true if the given expression can be called.
1710            That currently includes TypeExpr, IndexExpr and IdentifierExpr.
1711            Do not pass expressions that fail this test to the PostCallExpr init.
1712            """
1713            return expr inherits TypeExpr or expr inherits IndexExpr or expr inherits IdentifierExpr
1714
1715    var _expr as Expr
1716    var _args as List<of Expr>
1717    var _hasKeywordArg as bool  # such as Foo(1, a=2)
1718    var _isForAttribute as bool  # see Attributes.cobra
1719    var _helperMethod as Method?
1720
1721    cue init(token as IToken, expr as Expr, args as List<of Expr>)
1722        require .isTargetAcceptable(expr)
1723        base.init(token)
1724        _expr = expr
1725        _args = args
1726
1727    def addSubFields is override
1728        base.addSubFields
1729        .addField('expr', _expr)
1730        .addField('args', _args)
1731        .addField('hasKeywordArg', .hasKeywordArg)
1732        .addField('isForAttribute', .isForAttribute)
1733
1734    get allExprs as Expr*
1735        for expr in base.allExprs, yield expr
1736        for expr in .expr.allExprs, yield expr
1737        for arg in .args, for expr in arg.allExprs, yield expr
1738
1739    get canBeStatement as bool is override
1740        return true
1741
1742    get expr from var
1743
1744    get args from var
1745        has Subnodes
1746
1747    get hasKeywordArg from var
1748
1749    pro isForAttribute from var
1750
1751    get name as String
1752        e = .expr
1753        if e inherits IdentifierExpr, return e.name
1754        assert .didBindImp
1755        if e inherits TypeExpr, return e.realType.name
1756        if e inherits IndexExpr, return e.toCobraSource
1757        throw FallThroughException(e)
1758
1759    get willChangeVar as bool is override
1760        if _expr.willChangeVar, return true
1761        for arg in _args, if arg.willChangeVar, return true
1762        return false
1763
1764    def replaceChild(find as INode, replace as INode) as bool
1765        # Because _bindImp invokes .bindImp directly on the .right of an AssignExpr argument,
1766        # the base class thinks this PostCallExpr is the super node and fails to find a child to replace.
1767        # The real super node is the AssignExpr whose .bindImp was skipped because it's not
1768        # a true AssignExpr--it's just syntax for init'ing properties during instance creation.
1769        r = base.replaceChild(find, replace)
1770        if not r
1771            for arg in .args
1772                if arg inherits AssignExpr
1773                    r = arg.replaceChild(find, replace)
1774                    if r, break
1775        return r
1776
1777    def _bindImp is override
1778        base._bindImp
1779        expr = .expr
1780
1781        if expr inherits IdentifierExpr
1782            if expr.name.startsWith('_') and not .compiler.symbolForName(expr.name, false) inherits BoxVar
1783                # method invocation
1784                # _foo(x) --> ._foo(x)
1785                newCall = CallExpr(.token, .name, .args, true)
1786                dotted = DotExpr(.token, 'DOT', ThisLit(.token, isImplicit=true), newCall, isImplicit=true).bindImp
1787                _type = dotted.type to IType
1788                _transformTo(dotted)
1789                return
1790
1791        expr = expr.bindImp
1792
1793        if expr.definition inherits EnumDecl
1794            enumCall = EnumCallExpr(.token, expr.toCobraSource, .args, expr.definition to EnumDecl).bindImp  # CC: axe cast
1795            _type = enumCall.type to IType
1796            _transformTo(enumCall)
1797            return
1798
1799        for arg in .args
1800            try
1801                if arg inherits AssignExpr
1802                    _hasKeywordArg = true
1803                    if not arg.left inherits IdentifierExpr
1804                        .recordError('General purpose assignments are not allowed as arguments. Assignment syntax can only be used for keyword arguments.')
1805                    else if not expr.receiverType.isDynamicOrPassThrough
1806                        propertyName = (arg.left to IdentifierExpr).name
1807                        if expr.memberForName(propertyName) is nil
1808                            .recordCannotFindMemberError(expr, propertyName)
1809                    arg.right.bindImp  # 'x=y' has special treatment in arguments
1810                else
1811                    if _hasKeywordArg
1812                        .throwError('Cannot have a non-keyword argument ("[arg.toCobraSource]") after a keyword argument. All positional arguments must come before all keyword arguments.')
1813                    arg.bindImp
1814            catch ne as NodeException
1815                .compiler.recordError(ne)
1816
1817        if expr inherits TypeExpr
1818            # instantiation
1819            assert expr.containedType
1820            _type = expr.containedType
1821        else if expr inherits IndexExpr
1822            pass
1823        else if expr inherits IdentifierExpr
1824            assert expr.type  # or it should have throw an exception when binding
1825            if expr.type.isDynamicOrPassThrough
1826                pass
1827            else if expr.definition inherits IType
1828                pass
1829            else if expr.type.nonNil.isDescendantOf(.compiler.delegateType)
1830                pass
1831            else if expr.type.nonNil.isDescendantOf(.compiler.typeType)
1832                pass
1833            else
1834                .throwError('Cannot call "[expr.toCobraSource]" of type "[expr.type.name]".')
1835        else
1836            # one example where this happened: x to SomeType()
1837            # which yielded: PostCallExpr(expr=ToExpr(...))
1838            # TODO: can probably make this an error now
1839            assert false, _expr
1840
1841        if _type is nil
1842            assert expr.type is not nil
1843            exprType = expr.type.nonNil
1844            if exprType inherits MethodSig
1845                _type = exprType.returnType
1846            else if exprType.isDescendantOf(.compiler.delegateType)
1847                member = exprType.memberForName('invoke')
1848                if member inherits Method
1849                    _type = member.resultType
1850                else
1851                    .throwError('Cannot find a single "invoke" method for "_eventType.name".')
1852            else if expr.receiverType and expr.receiverType.isSystemTypeClass
1853                _type = .compiler.dynamicType
1854            else if exprType inherits Box
1855                _type = expr.receiverType  # for example, an IdentifierExpr of 'SomeClass' has a .receiverType of that class
1856                _handleClassInitializer
1857            else if exprType.isDynamic
1858                _type = .compiler.nilableDynamicType
1859            else if expr.definition inherits NameSpace
1860                .throwError('Cannot instantiate the namespace "[(expr.definition to NameSpace).fullName]".')
1861            else
1862                assert false, expr
1863
1864        if .hasKeywordArg and not .isForAttribute and not .hasError
1865            _makeHelperMethod
1866           
1867    def _makeHelperMethod
1868        """
1869        Add a private helper method to the current box to support extended initializer.
1870
1871        Foo(expr0, expr1, bar=expr2) -->
1872            call:
1873                _ch_ext_init_1207(expr0, expr1, expr2)
1874            def:
1875                def _ch_ext_init_1207(arg0 as int, arg1 as int, /#bar=#/arg2 as int)
1876                    obj = Foo(arg0, arg1)
1877                    obj.bar = arg2
1878                    return obj
1879        """
1880        box, token = .compiler.curBox, box.token.copy
1881        paramsForDecl = List<of Param>()
1882        argsForInitCall = List<of Expr>()  # args to pass to `Foo(arg0, arg1)`
1883        propsToSet = List<of AssignExpr>()  # props to set as `obj.bar = arg2` etc.
1884        firstPropArg = -1
1885        for i, arg in .args.numbered
1886            if arg inherits AssignExpr
1887                propsToSet.add(arg)
1888                if firstPropArg == -1, firstPropArg = i
1889                propertyName = (arg.left to IdentifierExpr).name
1890                argType = _expr.memberForName(propertyName).resultType
1891                arg = arg.right
1892            else
1893                argsForInitCall.add(if(arg inherits NilLiteral, arg, IdentifierExpr(token.copy('ID', 'arg[i]'))))
1894                argType = arg.type to !
1895            paramsForDecl.add(Param(box.token.copy('ID', 'arg[i]'), argType))
1896        name = '_ch_ext_init_[.serialNum]'  # ch = class helper, ext = extended, init = initializer
1897        if .compiler.codeMemberStack.count
1898            curMember = .compiler.curCodeMember
1899            genericParams = if(curMember inherits Method, List<of IType>((curMember to Method).genericParams), List<of IType>())
1900        else
1901            genericParams = List<of IType>()
1902        m = Method(Token.empty, token.copy('ID', name), box, name, genericParams, paramsForDecl, _type, nil, ['shared', 'private'], AttributeList(), '', isCompilerGenerated=true)
1903        m.locals.add(LocalVar(token.copy('ID', 'obj'), .type))
1904
1905        objId = IdentifierExpr(token.copy('ID', 'obj'), 'obj')
1906        callExpr = PostCallExpr(token.copy('ID', .type.name), IdentifierExpr(token.copy('ID', .type.name), .type), argsForInitCall)
1907        assign = AssignExpr(token.copy('ASSIGN', '='), 'ASSIGN', objId, callExpr)
1908        m.addStmt(assign)
1909
1910        i = firstPropArg
1911        for propSetExpr in propsToSet
1912            propName = (propSetExpr.left to IdentifierExpr).name
1913            memberExpr = DotExpr(token.copy('DOT', '.'), 'DOT', IdentifierExpr(token.copy('ID', 'obj')), MemberExpr(token.copy('ID', propName)))
1914            assign = AssignExpr(token.copy('ASSIGN', '='), 'ASSIGN', memberExpr, IdentifierExpr(token.copy('ID', 'arg[i]')))
1915            m.addStmt(assign)
1916            i += 1
1917
1918        retStmt = ReturnStmt(token.copy('RETURN', 'return'), IdentifierExpr(token.copy('ID', 'obj'), 'obj'))
1919        m.addStmt(retStmt)
1920
1921        if .compiler.isBindingInt, m.bindInt  # happens for: var foo = Bar(baz=1)
1922        else, m.bindAll
1923        box.addDecl(m)
1924        _helperMethod = m
1925
1926    def toCobraSource as String is override
1927        sb = StringBuilder()
1928        sb.append(_expr.toCobraSource)
1929        sb.append('(')
1930        sep = ''
1931        for arg in .args
1932            sb.append(sep)
1933            sb.append(arg.toCobraSource)
1934            sep = ', '
1935        sb.append(')')
1936        return sb.toString
1937
1938    def _handleClassInitializer
1939        initCall as Initializer?
1940        if .args.count > 0
1941            type = .compiler.symbolForName(.name, false, true) to IType?
1942            if type is not nil
1943                possibleCalls = type.memberForName('cue.init')
1944                if possibleCalls is nil
1945                    pass # TODO: determine if this is an error or if support for base class init functions need to be added
1946                else if possibleCalls inherits MemberOverload
1947                    initCall = possibleCalls.computeBestOverload(.args, nil, false) to Initializer?
1948                else if possibleCalls inherits Initializer
1949                    initCall = possibleCalls
1950                #else
1951                    # TODO: determine if this is an error
1952        #print initCall
1953        if initCall is not nil and .args.count == initCall.params.count
1954            for i, arg in .args.numbered
1955                if arg.type is not nil and arg.type.isDynamic
1956                    # set context type so that backend can type cast it correctly
1957                    arg.contextType = initCall.params[i].type
1958        #else
1959            # TODO: warning or error?
1960
1961
1962class RefExpr inherits Expr is partial
1963    """
1964    A `ref` expression is used to refer to a method without invoking it:
1965        ref obj.foo
1966        ref _foo
1967    """
1968
1969    cue init(token as IToken, expr as Expr)
1970        base.init(token)
1971        # TODO: can the types of expressions be limited?
1972        _expr = expr
1973
1974    def addSubFields
1975        base.addSubFields
1976        .addField('expr', _expr)
1977
1978    get allExprs as Expr*
1979        for expr in base.allExprs, yield expr
1980        for expr in .expr.allExprs, yield expr
1981
1982    get expr from var as Expr
1983
1984    get willChangeVar as bool is override
1985        if _expr.willChangeVar, return true
1986        return false
1987
1988    def _bindImp is override
1989        base._bindImp
1990        .compiler.refExprLevel += 1
1991        try
1992            _expr.bindImp
1993        catch ne as NodeException
1994            # TODO: is this really needed? if so, should this be handled universally rather than just at this site?
1995            if ne inherits NodeMultiException
1996                if ne.exceptions.count == 1
1997                    ne = ne.exceptions[0]
1998                    throw ne
1999            throw
2000        finally
2001            .compiler.refExprLevel -= 1
2002        if _expr inherits DotExpr
2003            right = _expr.right
2004            if right inherits MemberExpr
2005                if right.definition is nil
2006                    # happens from error recovery such as: obj = BadClassName() ... ref obj.foo
2007                    pass
2008                else if right.definition inherits AbstractMethod or right.definition inherits MemberOverload
2009                    right.isReference = true
2010                else
2011                    .throwError('Only methods can be referenced, not [right.definition.englishName].')
2012            else if right inherits CallExpr
2013                .throwError('Cannot call a method that is preceded by `ref`.')
2014            else
2015                throw FallThroughException([this, _expr, right])
2016        else if _expr inherits IdentifierExpr
2017            if _expr.definition inherits AbstractMethod
2018                pass
2019            else
2020                .throwError('Only methods can be referenced, not [Utils.pluralize(_expr.definition.englishName)].')
2021        else
2022            .throwError('Unexpected reference. Refer to methods after `ref` or remove `ref`.')
2023        _type = .compiler.passThroughType # TODO: Set a real type such as .compiler.delegateType
2024        # TODO: need to do something more sophisticated like overriding:    def canBeAssignedTo(type as IType) as bool
2025
2026    def toCobraSource as String is override
2027        return 'ref ' + _expr.toCobraSource
2028
2029
2030class SharpExpr
2031    is partial
2032    inherits Expr
2033
2034    var _expr as StringLit?
2035    var _sharpSource as String?
2036
2037    cue init(token as IToken, sharpSource as String)
2038        base.init(token)
2039        _sharpSource = sharpSource
2040
2041    cue init(token as IToken, expr as Expr)
2042        base.init(token)
2043        if expr inherits StringLit  # TODO:? make this an arg type
2044            _expr = expr
2045        else
2046            assert false, r'sharp expression must be a String Literal (No substitutions) expr=[expr]'
2047
2048    get allExprs as Expr*
2049        for expr in base.allExprs, yield expr
2050        if .expr, for expr in .expr.allExprs, yield expr
2051
2052    get canBeStatement as bool is override
2053        return true
2054
2055    get sharpSource from var
2056   
2057    get expr from var
2058
2059    def toCobraSource as String is override
2060        if _sharpSource
2061            quote = if(.token.text.startsWith('sharp"'), '"', "'")
2062            return "sharp[quote][_sharpSource][quote]"
2063        else
2064            return "sharp'[_expr.toCobraSource]'"
2065
2066    def _bindImp is override
2067        base._bindImp
2068        _type = .compiler.passThroughType
2069        if _expr
2070            _expr.bindImp
2071
2072
2073class SliceExpr
2074    is partial
2075    inherits Expr
2076    """
2077    Just like Python slices.
2078    """
2079
2080    var _target as Expr
2081    var _start as Expr?
2082    var _stop as Expr?
2083    var _step as Expr?
2084
2085    cue init(token as IToken, target as Expr, start as Expr?, stopp as Expr?, stepp as Expr?)
2086        base.init(token)
2087        _target = target
2088        _start = start
2089        _stop = stopp
2090        _step = stepp
2091
2092    def addSubFields
2093        base.addSubFields
2094        .addField('target', _target)
2095        .addField('start', _start)
2096        .addField('stop', _stop)
2097        .addField('step', _step)
2098
2099    get allExprs as Expr*
2100        for expr in base.allExprs, yield expr
2101        for expr in .target.allExprs, yield expr
2102        if .start, for expr in .start.allExprs, yield expr
2103        if .stop, for expr in .stop.allExprs, yield expr
2104        if .step, for expr in .step.allExprs, yield expr
2105
2106    get willChangeVar as bool is override
2107        if _target.willChangeVar, return true
2108        if _start and _start.willChangeVar, return true
2109        if _stop and _stop.willChangeVar, return true
2110        if _step and _step.willChangeVar, return true
2111        return false
2112
2113    get target from var
2114
2115    get start from var
2116
2117    get stop from var
2118
2119    get step from var
2120   
2121    def _bindImp
2122        base._bindImp
2123        intType = .compiler.intType
2124        try
2125            _target.bindImp
2126        catch ne as NodeException
2127            .compiler.recordError(ne)
2128        success
2129            if not _target.type.isSequenceLike and not _target.type inherits PassThroughType
2130                .throwError('Cannot slice values of type "[_target.type.name]". You can slice strings, arrays, IList and IList<of>.')
2131        if _start
2132            try
2133                _start.bindImp
2134            catch ne as NodeException
2135                .compiler.recordError(ne)
2136            success
2137                if _start.type.isDynamic
2138                    _start.contextType = intType
2139                else if not _start.isKindOf(intType)
2140                    _start.recordError('The start index of the slice is type "[_start.type.name]", but should be "int".')
2141        if _stop
2142            try
2143                _stop.bindImp
2144            catch ne as NodeException
2145                .compiler.recordError(ne)
2146            success
2147                if _stop.type.isDynamic
2148                    _stop.contextType = intType
2149                else if not _stop.isKindOf(intType)
2150                    _stop.recordError('The stop index of the slice is type "[_stop.type.name]", but should be "int".')
2151        if _step
2152            try
2153                _step.bindImp
2154            catch ne as NodeException
2155                .compiler.recordError(ne)
2156            success
2157                if _step.type.isDynamic
2158                    _step.contextType = intType
2159                else if not _step.isKindOf(intType)
2160                    _step.recordError('The step of the slice is type "[_step.type.name]", but should be "int".')
2161        if _target.hasError
2162            _type = .compiler.passThroughType
2163        else
2164            _type = _target.type
2165
2166    def toCobraSource as String is override
2167        sb = StringBuilder()
2168        sb.append(_target.toCobraSource)
2169        sb.append(r'[')
2170        if _start
2171            sb.append(_start.toCobraSource)
2172        sb.append(':')
2173        if _stop
2174            sb.append(_stop.toCobraSource)
2175        if _step
2176            sb.append(':')
2177            sb.append(_step.toCobraSource)
2178        sb.append(']')
2179        return sb.toString
2180
2181
2182class TruthExpr
2183    is partial
2184    inherits Expr
2185    """
2186    A truth expr wraps an expression such that it can be used where a bool is expected in .NET/C#.
2187    For example, if passed an integer typed expression, the truth expression will wrap it with a
2188    comparison 0!=expr.
2189    Statically typed bools are passed straight through.
2190    The `dynamic` and `dynamic?` types use a run-time service.
2191    Other nilable types are checked for nil.
2192    """
2193
2194    enum Treatment
2195        AsIs
2196        InvokeRuntime
2197        CompareToNull
2198        CompareToZero
2199        CompareToZeroChar
2200       
2201    var _expr as Expr
2202    var _origExpr as Expr
2203    var _notExpr as UnaryOpExpr?
2204    var _treatment as Treatment
2205   
2206    cue init(expr as Expr)
2207        .init(expr, nil)
2208
2209    cue init(expr as Expr, notExpr as UnaryOpExpr?)
2210        """
2211        Pass the notExpr if the truth expression is the target of the not operator.
2212        This affects warnings generated by TruthExpr.
2213        """
2214        base.init(expr.token)
2215        _origExpr = _expr = expr
2216        _notExpr = notExpr
2217
2218    def addMinFields is override
2219        base.addMinFields
2220        .addField('Treatment', _treatment)
2221
2222    def addSubFields is override
2223        base.addSubFields
2224        .addField('expr', _expr)
2225        .addField('Treatment', _treatment)
2226
2227    get allExprs as Expr*
2228        for expr in base.allExprs, yield expr
2229        for expr in .expr.allExprs, yield expr
2230
2231    get expr from var
2232
2233    get willChangeVar as bool is override
2234        if _expr.willChangeVar, return true
2235        if _origExpr.willChangeVar, return true
2236        # Don't check _notExpr which owns *this* expr:
2237        # if _notExpr and _notExpr.willChangeVar, return true
2238        return false
2239
2240    get notExpr from var
2241
2242    def _bindImp
2243        base._bindImp
2244        _expr.bindImp
2245        type = _expr.type
2246        if type is .compiler.boolType
2247            _treatment = Treatment.AsIs
2248        else if type inherits AbstractNumberType
2249            _treatment = Treatment.CompareToZero
2250        else if _expr inherits NilLiteral
2251            .compiler.warning(this, 'The value nil will always evaluate to false.')
2252            _expr = BoolLit(_expr.token, false)
2253            _expr.bindImp
2254        else if type inherits NilableType
2255            if type.nonNil inherits DynamicType or type.nonNil inherits PrimitiveType
2256                _treatment = Treatment.InvokeRuntime
2257            else
2258                _treatment = Treatment.CompareToNull
2259        else if type inherits DynamicType
2260            _treatment = Treatment.InvokeRuntime
2261        else if _expr.isKindOf(.compiler.passThroughType)
2262            _treatment = Treatment.InvokeRuntime
2263        else if type.isReference
2264            _treatment = Treatment.CompareToNull
2265        else if type inherits CharType
2266            _treatment = Treatment.CompareToZeroChar
2267        else if type inherits VoidType
2268            .throwError('Cannot determine truth because the method does not return a value.')
2269        else if type inherits Struct
2270            .throwError('Cannot determine truth from an arbitrary struct.')
2271        else
2272            throw FallThroughException(this)
2273        if type.isReference and not type inherits NilableType and not type inherits NilType and not type inherits PassThroughType and not type inherits DynamicType
2274            hint = 'You can remove the expression'
2275            if _notExpr
2276                if _expr.isKindOf(.compiler.stringType)
2277                    hint += ' or check for an empty string.'
2278                else if type inherits ArrayType
2279                    hint += ' or check for an empty array.'
2280                else if _expr.isKindOfCollection
2281                    hint += ' or check for an empty collection.'
2282                else
2283                    hint += '.'
2284                .compiler.warning(_notExpr, 'The expression "[_notExpr.toCobraSource]" (of type "[type.name]") will never evaluate to false because the expression is not nilable. [hint]')
2285            else
2286                if _expr.isKindOf(.compiler.stringType)
2287                    hint += ' or check for non-empty strings with ".length".'
2288                else if type inherits ArrayType
2289                    hint += ' or check for non-empty arrays with ".length".'
2290                else if _expr.isKindOfCollection
2291                    hint += ' or check for non-empty collections with ".count".'
2292                else
2293                    hint += '.'
2294                .compiler.warning(this, 'The expression "[_expr.toCobraSource]" (of type "[type.name]") will always evaluate to true because it is not nilable. [hint]')
2295        _type = .compiler.boolType
2296
2297    def toCobraSource as String is override
2298        return _origExpr.toCobraSource
2299
2300
2301class TypeExpr
2302    is partial
2303    inherits Expr
2304    implements IPotentialTypeExpr, ITypeProxy
2305    """
2306    Unlike the other expressions that implement IPotentialType, a TypeExpr always represents a type.
2307    Hence it implements ITypeProxy as well.
2308    """
2309   
2310    var _typeNode as ITypeProxy?
2311    var _containedType as IType?
2312
2313    cue init(token as IToken, typeNode as ITypeProxy)
2314        base.init(token)
2315        _typeNode = typeNode
2316
2317    cue init(typeNode as ITypeProxy)
2318        .init((typeNode to ISyntaxNode).token, typeNode)  # TODO: hmmm this is kind of weird
2319
2320    cue init(token as IToken, type as IType)
2321        base.init(token)
2322        _containedType = type
2323        _receiverType = type
2324
2325    get definition is override
2326        return _containedType
2327
2328    get _innerHasError as bool
2329        return base._innerHasError or (.typeNode and .typeNode.hasError)
2330
2331    get containedType from var
2332
2333    get potentialType as IType?
2334        return .realType
2335
2336    get realType as IType
2337        assert .didBindImp
2338        assert _containedType
2339        return _containedType to !
2340       
2341    get typeNode from var
2342
2343    def addRefFields is override
2344        base.addRefFields
2345        .addField('containedType', _containedType)
2346
2347    def addSubFields is override
2348        base.addSubFields
2349        .addField('typeNode', _typeNode)
2350
2351    def toCobraSource as String is override
2352        assert _containedType
2353        return _containedType.name
2354
2355    def _bindImp is override
2356        base._bindImp
2357        if not _containedType and _typeNode
2358            _containedType = _receiverType = _typeNode.realType
2359        _type = .compiler.typeType
2360
2361
2362class AllOrAnyExpr
2363    is abstract, partial
2364    inherits Expr
2365    """
2366    The base class for AllExpr and AnyExpr which have much in common.
2367   
2368    They are unary prefix operators taking something enumerable and returning a bool.
2369    """
2370
2371    var _expr as Expr
2372
2373    cue init(token as IToken, expr as Expr)
2374        base.init(token)
2375        _expr = expr
2376
2377    get allExprs as Expr*
2378        for expr in base.allExprs, yield expr
2379        for expr in .expr.allExprs, yield expr
2380
2381    get opName as String is abstract
2382
2383    get expr from var
2384
2385    get willChangeVar as bool is override
2386        if base.willChangeVar, return true
2387        if .expr.willChangeVar, return true
2388        return false
2389
2390    def addSubFields is override
2391        base.addSubFields
2392        .addField('expr', _expr)
2393
2394    def toCobraSource as String is override
2395        return '[.opName] [.expr.toCobraSource]'
2396
2397    def _bindImp
2398        base._bindImp
2399        _type = .compiler.boolType
2400        _expr.bindImp
2401        enumerable = .compiler.enumerableType
2402        if _expr.type.isDescendantOf(enumerable)
2403            pass
2404        else if _expr.type.isDynamic
2405            _expr.contextType = enumerable
2406        else
2407            .throwError('Expecting an enumerable expression after "[.opName]", but got an expression of type "[.expr.type.name]".')
2408
2409
2410class AllExpr
2411    is partial
2412    inherits AllOrAnyExpr
2413    """
2414    all <enumerable> --> true if all elements are true
2415    """
2416   
2417    cue init(token as IToken, expr as Expr)
2418        base.init(token, expr)
2419
2420    get opName as String is override
2421        return 'all'
2422   
2423
2424class AnyExpr
2425    is partial
2426    inherits AllOrAnyExpr
2427    """
2428    any <enumerable> --> true if any element is true
2429    """
2430   
2431    cue init(token as IToken, expr as Expr)
2432        base.init(token, expr)
2433
2434    get opName as String is override
2435        return 'any'
2436
2437
2438class UnaryOpExpr
2439    is partial
2440    inherits Expr
2441
2442    var _op as String
2443    var _expr as Expr
2444
2445    cue init(token as IToken, op as String, expr as Expr)
2446        require op in ['MINUS', 'PLUS', 'NOT', 'TILDE']
2447        base.init(token)
2448        _op = op
2449        _expr = expr
2450
2451    def addMinFields
2452        base.addMinFields
2453        .addField('op', _op)
2454
2455    def addSubFields
2456        base.addSubFields
2457        .addField('expr', _expr)
2458
2459    get allExprs as Expr*
2460        for expr in base.allExprs, yield expr
2461        for expr in .expr.allExprs, yield expr
2462
2463    get willChangeVar as bool is override
2464        if _expr.willChangeVar, return true
2465        return false
2466
2467    get op from var
2468
2469    get expr from var
2470
2471    def _bindImp is override
2472        base._bindImp
2473        op = .op
2474        expr = _expr
2475        expr.bindImp
2476        if _type is nil, _type = _expr.type
2477        branch op
2478            on 'MINUS', _type = expr.type
2479            on 'PLUS',  _type = expr.type
2480            on 'TILDE', _type = expr.type
2481            on 'NOT'
2482                if expr.type is not .compiler.boolType
2483                    _expr = TruthExpr(expr, this).bindAll to TruthExpr
2484                    _type = .compiler.boolType
2485            else
2486                throw FallThroughException(op)
2487
2488        if op in ['MINUS', 'PLUS']  # TODO: 'TILDE' for ints?
2489            if expr inherits AtomicLiteral
2490                if expr.isNumeric
2491                    token = expr.token.copy
2492                    branch op
2493                        on 'PLUS'
2494                            pass
2495                        on 'MINUS'
2496                            token.value = -(token.value to dynamic)
2497                            token.text = if(token.text.startsWith('-'), token.text[1:], '-' + token.text)
2498                        else
2499                            throw FallThroughException(op)
2500                    nodeType = expr.getType
2501                    expr = nodeType(token)
2502                    expr.type = _type
2503                    expr.bindAll
2504                    _transformTo(expr)
2505
2506    def toCobraSource as String is override
2507        branch _op
2508            on 'MINUS', op = '-'
2509            on 'PLUS',  op = '+'
2510            on 'TILDE', op = '~'
2511            on 'NOT',   op = 'not '
2512            else, throw FallThroughException(_op)
2513        return op + _expr.toCobraSource
2514
2515
2516##
2517## Literals
2518##
2519
2520class Literal
2521    is partial
2522    inherits Expr
2523
2524    cue init(token as IToken)
2525        base.init(token)
2526
2527
2528class AtomicLiteral
2529    is partial
2530    inherits Literal
2531
2532    var _text as String
2533
2534    cue init(token as IToken)
2535        base.init(token)
2536        _text = token.text
2537
2538    def isEquivalentTo(expr as Expr) as bool
2539        r = base.isEquivalentTo(expr)
2540        if not r
2541            r = expr.typeOf == .typeOf and (expr to AtomicLiteral)._text == _text
2542        return r
2543
2544    get isNumeric as bool
2545        """
2546        Returns true if the literal is numeric such that + and - could apply to it.
2547        The default implementation returns false.
2548        """
2549        return false
2550
2551    def bindImp as dynamic is override
2552        base.bindImp
2553        .checkType
2554        return .bindImpResult
2555
2556    def checkType
2557        assert _type, this
2558
2559    def _bindImp is override
2560        base._bindImp
2561
2562    def toCobraSource as String is override
2563        return _text
2564
2565
2566class BoolLit
2567    is partial
2568    inherits AtomicLiteral
2569
2570    var _value as bool
2571
2572    cue init(token as IToken)
2573        require token.text in ['true', 'false']
2574        base.init(token)
2575        _value = token.text=='true'
2576
2577    cue init(token as IToken, value as bool)
2578        base.init(token)
2579        _value = value
2580
2581    def _bindImp is override
2582        base._bindImp
2583        _type = .compiler.boolType
2584
2585
2586class CharLit
2587    is partial
2588    inherits AtomicLiteral
2589
2590    var _value as String  # TODO: should probably be char
2591
2592    cue init(token as IToken)
2593        require
2594            token.which in ['CHAR_LIT_SINGLE', 'CHAR_LIT_DOUBLE']
2595            token.value inherits String
2596        body
2597            base.init(token)
2598            _value = token.value to String
2599
2600    def _bindImp is override
2601        base._bindImp
2602        _type = .compiler.charType
2603
2604
2605class DecimalLit
2606    is partial
2607    inherits AtomicLiteral
2608
2609    var _value as decimal
2610
2611    cue init(token as IToken)
2612        require token.value inherits decimal
2613        base.init(token)
2614        _value = token.value to decimal
2615
2616    get isNumeric as bool is override
2617        return true
2618
2619    def _bindImp is override
2620        base._bindImp
2621        _type = .compiler.decimalType
2622
2623
2624class FractionalLit
2625    is partial
2626    inherits AtomicLiteral
2627
2628    var _value as decimal
2629
2630    cue init(token as IToken)
2631        require token.value inherits decimal
2632        base.init(token)
2633        _value = token.value to decimal
2634
2635    get isNumeric as bool is override
2636        return true
2637
2638    get value from var
2639
2640    def _bindImp is override
2641        base._bindImp
2642        _type = .compiler.numberType
2643
2644
2645class FloatLit
2646    is partial
2647    inherits AtomicLiteral
2648
2649    var _value as float
2650
2651    cue init(token as IToken)
2652        require token.value inherits float
2653        base.init(token)
2654        _value = token.value to float
2655
2656    get isNumeric as bool is override
2657        return true
2658
2659    def _bindImp is override
2660        base._bindImp
2661        if _type is nil
2662            _type = if(.token.info inherits int, .compiler.floatType(.token.info to int), .compiler.floatType)
2663
2664
2665class IntegerLit
2666    is partial
2667    inherits AtomicLiteral
2668
2669    var _value as int
2670
2671    cue init(token as IToken)
2672        require token.value inherits int
2673        base.init(token)
2674        _value = token.value to int
2675
2676    cue init(token as IToken, value as int)
2677        base.init(token)
2678        _value = value
2679
2680    get isNumeric as bool is override
2681        return true
2682
2683    get value from var
2684
2685    def canBeAssignedTo(type as IType) as bool is override
2686        if type.nonNil.isDescendantOf(.compiler.anyIntType)
2687            # to-do: check that the literal fits in the range of the type
2688            return true
2689        else
2690            return base.canBeAssignedTo(type)
2691
2692    def _bindImp is override
2693        base._bindImp
2694        if _type is nil
2695            if .token.info inherits int
2696                size = .token.info to int
2697                signed = size < 0
2698                size = if(signed, -1, +1) * size
2699                _type = .compiler.intType(signed, size)
2700            else
2701                _type = .compiler.intType
2702
2703
2704class NilLiteral
2705    is partial
2706    inherits AtomicLiteral
2707
2708    cue init(token as IToken)
2709        base.init(token)
2710
2711    def _bindImp is override
2712        base._bindImp
2713        _type = .compiler.nilType
2714
2715
2716class StringLit inherits AtomicLiteral is partial
2717
2718    var _string as String  # String contents (with no surrounding quotes or escaping)
2719
2720    cue init(token as IToken)
2721        require token.which.startsWith('STRING')
2722        base.init(token as IToken)
2723        _string = token.value to String
2724
2725    get isObjectLiteral as bool is override
2726        return true
2727
2728    get string from var
2729
2730    def _bindInt is override
2731        if not _type
2732            _type = .compiler.stringType
2733        base._bindInt
2734
2735    def _bindImp is override
2736        if not _type
2737            _type = .compiler.stringType
2738        base._bindImp
2739
2740    def toCobraSource as String is override
2741        return .token.text
2742
2743
2744class StringSubstLit
2745    is partial
2746    inherits Literal
2747
2748    var _items as List<of Expr>
2749
2750    cue init(items as List<of Expr>)
2751        require items.count
2752        base.init(items[0].token)
2753        if true
2754            _items = items
2755        else
2756            # TODO: the efficient, but not debugged case
2757            # CC: potential
2758            # _items = for item in items if not item inherits StringLit or item.string get item
2759            _items = List<of Expr>()
2760            for item in _items
2761                if item inherits StringLit and not (item to StringLit).string.length
2762                    continue
2763                _items.add(item)
2764            assert _items.count
2765
2766    def addSubFields is override
2767        base.addSubFields
2768        .addField('items', _items)
2769
2770    get allExprs as Expr*
2771        for expr in base.allExprs, yield expr
2772        for item in .items, for expr in item.allExprs, yield expr
2773
2774    get items from var
2775        has Subnodes
2776
2777    def toCobraSource as String is override
2778        sb = StringBuilder()
2779        for item in _items
2780            if item inherits StringLit
2781                sb.append(item.token.text)
2782            else
2783                sb.append(item.toCobraSource)
2784        return sb.toString
2785
2786    def _bindImp is override
2787        base._bindImp
2788        for item in _items
2789            try
2790                item.bindImp
2791            catch ne as NodeException
2792                .compiler.recordError(ne)
2793        if not _type
2794            _type = .compiler.stringType
2795
2796
2797class FormattedExpr
2798    inherits Expr
2799    """
2800    This is used exclusively for string substitutions that have formatting:
2801        '[i:N]'
2802    """
2803
2804    var _expr as Expr
2805    var _format as String
2806
2807    cue init(expr as Expr, format as String)
2808        base.init(expr.token)
2809        _expr = expr
2810        _format = format
2811
2812    get allExprs as Expr*
2813        for expr in base.allExprs, yield expr
2814        for expr in .expr.allExprs, yield expr
2815
2816    get expr from var
2817
2818    get format from var
2819
2820    def toCobraSource as String is override
2821        return '[_expr.toCobraSource]:[_format]'
2822
2823    def _bindImp is override
2824        base._bindImp
2825        _expr.bindImp
2826        _type = .compiler.stringType
2827
2828
2829class ThisOrBaseLit inherits AtomicLiteral is abstract, partial
2830    """
2831    The "current box" used to be passed to .init rather than referenced in _bindImp, but that
2832    didn't work well with partial classes. Also, it's not necessary to creating a ThisOrBaseLit.
2833    """
2834
2835    cue init(token as IToken)
2836        base.init(token)
2837
2838    def _bindImp
2839        base._bindImp
2840        _type = .compiler.curBox
2841        if _type inherits Extension
2842            _type = _type.extendedBox
2843
2844
2845class BaseLit
2846    is partial
2847    inherits ThisOrBaseLit
2848
2849    cue init(token as IToken)
2850        base.init(token)
2851
2852    def checkType is override
2853        pass
2854
2855    def memberForName(name as String) as IMember? is override
2856        assert .didBindImp
2857        assert _type
2858        t = .receiverType
2859        if t.superType
2860            t = t.superType
2861        return t.memberForName(name)
2862
2863    def toCobraSource as String is override
2864        return 'base'
2865
2866
2867class ThisLit
2868    is partial
2869    inherits ThisOrBaseLit
2870
2871    # TODO: somewhere it has to be error checked that you're not assigning to
2872    # "this" (unless C# allows that which I doubt)
2873
2874    cue init(token as IToken)
2875        base.init(token)
2876
2877    get isExplicit as bool
2878        """
2879        Returns true if the `this` literal was explicitly present as in `this.foo` vs. implicitly present as in `.foo`.
2880        """
2881        return .token.which == 'THIS'
2882
2883    def toCobraSource as String is override
2884        return 'this'
2885
2886
2887class VarLit
2888    is partial
2889    inherits AtomicLiteral
2890
2891    var _propertyMember as ProperDexerXetter?
2892    var _name as String
2893    var _var as IVar?
2894
2895    cue init(token as IToken, propertyMember as ProperDexerXetter)
2896        base.init(token)
2897        _propertyMember = propertyMember
2898        _name = '_'+propertyMember.parent.name
2899
2900    def _bindImp is override
2901        possible = _propertyMember.parent.parentBox.symbolForName(_name, true)
2902        if possible is nil
2903            .throwError('Cannot find a class variable named "[_name]".')
2904        else if possible inherits IVar
2905            _var = possible
2906        else
2907            assert false, possible
2908        _type = _var.type
2909        _propertyMember = nil  # don't need this reference anymore
2910        base._bindImp
2911
2912
2913class CompositeLiteral inherits Literal
2914
2915    cue init(token as IToken)
2916        base.init(token)
2917
2918    get isObjectLiteral as bool is override
2919        return true
2920
2921
2922class SequenceLit
2923    is abstract, partial
2924    inherits CompositeLiteral
2925
2926    var _exprs as List<of Expr>
2927
2928    cue init(token as IToken, exprs as List<of Expr>)
2929        base.init(token)
2930        _exprs = exprs
2931
2932    def addSubFields is override
2933        base.addSubFields
2934        .addField('exprs', _exprs)
2935
2936    get allExprs as Expr*
2937        for expr in base.allExprs, yield expr
2938        for expr in .exprs, for expr2 in expr.allExprs, yield expr2
2939
2940    get exprs from var
2941        has Subnodes
2942
2943    get willChangeVar as bool is override
2944        for expr in _exprs, if expr.willChangeVar, return true
2945        return false
2946
2947    def _bindImp is override
2948        base._bindImp
2949        exceptions = List<of NodeException>()
2950        for expr in _exprs
2951            try
2952                expr.bindImp
2953            catch ne as NodeException
2954                exceptions.add(ne)
2955        if exceptions.count
2956            throw NodeMultiException(exceptions)
2957        if _type is nil
2958            exprs = _exprs
2959            if exprs.count == 0
2960                type = .compiler.defaultType
2961            else
2962                type = exprs[0].type to !
2963                for i in 1 : exprs.count
2964                    exprs[i].bindImp
2965                    type = exprs[i].type.greatestCommonDenominatorWith(type)
2966                if type.isSystemObjectClass  # make dynamic if heterogenous and all non Object
2967                    allObj = all for expr in exprs get expr.type.isSystemObjectClass
2968                    if not allObj, type = .compiler.dynamicType
2969            if type inherits NilType, type = .compiler.defaultType
2970            _type = _makeTypeWith(type)
2971
2972    def _makeTypeWith(type as IType) as IType is abstract
2973
2974    get brackets as List<of String> is abstract
2975
2976    def toCobraSource as String is override
2977        brackets = .brackets
2978        sb = StringBuilder(brackets[0])
2979        sep = ''
2980        for expr in _exprs
2981            sb.append(sep)
2982            sb.append(expr.toCobraSource)
2983            sep = ', '
2984        sb.append(brackets[1])
2985        return sb.toString
2986
2987
2988class ListLit
2989    is partial
2990    inherits SequenceLit
2991
2992    # CC: should just inherit this because no initializers are defined
2993    cue init(token as IToken, exprs as List<of Expr>)
2994        base.init(token, exprs)
2995
2996    def _makeTypeWith(type as IType) as IType is override
2997        return .compiler.listOfType.constructedTypeFor([type])
2998
2999    get brackets as List<of String> is override
3000        return [r'[', ']']
3001
3002
3003class ArrayLit inherits SequenceLit is partial
3004
3005    # CC: should just inherit this because no initializers are defined
3006    cue init(token as IToken, exprs as List<of Expr>)
3007        base.init(token, exprs)
3008
3009    def _makeTypeWith(type as IType) as IType is override
3010        return .typeProvider.arrayType(type)
3011
3012    get brackets as List<of String> is override
3013        return [r'@[', ']']
3014
3015
3016class SetLit inherits SequenceLit is partial
3017    """
3018    Sets aren't exactly sequences although they appear that way in source code which itself is a sequence of characters.
3019    Like lists, sets have one generic type param.
3020
3021    Hence it's convenient to inherit SequenceLit, the base class for ListLit and ArrayLit as well.
3022
3023    Examples:
3024        {1, 2, 3}
3025        {'Cobra', 'Python'}
3026        {1, Object(), 'Car'}
3027        {,}
3028    """
3029
3030    cue init(token as IToken, exprs as List<of Expr>)
3031        base.init(token, exprs)
3032
3033    def _makeTypeWith(type as IType) as IType is override
3034        return .compiler.setOfType.constructedTypeFor([type])
3035
3036    get brackets as List<of String> is override
3037        return [r'{', '}']
3038
3039    def _bindImp
3040        base._bindImp
3041        if not .hasError
3042            exprs = .exprs
3043            c = exprs.count
3044            done = false
3045            for i in c-1
3046                for j in i+1 : c
3047                    if exprs[i].isEquivalentTo(exprs[j])
3048                        .compiler.warning(exprs[i], 'Duplicate member in set.')
3049                        done = true
3050                        break
3051                if done, break
3052
3053
3054class DictLit inherits CompositeLiteral is partial
3055
3056    var _entries as List<of List<of Expr>>
3057
3058    cue init(token as IToken, entries as List<of List<of Expr>>)
3059        base.init(token)
3060        if CobraCore.willCheckAssert
3061            for entry in entries
3062                assert entry.count == 2
3063        _entries = entries
3064
3065    def addSubFields is override
3066        base.addSubFields
3067        .addField('entries', _entries)
3068
3069    get allExprs as Expr*
3070        for expr in base.allExprs, yield expr
3071        for entry in .entries, for element in entry, for expr in element.allExprs, yield expr
3072
3073    get willChangeVar as bool is override
3074        for entry in _entries
3075            for expr in entry
3076                if expr.willChangeVar
3077                    return true
3078        return false
3079
3080    get entries from var
3081
3082    def replaceChild(find as INode, replace as INode) as bool
3083        didReplace = base.replaceChild(find, replace)
3084        if replace inherits Expr
3085            for entry in _entries
3086                if entry[0] is find
3087                    entry[0] = replace
3088                    didReplace = true
3089                if entry[1] is find
3090                    entry[1] = replace
3091                    didReplace = true
3092        return didReplace
3093
3094    def _bindImp is override
3095        base._bindImp
3096        hadError = false
3097        for entry in _entries
3098            try
3099                entry[0].bindImp
3100            catch ne as NodeException
3101                .compiler.recordError(ne)
3102                hadError = true
3103            try
3104                entry[1].bindImp
3105            catch ne as NodeException
3106                .compiler.recordError(ne)
3107                hadError = true
3108        if hadError
3109            return
3110        if .type is nil
3111            entries = _entries
3112            if _entries.count == 0
3113                keyType = valueType = .compiler.defaultType
3114            else
3115                keyType   = entries[0][0].type to !
3116                valueType = entries[0][1].type to !
3117                i = 1
3118                keysAllSysObject = valuesAllSysObject = true
3119                while i < entries.count
3120                    keyType   = entries[i][0].type.greatestCommonDenominatorWith(keyType)
3121                    if not entries[i][0].type.isSystemObjectClass, keysAllSysObject = false
3122                    valueType = entries[i][1].type.greatestCommonDenominatorWith(valueType)
3123                    if not entries[i][1].type.isSystemObjectClass, valuesAllSysObject = false 
3124                    i += 1
3125                # make types dynamic if heterogenous and all non Object
3126                if keyType.isSystemObjectClass and not keysAllSysObject
3127                    keyType = .compiler.dynamicType
3128                if valueType.isSystemObjectClass and not valuesAllSysObject
3129                    valueType = .compiler.dynamicType
3130            if keyType inherits NilType, keyType = .compiler.defaultType
3131            if valueType inherits NilType, valueType = .compiler.defaultType
3132            _type = .compiler.dictionaryOfType.constructedTypeFor([keyType, valueType])
3133
3134
3135class ToNilableOrNotExpr
3136    is abstract, partial
3137    inherits Expr
3138    """
3139    The abstract base class for ToNilableExpr and ToNonNilableExpr which have much in common.
3140    """
3141   
3142    var _rightTok as IToken
3143    var _expr as Expr
3144
3145    cue init(opToken as IToken, rightTok as IToken, expr as Expr)
3146        base.init(opToken)
3147        _rightTok = rightTok
3148        _expr = expr
3149
3150    get allExprs as Expr*
3151        for expr in base.allExprs, yield expr
3152        for expr in .expr.allExprs, yield expr
3153
3154    get rightTok from var
3155   
3156    get expr from var
3157
3158    def _bindImp
3159        base._bindImp
3160        _expr.bindImp
3161
3162    get willChangeVar as bool is override
3163        if _expr.willChangeVar, return true
3164        return false
3165
3166    def toCobraSource as String is override
3167        sb = StringBuilder()
3168        sb.append(_expr.toCobraSource)
3169        sb.append(' to ')
3170        sb.append(_rightTok.text)
3171        return sb.toString
3172
3173
3174class ToNilableExpr
3175    is partial
3176    inherits ToNilableOrNotExpr
3177    """
3178    Casts an expression to the nilable version of its type.
3179   
3180    When coding in Cobra:
3181
3182    You would do this primarily when the expression is on the right hand side of an assignment and
3183    you want the left hand var to by implicitly typed as nilable so you can assign nil to it later.
3184   
3185    ex: name = obj.name to ?
3186    ex: age = customer.age to ?
3187    """
3188   
3189    cue init(opToken as IToken, rightTok as IToken, expr as Expr)
3190        base.init(opToken, rightTok, expr)
3191
3192    def _bindImp
3193        base._bindImp
3194        if not _expr.hasError
3195            assert _expr.type
3196            if _expr.type inherits NilableType
3197                _type = _expr.type
3198                .compiler.warning(this, 'The given expression is already nilable so "to ?" is redundant. You can remove it.')  # TODO: needs test case
3199            else
3200                _type = .typeProvider.nilableType(_expr.type to !)
3201
3202
3203class ToNonNilableExpr
3204    is partial
3205    inherits ToNilableOrNotExpr
3206    """
3207    Casts an expression to the non-nilable version of its type.
3208   
3209    When coding in Cobra:
3210
3211    You would do this when trying to use a nilable expression where a non-nil type is expected and
3212    you know that at run-time the expression won't actually by nil (presumably due to the logic of
3213    your code).
3214   
3215    Or you might do this to affect type inference.
3216   
3217    ex: name = obj.name to !
3218    ex: obj.foo(age to !)
3219    """
3220   
3221    cue init(opToken as IToken, rightTok as IToken, expr as Expr)
3222        base.init(opToken, rightTok, expr)
3223
3224    def _bindImp
3225        base._bindImp
3226        if (et = _expr.type) inherits NilableType
3227            _type = et.nonNil
3228        else
3229            .compiler.warning(this, 'The given expression is already non-nilable so "to !" is redundant. You can remove it.') # TODO: needs test case
3230            _type = _expr.type
3231
3232
3233class ChainedCompareExpr inherits Expr is partial
3234       
3235    var _items as List<of Expr>
3236    var _operations as List<of String>
3237   
3238    cue init(opToken as IToken, items as List<of Expr>, operations as List<of String>)
3239        require
3240            # should not happen, there is an error in the compiler if this occurs
3241            items.count == operations.count + 1
3242            items.count > 2
3243        body
3244            base.init(opToken)
3245            _items, _operations = items, operations
3246
3247    get allExprs as Expr*
3248        for expr in base.allExprs, yield expr
3249        for item in .items, for expr in item.allExprs, yield expr
3250
3251    get items from var
3252
3253    def _bindImp is override
3254        base._bindImp
3255        itemIndex = 1
3256        for operation in _operations
3257            _items[itemIndex - 1].bindImp
3258            _items[itemIndex].bindImp
3259            _willCompare(_items[itemIndex - 1], operation, _items[itemIndex])
3260            itemIndex += 1
3261        _type = .compiler.boolType
3262       
3263    def _willCompare(left as Expr, op as String, right as Expr) as bool
3264        if left.type is nil or right.type is nil
3265            assert .hasError
3266            return false
3267        leftType = left.type to !
3268        rightType = right.type to !
3269        innerMsg as String?
3270        # cannot invoke .isAssignableTo directly on the types because if one is nilable and the other is not, you can get false, even though that doesn't apply for "is". So use isEquatableTo and isComparableTo
3271        if op in ['EQ', 'NE', 'IS', 'ISNOT']
3272            if not leftType.isEquatableTo(rightType)
3273                innerMsg = if(op.startsWith('IS'), 'can never be identical', 'cannot be equated')
3274            else if op in ['IS', 'ISNOT']
3275                # Essentially "is" and "is not" are for reference types. Give a warning when used for value types.
3276                if not leftType.isDynamicOrPassThrough and not rightType.isDynamicOrPassThrough
3277                    opName = if(op=='IS', 'is', 'is not')
3278                    altName = if(op=='IS', '==', '<>')
3279                    if leftType inherits NilableType and rightType inherits NilType
3280                        pass
3281                    else if not leftType.isReference and not rightType.isReference
3282                        .compiler.warning(this, 'Both the left and right sides of "[opName]" are value types ("[leftType.name]" and "[rightType.name]"), but "[opName]" applies to reference types. Use "[altName]" instead.')
3283                    # interestingly, I don't think the following ever happen in practice...
3284                    else if not leftType.isReference and not rightType.isNilableAndDescendantOf(leftType)
3285                        .compiler.warning(this, 'The left side of "[opName]" is a value type ("[leftType.name]") while the right side is an incompatible reference type ("[rightType.name]").')
3286                    else if not rightType.isReference and not leftType.isNilableAndDescendantOf(rightType)
3287                        .compiler.warning(this, 'The right side of "[opName]" is a value type ("[rightType.name]") while the left side is an incompatible reference type ("[leftType.name]").')                     
3288                    else if left.isObjectLiteral or right.isObjectLiteral
3289                        .compiler.warning(this, 'Do not use the identity operator "[opName]" with an object literal. Use an equality operator such as "==" or "<>".')
3290        else
3291            if not leftType.isComparableTo(rightType)
3292                innerMsg = 'cannot be compared'
3293        if innerMsg
3294            typeNames = if(left.type.name==right.type.name, 'type ("[left.type.name]")', 'types ("[left.type.name]" and "[right.type.name]")')
3295            msg = 'The left and right sides of the "[.token.text]" expression [innerMsg] because of their [typeNames].'
3296            if op == 'IS' and right.type.isDescendantOf(.compiler.typeType) # C#
3297                msg += ' Maybe you should try "inherits" instead of "is".'
3298            .throwError(msg)
3299            return false
3300        return true
3301   
3302    def toCobraSource as String is override
3303        sb = StringBuilder(_items[0].toCobraSource)
3304        sep = ' '       
3305        for index in _operations.count
3306            sb.append(sep)
3307            branch _operations[index]
3308                on 'EQ', sb.append('==')
3309                on 'NE', sb.append('<>')
3310                on 'GT', sb.append('>')
3311                on 'LT', sb.append('<')
3312                on 'GE', sb.append('>=')
3313                on 'LE', sb.append('<=')
3314                on 'IS', sb.append('is')
3315                on 'ISNOT', sb.append('is not')
3316            sb.append(sep)
3317            sb.append(_items[index + 1].toCobraSource)
3318        return sb.toString
Note: See TracBrowser for help on using the browser.