Wiki

root/cobra/trunk/Source/Statements.cobra

Revision 2657, 51.7 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 
1class Stmt
2    is abstract, partial
3    inherits SyntaxNode
4
5    var _parent as INode?
6   
7    cue init(token as IToken)
8        base.init(token)
9
10    pro parent from var
11        """
12        The node that the statement belongs to. This is being set on an as-needed basis.
13        Typical needs are for error checking, but could also include other purposes such as code generation.
14        The common place to set a node's `parent` is in the `_bindImp` of the parent
15        """
16       
17    get lastToken as IToken
18        return .token
19
20    def afterParserRecognizesStatement
21        pass
22
23    def afterStatementBindImp
24        """
25        Invoked to let expressions know when they are used as statements. This default
26        implementation does nothing. Invoked by OneBlockCodeMember and BlockStmt.
27        """
28        pass
29
30    def noAssignmentAllowed(expr as Expr)
31        """
32        Utility method for IfStmt and WhileStmt which do not allow assignment in
33        their expressions.
34        """
35        if expr inherits BinaryOpExpr
36            if expr.op=='ASSIGN'
37                .throwError('Cannot make an assignment in a flow control expression. Change to == or make the assignment just above.')
38            else if expr.op=='BANG_EQUALS'
39                .throwError('Cannot use an augmented assignment in a flow control expression. If you meant "does not equal" then use "<>". "a != b" means "a = a ! b" where "!" is an operator concerning nil.')
40            else if expr.op.endsWith('_EQUALS')
41                .throwError('Cannot use an augmented assignment in a flow control expression. Change to == or make the assignment just above.')
42
43    def bindVar(ve as NameExpr) as IVar
44        """
45        Computes the _var from the _varExpr which is either IdentifierExpr or AsExpr.
46        May call `_error` and `inferredType` as needed.
47        Typical use: _var = .bindVar(_varExpr)
48        """
49        # TODO: would prefer to do this as a mixin with two class vars:
50        # var _varExpr as NameExpr
51        # var _var as IVar?
52        varr as IVar?
53        if ve inherits IdentifierExpr
54            # find or infer
55            definition as INamedNode?
56            canBeUndottedMember = ve.name.canBeUndottedMemberName
57            usingExistingLocal = false
58            if canBeUndottedMember
59                assert .compiler.boxStack.count
60                definition = .compiler.symbolForName(ve.name, false) to passthrough
61                if definition is nil
62                    ve.throwUnknownIdError
63            else
64                definition = .compiler.findLocal(ve.name)
65                # infer a local var
66                it = .inferredType
67                if it is nil
68                    .throwError('Cannot infer the type for "[ve.name]" from the "for" loop.')               
69                if definition is nil
70                    definition = LocalVar(ve.token, it).bindAll to INamedNode  # CC: axe cast after "as this"
71                    .compiler.curCodeMember.addLocal(definition to LocalVar)
72                else
73                    # using an existing local. that's fine as long as the types are the name
74                    usingExistingLocal = true
75            if definition inherits IVar
76                varr = definition
77                if usingExistingLocal
78                    ve.bindImp  # can't do this at the beginning of this method as it interferes with type inference. but here, it's an existing var so no "Unknown identifier" error will occur
79                    assert definition.type
80                    if definition.type is .compiler.passThroughType
81                        varr.type = definition.type
82                    else if not it.isAssignableTo(definition.type to !)
83                        .throwError('Cannot redeclare "[ve.name]" as "[it.name]" because it was declared as "[definition.type.name]" earlier.') # TODO: would be nice to give the location of the other declaration             
84            else
85                .throwError('The definition of "[ve.name]" is not a variable which is what the "for" loop requires.')
86            ve.bindImp
87        else if ve inherits AsExpr
88            ve.bindImp
89            assert ve.definition
90            varr = ve.definition
91        else
92            throw FallThroughException(ve)
93        return varr to !
94
95    def inferredType as IType?
96        """
97        Used by .bindVar so that the subclasses can implement their inferred type logic.
98        """
99        return nil
100
101    var _canSetLine as bool
102
103    def bindImp as dynamic is override
104        # CC: and ensure result inherits Stmt
105        base.bindImp
106        assert .didBindImp, this
107        .postBindImp
108        return .bindImpResult
109
110    def postBindImp
111        .help
112       
113    def help
114        if not .hasError and .isHelpRequested
115            topic = .token.text + ' statement'
116            c = .compiler
117            hg = HelpGenerator(topic, this, c.curBoxMember, c.curBox)
118            hg.searchTerms.add(topic)
119            hg.generate
120            .compiler.warning(this, '@help at "[hg.path]".')
121
122    def _bindImp
123        base._bindImp
124        _canSetLine = .compiler.willTrackLocals
125        # note that the code member stack can be empty because of class vars (ex: var _foo = true)
126        # even the box stack can be empty because of assembly; has ...
127
128
129class AssertStmt
130    is partial
131    inherits Stmt
132
133    var _expr as Expr
134    var _info as Expr?
135
136    cue init(token as IToken, expr as Expr, info as Expr?)
137        base.init(token)
138        _expr = TruthExpr(expr)
139        _info = info
140
141    def addSubFields
142        base.addSubFields
143        .addField('expr', _expr)
144        .addField('info', _info)
145
146    get expr from var
147
148    get info from var
149
150    def _innerClone
151        base._innerClone
152        _expr = _expr.clone
153        if _info, _info = _info.clone
154
155    def _bindImp
156        base._bindImp
157        _expr.bindImp
158        e = _expr
159        if e.willChangeVar
160            if e inherits TruthExpr
161                e = e.expr
162            if e inherits AbstractAssignExpr
163                # TODO: should also cover any assignment expressions anywhere inside the expression
164                .throwError('Assert condition is an assignment. Since asserts may be suppressed these conditions should not have side effects. Perhaps you meant to do a comparison "==" instead of assignment "=".')
165            else
166                .throwError('Condition has a side effect. Since asserts may be suppressed at compile-time or run-time, their conditions cannot have side effects.')
167        # Augment any warning about non nilable expr evaluate to true
168        .compiler.augmentWarning(this, 'always evaluate to true because it is not nilable', _
169            'You can remove the expression', _
170            'This assertion is always true. You can remove the assertion or correct the condition')
171        if _info, _info.bindImp
172
173
174class CompileTimeTraceStmt inherits Stmt
175
176    # to-do: retire after @help is more mature
177
178    var _expr as Expr
179   
180    cue init(token as IToken, expr as Expr)
181        base.init(token)
182        _expr = expr
183
184    def addSubFields
185        base.addSubFields
186        .addField('expr', _expr)
187
188    get expr from var
189
190    def _innerClone
191        base._innerClone
192        _expr = _expr.clone
193
194    def _bindImp
195        base._bindImp
196        expr = _expr
197        # this output will likely need to be done as warning output so it works with IDEs
198        prefix = '    *'
199        print
200        print '* Compile-time trace at [.token.toTechString]'
201        print '[prefix] before bind imp on expression: [expr.toCobraSource] = [expr]'
202        expr.bindImp
203        print '[prefix] after bind imp on expression:  [expr.toCobraSource] = [expr]'
204        dexpr = expr to dynamic
205        try
206            defi = dexpr.definition
207            if defi, print '[prefix] definition = [defi]'
208        catch DynamicOperationException
209            print '[prefix] no definition'
210        try
211            type = dexpr.type
212            if type, print '[prefix] type = [type]'
213        catch DynamicOperationException
214            print '[prefix] no type'
215
216
217class BranchStmt inherits Stmt is partial
218
219    cue init(token as IToken, expr as Expr, onParts as List<of BranchOnPart>, elsePart as BlockStmt?)
220        base.init(token)
221        _expr = expr
222        _onParts = onParts
223        _elsePart = elsePart
224
225    def addSubFields
226        base.addSubFields
227        .addField('expr', .expr)
228        .addField('onParts', .onParts)
229        .addField('elsePart', .elsePart)
230
231    get expr from var as Expr
232    get onParts from var as List<of BranchOnPart>
233    get elsePart from var as BlockStmt?
234   
235    def _innerClone
236        base._innerClone
237        _expr = _expr.clone
238        _onParts = _onParts.toList
239        for i in .onParts.count, .onParts[i] = .onParts[i].clone
240        _elsePart = _elsePart.clone
241
242    def _bindImp
243        base._bindImp
244        _expr.bindImp
245        uniqueOnParts = Set<of String>()
246        for onPart in _onParts
247            onPart.bindImp
248            for expr in onPart.exprs
249                source = expr.toCobraSource
250                if source in uniqueOnParts, expr.recordError('Branch case "[source]" was already used.')
251                uniqueOnParts.add(source)
252        if _elsePart, _elsePart.bindImp
253
254
255class BranchOnPart inherits Node
256
257    var _exprs as List<of Expr>
258    var _block as BlockStmt
259
260    cue init(exprs as List<of Expr>, block as BlockStmt)
261        base.init
262        _exprs = exprs
263        _block = block
264
265    def addSubFields
266        base.addSubFields
267        .addField('exprs', _exprs)
268        .addField('block', _block)
269
270    get block from var
271
272    get exprs from var
273        has Subnodes
274
275    def _innerClone
276        base._innerClone
277        _exprs = _exprs.toList2
278        for i in _exprs.count, _exprs[i] = _exprs[i].clone
279        _block = _block.clone
280
281    def _bindImp
282        base._bindImp
283        for expr in _exprs, expr.bindImp
284        _block.bindImp
285
286
287class BlockStmt
288    is partial
289    inherits Stmt
290    """
291    A BlockStmt holds a series of statements which are the target of complex statement such
292    if, while, etc. BlockStmts are *not* the target of a method or property.
293    """
294
295    var _stmts as List<of Stmt>
296    var _ifInheritsVar as IVar?
297    var _ifInheritsType as IType?
298    var _curStmtIndex as int  # to implement .replaceChild
299
300    cue init(token as IToken, stmts as List<of Stmt>)
301        base.init(token)
302        _stmts = stmts
303
304    get lastToken as IToken
305        return if(_stmts.count, _stmts[_stmts.count-1].lastToken, base.lastToken)
306
307    def addSubFields
308        base.addSubFields
309        .addField('stmts', _stmts)
310
311    def setIfInherits(varr as IVar, type as IType)
312        _ifInheritsVar = varr
313        _ifInheritsType = type
314
315    get stmts from var
316        has Subnodes
317
318    def replaceChild(find as INode, replace as INode) as bool
319        # using _curStmtIndex is faster and blocks can get potentially large
320        if _curStmtIndex < _stmts.count and _stmts[_curStmtIndex] is find
321            _stmts[_curStmtIndex] = replace to Stmt
322            return true
323        else
324            return base.replaceChild(find, replace)
325
326    def _innerClone
327        base._innerClone
328        _stmts = _stmts.toList2
329        for i in _stmts.count, _stmts[i] = _stmts[i].clone
330        # not expecting a clone after binding imp:
331        assert _ifInheritsVar is nil
332        assert _ifInheritsType is nil
333
334    def _bindImp
335        base._bindImp
336        _curStmtIndex = 0
337        for stmt in _stmts.toArray
338            stmt.parent = this
339            try
340                stmt.bindImp
341                stmt.afterStatementBindImp  # to let expressions know when they are used as statements
342            catch ne as NodeException
343                .compiler.recordError(ne)
344            _curStmtIndex += 1
345
346
347class BreakStmt
348    is partial
349    inherits Stmt
350
351    cue init(tok as IToken)
352        base.init(tok)
353
354
355class ContinueStmt
356    is partial
357    inherits Stmt
358
359    cue init(tok as IToken)
360        base.init(tok)
361
362
363class ExpectStmt inherits Stmt is partial
364
365    var _exceptionTypeNode as ITypeProxy
366    var _exceptionType as IType?
367    var _block as BlockStmt
368    var _varNumber as int
369   
370    get block from var
371
372    cue init(token as IToken, exceptionTypeNode as ITypeProxy, block as BlockStmt)
373        base.init(token)
374        _exceptionTypeNode = exceptionTypeNode
375        _block = block
376
377    get lastToken as IToken is override
378        return _block.lastToken
379
380    def addSubFields
381        base.addSubFields
382        .addField('exceptionTypeNode', _exceptionTypeNode)
383        .addField('exceptionType', _exceptionType)
384        .addField('block', _block)
385
386    def _innerClone
387        base._innerClone
388        _block = _block.clone
389
390    def _bindImp
391        base._bindImp
392        if _exceptionType is nil
393            _exceptionType = (_exceptionTypeNode.bindAll to ITypeProxy).realType  # CC: axe cast
394        assert _exceptionType, this
395        excClass = .compiler.exceptionType
396        if not _exceptionType.isDescendantOf(excClass)
397            .throwError('Can only use Exception and its descendants for "expect". "[_exceptionType.name]" does not inherit the Exception class.')
398        .compiler.curBox.makeNextPrivateSerialNumber  # the code gen typically uses two serial nums
399        _varNumber = .compiler.curBox.makeNextPrivateSerialNumber
400        _block.bindImp
401
402
403class ForStmt inherits Stmt is partial
404    """
405    Abstract base class for ForNumericStmt and ForEnumerablebase.
406    """
407
408    cue init(token as IToken, varr as NameExpr, block as BlockStmt)
409        base.init(token)
410        _varExpr = varr
411        _block = block
412
413    get varExpr from var as NameExpr
414   
415    get var from var as IVar?
416   
417    get block from var as BlockStmt
418   
419    get lastToken as IToken is override
420        return _block.lastToken
421
422    def addSubFields
423        base.addSubFields
424        .addField('varExpr', .varExpr)
425        .addField('var', .var)
426        .addField('block', .block)
427
428    def _innerClone
429        base._innerClone
430        _varExpr = _varExpr.clone
431        _block = _block.clone
432
433    def _bindImp
434        base._bindImp
435        _varExpr.bindImp
436        if _varExpr.definition
437            if _varExpr.definition inherits IVar
438                _var = _varExpr.definition
439            else
440                .throwError('Expecting a variable not a [_varExpr.definition.getType.name].')  # TODO: what's the best way to report what was found?
441        else
442            assert _varExpr.hasError
443        #if not var.isTracked  TODO disabled this to get tests\200-classes\804-test.cobra working.
444        if false
445            varr = .var
446            existingVar = .compiler.curCodeMember.findLocal(varr.name)
447            if existingVar
448                # that's fine as long as the types are the name
449                if varr.type is .compiler.passThroughType
450                    varr.type = existingVar.type
451                else if existingVar.type is not varr.type
452                    .throwError('Cannot redeclare "[varr.name]" as "[varr.type]" because it was declared as "[existingVar.type]" earlier.') # TODO: would be nice to give the location of the other declaration
453           
454# TODO: check for existing  variables
455#       if 1
456#           ns = .compiler.nameSpaceStack.peek
457#           existingVar = ns.findLocal(varr.name)
458#           if existingVar
459#               # that's fine as long as the types are the name
460#               if varr.type is .compiler.passThroughType
461#                   varr.type = existingVar.type
462#               else if existingVar.type is not varr.type
463#                   .error('Cannot redeclare "[varr.name]" as "[varr.type]" because it was declared as "[existingVar.type]" earlier.')
464#           else
465#               .compiler.nameStack.peek.pushName(varr)   # TODO should be pushSymbol() or pushVar()
466#       varr.bindAll  # CallExpr() wants its definition to have bound int, so here we call bindAll instead of bindImp
467        _block.bindImp
468        # TODO axe this
469        #if not varr.isTracked
470        #   .compiler.nameStack.peek.pop   # TODO should be popSymbol() or popVar()
471
472
473class OldForNumericStmt
474    is partial
475    inherits ForStmt
476
477    var _start as Expr
478    var _stop as Expr
479    var _dir as int
480    var _step as Expr?
481
482    cue init(token as IToken, varr as NameExpr, start as Expr, stopp as Expr, dir as int, stepp as Expr?, block as BlockStmt)
483        require
484            dir==-1 or dir==+1
485        body
486            base.init(token, varr, block)
487            _start = start
488            _stop = stopp
489            _dir = dir
490            _step = stepp
491
492    def addSubFields
493        base.addSubFields
494        .addField('start', _start)
495        .addField('stop', _stop)
496        .addField('dir', _dir)
497        .addField('step', _step)
498        .addField('block', _block)
499
500    def _innerClone
501        base._innerClone
502        _start = _start.clone
503        _stop = _stop.clone
504        if _step, _step = _step.clone
505
506    def _bindImp
507        _start.bindImp
508        _stop.bindImp
509        if _step
510            _step.bindImp
511        _var = .bindVar(_varExpr)
512        base._bindImp
513        _block.bindImp
514
515    def inferredType as IType? is override
516        return _start.type.greatestCommonDenominatorWith(_stop.type to !)
517
518
519class ForNumericStmt inherits ForStmt is partial
520
521    cue init(token as IToken, varr as NameExpr, start as Expr, stopp as Expr, dir as int, stepp as Expr?, block as BlockStmt)
522        require
523            dir in [-1, 0, +1]  # 0 is for current syntax. -1 and +1 are for old "for x = 0 .. n ++ 2" syntax
524        body
525            base.init(token, varr, block)
526            _start = start
527            _stop = stopp
528            _dir = dir
529            _step = stepp
530
531    def addSubFields
532        base.addSubFields
533        .addField('start', _start)
534        .addField('stop', _stop)
535        .addField('dir', _dir)
536        .addField('step', _step)
537        .addField('block', _block)
538
539    get start from var as Expr
540   
541    get stop from var as Expr
542   
543    get step from var as Expr?
544   
545    get dir from var as int
546   
547    def _innerClone
548        base._innerClone
549        _start = _start.clone
550        _stop = _stop.clone
551        if _step, _step = _step.clone
552
553    def _bindImp
554        _start.bindImp
555        _stop.bindImp
556        if _step, _step.bindImp
557        _var = .bindVar(_varExpr)
558        base._bindImp
559        _block.bindImp
560        if not _start.hasError and not _stop.hasError
561            startType, stopType = _start.type to !, _stop.type to !
562            if _checkCompatibleTypes(startType, stopType)
563                if _step and not _step.hasError
564                    stepType = _step.type to !
565                    if not _checkCompatibleTypes(startType, stepType)
566                        .throwError('For loop start condition type "[startType.name]" is not compatible with step condition type "[stepType.name]".[_divHelper(_start)][_divHelper(_step)]')
567                    else if not _checkCompatibleTypes(stopType, stepType)
568                        .throwError('For loop stop condition type "[stopType.name]" is not compatible with step condition type "[stepType.name]".[_divHelper(_stop)][_divHelper(_step)]')
569            else
570                .throwError('For loop start condition type "[startType.name]" is not compatible with stop condition type "[stopType.name]".[_divHelper(_start)][_divHelper(_stop)]')
571        # optimization:
572        if _dir == 0  # the value for: for x in start : stop : step
573            if _step is nil
574                _dir = +1
575                _step = IntegerLit(.token.copy('INTEGER_LIT', '+1'), +1)
576                _step.bindImp
577            else if _step inherits IntegerLit
578                _dir = if(_step.value < 0, -1, +1)
579
580    def inferredType as IType? is override
581        return _start.type.greatestCommonDenominatorWith(_stop.type to !)
582   
583    def _checkCompatibleTypes(type1 as IType, type2 as IType) as bool
584        if type1 == type2
585            return true
586        if type1.isDynamic or type2.isDynamic
587            return true
588        if type1.isDescendantOf(.compiler.anyIntType) and type2.isDescendantOf(.compiler.anyIntType)
589            return true
590        return false
591
592    def _divHelper(expr as Expr?) as String
593        if expr inherits BinaryMathExpr and expr.token.which == 'SLASH'
594            return ' Note that the "/" operator produces a decimal or floating point number. If you want integer division, use the "//" operator.'
595        return ''
596
597
598class ForEnumerableStmt inherits ForStmt is partial
599
600    cue init(token as IToken, varr as NameExpr, what as Expr, block as BlockStmt)
601        base.init(token, varr, block)
602        _what = what
603
604    cue init(token as IToken, varr as NameExpr, args as List<of NameExpr>, what as Expr, block as BlockStmt)
605        # multiArg assignment in for stmt
606        .init(token, varr, what, block)
607        _multiArgs = args
608       
609    def addSubFields
610        base.addSubFields
611        .addField('what', _what)
612        .addField('varNumber', _varNumber)
613        .addField('multiArgs', _multiArgs)
614
615    get what from var as Expr
616   
617    get varNumber from var as int
618   
619    get multiArgs from var as List<of NameExpr>?
620   
621    def _innerClone
622        base._innerClone
623        _what = _what.clone
624        if .multiArgs
625            _multiArgs = _multiArgs.toList
626            for i in .multiArgs.count, .multiArgs[i] = .multiArgs[i].clone
627
628    def _bindImp
629        _what.bindImp
630        if _what.type.isDynamic
631            _what.contextType = .compiler.enumerableType
632        if _what.type inherits AnyIntType
633            # 'for x in 10' is a numeric for loop
634            _transformTo(ForNumericStmt(.token, _varExpr, IntegerLit(.token.copy('INTEGER_LIT', '0'), 0), _what, 1, nil, _block).bindAll)
635            base._bindImp  # just to pass the assertion that base._bindImp was invoked
636        else
637            _var = .bindVar(_varExpr)
638            if _multiArgs, _unpackMultiArgStmts
639            base._bindImp
640            _varNumber = .compiler.curBox.makeNextPrivateSerialNumber
641            _block.bindImp
642   
643    def _unpackMultiArgStmts
644        """
645        Handle expansion of statements for multiArg variables in for statements:
646        for k, v in <dict>  # becomes
647            for varExpr_uniq in <dict>
648                k = varExpr_uniq.key
649                v = varExpr_uniq.value
650        and
651        for a, b, c in <IEnumerable (of triplets in this case)> becomes
652            for varExpr_uniq in <IEnumerable>
653                a = varExpr_uniq[0]
654                b = varExpr_uniq[1]
655                c = varExpr_uniq[2]
656        with rest of block following               
657        """
658        # TODO: make sure varName starts with _lh_
659        stmts = List<of Stmt>()
660        varName = _varExpr.name  # "[_varExpr.name]_[.serialNum]"
661        token = _token.copy
662        count = 0
663        isStreamOfKeyValue = .what.type.isDictionaryLike
664        if not isStreamOfKeyValue and _var.type inherits Box
665            isStreamOfKeyValue = (_var.type to Box).isIndirectConstructionOf(.compiler.libraryType('System.Collections.Generic.KeyValuePair<of,>') to Box)
666        if isStreamOfKeyValue
667            # This covers:
668            #    * dictionaries such as: [I]Dictionary<of TKey, TValue>
669            #    * streams such as: KeyValuePair<of TKey, TValue>*
670            #    * any user type that implements IEnumerable<of KeyValuePair<of TKey, TValue>>
671           
672            # assume enumerator will return KeyValue so can't have other than 2 multiargs
673            if _multiArgs.count <> 2
674                .throwError('Cannot have other than two multiarg variables in a for statement as assignment targets from a dictionary or key-value stream.')
675            kvRHS = ['key', 'value']
676            for id in _multiArgs
677                forVar    = IdentifierExpr(token.copy('ID', varName), varName)
678                rhsToken  = token.copy('ID', kvRHS[count])
679                keyOrVal  = MemberExpr(rhsToken)
680                kvExpr    = BinaryOpExpr.make(token.copy('DOT', '.'), 'DOT', forVar, keyOrVal) # forVar.{key,value}
681                assignTok = token.copy('ASSIGN', '=')
682                # print id.toCobraSource, '=', kvExpr.toCobraSource
683                stmts.add(AssignExpr(assignTok, assignTok.which, id, kvExpr))  # id = forVar.{key,value}
684                count += 1
685        else  # enumeration, each item of which is matching count sequence
686            # TODO: ? Add check varName.count <> multArgs.count throw RTException
687            for id in _multiArgs
688                intToken = token.copy('INTEGER_LIT', '[count]')
689                intToken.value = count
690                countIntLit = IntegerLit(intToken)
691                exprs = List<of Expr>()
692                exprs.add(countIntLit)
693                forVar = IdentifierExpr(token.copy('ID', varName), varName)
694                idxExpr = IndexExpr(token.copy('LBRACKET', r'['), forVar, exprs)  # forVar[count]
695                assignTok = token.copy('ASSIGN', '=')
696                stmts.add(AssignExpr(assignTok, assignTok.which, id, idxExpr)) 
697                count += 1
698        _block.stmts.insertRange(0, stmts)
699   
700    def inferredType as IType? is override
701        assert _what.type
702        return _what.type.innerType
703
704
705class IfStmt
706    is partial
707    inherits Stmt
708
709    var _cond as Expr
710    var _trueStmts as BlockStmt
711    var _falseStmts as BlockStmt?
712    var _doNotPopIfInheritsStack as bool
713    var _ifInheritsVar as IVar?
714
715    cue init(token as IToken, cond as Expr, trueStmts as BlockStmt, falseStmts as BlockStmt?)
716        base.init(token)
717        _cond = cond
718        _trueStmts  = trueStmts
719        _falseStmts = falseStmts
720
721    get cond from var
722
723    get ifInheritsVar from var
724
725    get trueStmts from var
726   
727    get falseStmts from var
728   
729    get lastToken as IToken is override
730        if _falseStmts
731            return _falseStmts.lastToken
732        else
733            return _trueStmts.lastToken
734
735    def addSubFields
736        base.addSubFields
737        .addField('cond', _cond)
738        .addField('trueStmts', _trueStmts)
739        .addField('falseStmts', _falseStmts)
740        .addField('ifInheritsVar', _ifInheritsVar)
741
742    def _innerClone
743        assert not _ifInheritsVar  # should not have bound imp yet
744        base._innerClone
745        _cond = _cond.clone
746        _trueStmts = _trueStmts.clone
747        if _falseStmts, _falseStmts = _falseStmts.clone
748
749    def _bindImp
750        base._bindImp
751        try
752            _cond.bindImp
753        catch ne as NodeException
754            .compiler.recordError(ne)
755        success
756            _doNotPopIfInheritsStack = false
757            .noAssignmentAllowed(_cond)
758            if _cond.type is not .compiler.boolType
759                _cond = TruthExpr(_cond).bindImp to TruthExpr  # CC: axe cast when supporting 'as this'
760            cond = _cond
761            ifInherits = false
762            # TODO: handle the "x inherits Y" being part of a sequence of "and"ed exprs
763            # CC: combine some of the if statements below when "if inherits" can handle complex expressions
764            if cond inherits InheritsExpr
765                left = cond.left
766                if left inherits IdentifierExpr
767                    leftVar = left.namedDefinition
768                    ifInherits = _ivarIfInherits(cond, leftVar)
769                else if left inherits AssignExpr
770                    if left.left inherits IdentifierExpr
771                        leftVar = left.left.definition
772                        ifInherits = _ivarIfInherits(cond, leftVar)
773            if not ifInherits
774                # check for an if-not-nil
775                if cond inherits TruthExpr
776                    if cond.expr inherits IdentifierExpr
777                        leftVar = cond.expr.definition
778                        leftType = leftVar.typeForIdentifier
779                        if leftType inherits NilableType or (leftType.isReference and leftType is not .compiler.objectType)
780                            # if x ...
781                            # Note: The check for .objectType just above is due to the fact that a type of Object can point to boxed values such as ints and bools which Cobra will treat properly at runtime.
782                            notNil = true
783            # TODO: the checks about variables that are never nil (/always non-nil) need to happen at the *expression* level so they kick in for assert, require, etc.
784            if not ifInherits and not notNil
785                if cond inherits CompareExpr
786                    if cond.op in ['ISNOT', 'NE'] and cond.left inherits IdentifierExpr and cond.right inherits NilLiteral
787                        # if x is not nil ...
788                        # if x <> nil ...
789                        # TODO: also handle "if nil is not x"
790                        # TODO? "if not x is nil"  "if nil is not x"
791                        notNil = true
792                        leftVar = cond.left.definition
793            if notNil
794                if leftVar inherits IVar
795                    if leftVar.type inherits WrappedType
796                        leftVar.ifInheritsStack.push((leftVar.type to WrappedType).theWrappedType)
797                        ifInherits = true
798                else
799                    throw FallThroughException(leftVar)
800            if ifInherits
801                if leftVar inherits IVar
802                    _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek)
803                else
804                    throw FallThroughException(leftVar)
805        if ifInherits
806            if leftVar inherits IVar
807                _ifInheritsVar = leftVar
808                stackCount = leftVar.ifInheritsStack.count
809            else
810                assert false, leftVar
811        _trueStmts.parent = this
812        _trueStmts.bindImp
813        if ifInherits
814            if not _doNotPopIfInheritsStack
815                (leftVar to IVar).ifInheritsStack.pop
816            assert (leftVar to IVar).ifInheritsStack.count == stackCount - 1
817           
818        if _falseStmts
819            _falseStmts.parent = this
820            _falseStmts.bindImp
821
822    def _ivarIfInherits(cond as InheritsExpr, leftVar as INamedNode?) as bool
823        ifInherits = false
824        if leftVar inherits IVar  # if-inherits smarts only work on variables
825            # if x inherits Y ...
826            if (right = cond.right) inherits IPotentialTypeExpr
827                assert right.potentialType
828                leftVar.ifInheritsStack.push(right.potentialType to !)
829            else
830                throw FallThroughException(right)
831            ifInherits = true
832            _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek)
833        return ifInherits   
834
835    def doNotPopIfInheritsStack
836        _doNotPopIfInheritsStack = true
837
838
839class ListenOrIgnoreStmt
840    is abstract, partial
841    inherits Stmt
842   
843    var _event as Expr?
844    var _target as Expr?
845
846    cue init(token as IToken, eventRef as Expr, target as Expr)
847        base.init(token)
848        _event = eventRef
849        _target = target
850
851    def addSubFields
852        base.addSubFields
853        .addField('event', _event)
854        .addField('target', _target)
855
856    get event from var
857   
858    get target from var
859
860    def _innerClone
861        base._innerClone
862        if _event, _event = _event.clone
863        if _target, _target = _target.clone
864
865    def _bindImp
866        base._bindImp
867
868        _event.bindImp
869        if not _event.definition inherits BoxEvent
870            if _event.definition
871                .throwError('The first parameter must be an event, not a [_event.definition.englishName].')
872            else
873                .throwError('The first parameter must be an event.')
874            # TODO: could suggest events with similar names
875
876        .compiler.refExprLevel += 1
877        # ^ to avoid errors such as ".foo requirements arguments"
878        #   the more accurate and informative error about requiring "ref" is detected further below
879        try
880            _target.bindImp
881        finally
882            .compiler.refExprLevel -= 1
883        if not _target inherits RefExpr and not _target inherits AnonymousMethodExpr and not _target.type.nonNil.isDescendantOf(.compiler.delegateType)
884            if _target.definition inherits AbstractMethod
885                sugg = ' Put "ref" before the method such as "ref [_target.toCobraSource]".'
886            else
887                sugg = ''
888            .throwError('The second parameter must be a reference to a method.[sugg]')
889        else
890            # TODO: check that _target is a normal method reference
891            pass
892
893
894class ListenStmt
895    is partial
896    inherits ListenOrIgnoreStmt
897
898    cue init(token as IToken, eventRef as Expr, target as Expr)
899        base.init(token, eventRef, target)
900
901
902class IgnoreStmt
903    is partial
904    inherits ListenOrIgnoreStmt
905
906    cue init(token as IToken, eventRef as Expr, target as Expr)
907        base.init(token, eventRef, target)
908
909
910class LockStmt inherits Stmt is partial
911
912    cue init(token as IToken, expr as Expr, block as BlockStmt)
913        base.init(token)
914        _expr, _block = expr, block
915
916    get expr from var as Expr
917   
918    get block from var as BlockStmt
919   
920    get lastToken as IToken is override
921        return _block.lastToken
922
923    def addSubFields
924        base.addSubFields
925        .addField('expr', .expr)
926        .addField('block', .block)
927
928    def _innerClone
929        base._innerClone
930        _expr = _expr.clone
931        _block = _block.clone
932
933    def _bindImp
934        base._bindImp
935        # TODO: the expression must be a reference type
936        _expr.bindImp
937        _block.bindImp
938
939
940class PassStmt
941    is partial
942    inherits Stmt
943   
944    cue init(token as IToken)
945        base.init(token)
946
947    def _bindImp
948        base._bindImp
949
950
951class AbstractPrintStmt inherits Stmt is abstract, partial
952
953    cue init(token as IToken, destination as Expr?)
954        base.init(token)
955        _destination = destination
956   
957    def addSubFields
958        base.addSubFields
959        .addField('destination', .destination)
960
961    get destination from var as Expr?
962   
963    def _innerClone
964        base._innerClone
965        if _destination, _destination = _destination.clone
966
967    def _bindImp
968        base._bindImp
969        if .destination
970            .destination.bindImp
971            t = .destination.type
972            if not t.isDynamicOrPassThrough and not t.isDescendantOf(.compiler.libraryType('System.IO.TextWriter'))  # TODO-BACKEND
973                .recordError('Invalid destination of type "[t.name]" for "print". Use a TextWriter or subclass thereof instead.')
974
975
976class PrintStmt inherits AbstractPrintStmt is partial
977
978    cue init(token as IToken, destination as Expr?, args as List<of Expr>, stopp as bool)
979        base.init(token, destination)
980        _args, _stop = args, stopp
981
982    get args from var as List<of Expr>
983        has Subnodes
984
985    get stop from var as bool
986
987    def _innerClone
988        base._innerClone
989        _args = _args.toList2
990        for i in _args.count, _args[i] = _args[i].clone
991
992    def _bindImp
993        base._bindImp
994        for i, arg in .args.numbered
995            try
996                arg.bindImp
997            catch ne as NodeException
998                ne.prefixMessage('For "print" arg [i+1]: ')
999                .compiler.recordError(ne)
1000            if arg.type and arg.type inherits VoidType
1001                .recordError('For "print" arg [i+1]: Expression cannot be printed because it does not return a value.')
1002
1003    def addSubFields
1004        base.addSubFields
1005        .addField('stop', .stop)
1006        .addField('args', .args)
1007
1008
1009class PrintRedirectStmt inherits AbstractPrintStmt is partial
1010
1011    var _block as BlockStmt
1012
1013    cue init(token as IToken, destination as Expr, block as BlockStmt)
1014        base.init(token, destination)
1015        _block = block
1016
1017    get block from var
1018
1019    get lastToken as IToken is override
1020        return _block.lastToken
1021
1022    def _innerClone
1023        base._innerClone
1024        _block = _block.clone
1025
1026    def _bindImp
1027        base._bindImp
1028        _destination.bindImp
1029        _block.bindImp
1030
1031    def addSubFields
1032        base.addSubFields
1033        .addField('block', _block)
1034
1035
1036class RaiseStmt
1037    is partial
1038    inherits Stmt
1039    """
1040    Raise an event.
1041   
1042    TODO: test raising an inherited event, either from source or from binary
1043    TODO: Test generic events.
1044    """
1045
1046    var _name = ''
1047    var _exprs as List<of Expr>
1048
1049    # computed in _bindImp
1050    var _definition as BoxEvent?
1051    var _eventType as IType?
1052    var _params as IList<of Param>?
1053    var _args as List<of Expr>?
1054
1055    cue init(token as IToken, exprs as List<of Expr>)
1056        require exprs.count > 0
1057        base.init(token)
1058        _exprs = exprs
1059
1060    def addSubFields
1061        base.addSubFields
1062        .addField('exprs', _exprs)
1063
1064    get name from var
1065
1066    get exprs from var
1067
1068    def _innerClone
1069        base._innerClone
1070        _exprs = _exprs.toList2
1071        for i in _exprs.count, _exprs[i] = _exprs[i].clone
1072
1073    def _bindImp
1074        base._bindImp
1075        hasError = false
1076        for expr in _exprs
1077            try
1078                expr.bindImp
1079            catch ne as NodeException
1080                .compiler.recordError(ne)
1081                hasError = true
1082        if not hasError
1083            expr = _exprs[0]
1084            if expr inherits DotExpr
1085                right = expr.dotRight
1086                defi = right.definition
1087                if defi inherits BoxEvent
1088                    if right inherits CallExpr
1089                        .throwError('Cannot call events. The general form is "raise .<event>, \[<sender>], <arg1>, <arg2>, ...".')
1090                    _definition = defi
1091                    _name = defi.name
1092                    _eventType = defi.handlerType
1093                else
1094                    .throwError('Expecting an event to raise.')
1095            else if expr inherits IdentifierExpr  # ex: raise _eventName
1096                _name = expr.name
1097                _eventType = expr.potentialType
1098                if not _eventType
1099                    hint = ''
1100                    if expr.type inherits MethodSig 
1101                        hint = ' Use "[_name](args)" to invoke the method reference (delegate).'
1102                    .throwError('The target of "raise" is not an event.[hint]')
1103                if _eventType.isDescendantOf(.compiler.delegateType)
1104                    pass
1105                else if _eventType.isDescendantOf(.compiler.exceptionType)
1106                    .throwError('Use "throw" instead of "raise" for the exception "[_eventType.name]".')
1107                else
1108                    .throwError('Invalid expression for raising events. Use "raise .someEvent, args" or "throw SomeException(args)".')
1109            else
1110                .throwError('Invalid expression for raising events. Use "raise .someEvent, args" or "throw SomeException(args)".')
1111            if _eventType inherits MethodSig
1112                _params = _eventType.params
1113            else
1114                member = _eventType.memberForName('invoke')
1115                if member inherits Method
1116                    _params = member.params
1117                else
1118                    .throwError('Cannot find a single "invoke" method for "_eventType.name".')
1119            # TODO: check type compatibility of exprs with params
1120            args = _exprs[1:]
1121            params = _params
1122            unThis = 'Unnecessary "this" which is already implied by raising an event. You can remove it.'
1123            if args.count == params.count
1124                if args.count > 0 and args[0] inherits ThisLit
1125                    .compiler.warning(this, unThis)
1126            else if args.count == params.count - 1
1127                # off by one.
1128                if args[0] inherits ThisLit
1129                    .compiler.warning(this, unThis)
1130                    # since we have 'this', try appending the args object
1131                    args.add(_postCallForType(params[params.count-1].type))
1132                else
1133                    # since we don't have 'this', try prepending 'this'
1134                    args.insert(0, ThisLit(.token).bindImp)
1135            else if args.count == 0 and params.count == 2
1136                # missing both 'this' and event args
1137                args.add(ThisLit(.token).bindImp)
1138                args.add(_postCallForType(params[params.count-1].type))
1139            else
1140                .throwError('Event expects [params.count] arguments, but the "raise" statement is providing [args.count].')
1141            _args = args
1142
1143    def _postCallForType(type as IType) as PostCallExpr     
1144        init = type.memberForName('cue.init')
1145        good = false
1146        if init inherits Initializer
1147            if init.params.count == 0
1148                good = true
1149        else if init inherits MemberOverload
1150            for member in init.members
1151                if member.params.count == 0
1152                    good = true
1153                    break
1154        if good
1155            token = .token.copy('ID', type.name)
1156            return PostCallExpr(token, TypeExpr(token, type), List<of Expr>()).bindImp
1157        else
1158            .throwError('Missing argument for "raise" of type "[type.name]".')
1159        throw FallThroughException() # suppress an error
1160
1161
1162class ReturnStmt
1163    is partial
1164    inherits Stmt
1165
1166    var _expr as Expr?
1167
1168    cue init(token as IToken, expr as Expr?)
1169        base.init(token)
1170        _expr = expr
1171
1172    def addSubFields
1173        base.addSubFields
1174        .addField('expr', _expr)
1175
1176    get expr from var
1177
1178    def _innerClone
1179        base._innerClone
1180        if _expr, _expr = _expr.clone
1181
1182    def _bindImp
1183        base._bindImp
1184        expr = _expr
1185        curCodeMember = .compiler.codeMemberStack.peek
1186        if _expr
1187            expr = _expr.bindImp
1188            if not expr.canBeAssignedTo(curCodeMember.resultType)
1189                suffix = if(curCodeMember.resultType is .compiler.voidType, 'not declared to return anything.', 'declared to return a [curCodeMember.resultType.name].')
1190                .throwError('Cannot return [expr.type.name] because "[curCodeMember.name]" is [suffix]')
1191            expr.contextType = curCodeMember.resultType
1192        else
1193            # TODO: the check for .passThroughType below is required only because lambdas are using it for their return value instead of setting it to the correct type. so improving lambdas (AnonExpr) will allow the removal of that check
1194            if curCodeMember.resultType is not .compiler.voidType and curCodeMember.resultType is not .compiler.passThroughType
1195                .throwError('Return statement must return a [curCodeMember.resultType.name], or [curCodeMember.name] must have its return type removed.')
1196        curCodeMember.hasReturnStmt = true
1197        _backEndResultVarName = curCodeMember.backEndResultVarName
1198
1199
1200class TraceStmt
1201    is abstract, partial
1202    inherits Stmt
1203
1204    var _codePart as AbstractMethod
1205
1206    cue init(token as IToken, codePart as AbstractMethod)
1207        base.init(token)
1208        _codePart = codePart
1209
1210    get codePart from var
1211
1212    def includeTraces as bool   
1213        return .compiler.options.boolValue('include-traces')
1214
1215
1216class TraceLocationStmt
1217    is partial
1218    inherits TraceStmt
1219
1220    cue init(token as IToken, codePart as AbstractMethod)
1221        base.init(token, codePart)
1222
1223
1224class TraceAllStmt
1225    is partial
1226    inherits TraceStmt
1227
1228    cue init(token as IToken, codePart as AbstractMethod)
1229        base.init(token, codePart)
1230
1231
1232class TraceExprsStmt
1233    is partial
1234    inherits TraceStmt
1235
1236    var _exprs as List<of Expr>
1237
1238    cue init(token as IToken, codePart as AbstractMethod, exprs as List<of Expr>)
1239        base.init(token, codePart)
1240        _exprs = exprs
1241
1242    get exprs from var
1243        has Subnodes
1244
1245    def _innerClone
1246        base._innerClone
1247        _exprs = _exprs.toList2
1248        for i in _exprs.count, _exprs[i] = _exprs[i].clone
1249
1250    def _bindImp
1251        base._bindImp
1252        for expr in _exprs, expr.bindImp
1253
1254
1255class TryStmt
1256    is partial
1257    inherits Stmt
1258
1259    var _tryBlock as BlockStmt
1260    var _catchBlocks as List<of CatchBlock>
1261    var _successBlock as BlockStmt?
1262    var _finallyBlock as BlockStmt?
1263    var _varNumber as int
1264
1265    cue init(token as IToken, tryBlock as BlockStmt, catchBlocks as List<of CatchBlock>, successBlock as BlockStmt?, finallyBlock as BlockStmt?)
1266        base.init(token)
1267        _tryBlock = tryBlock
1268        _catchBlocks = catchBlocks
1269        _successBlock = successBlock
1270        _finallyBlock = finallyBlock
1271
1272    get lastToken as IToken is override
1273        if _finallyBlock, return _finallyBlock.lastToken
1274        if _successBlock, return _successBlock.lastToken
1275        if _catchBlocks.count, return _catchBlocks[_catchBlocks.count-1].lastToken
1276        return _tryBlock.lastToken
1277
1278    def addSubFields
1279        base.addSubFields
1280        .addField('tryBlock', _tryBlock)
1281        .addField('catchBlocks', _catchBlocks)
1282        .addField('succesBlock', _successBlock)
1283        .addField('finallyBlock', _finallyBlock)
1284
1285    get tryBlock from var
1286   
1287    get catchBlocks from var
1288        has Subnodes
1289
1290    get successBlock from var
1291   
1292    get finallyBlock from var
1293   
1294    def _innerClone
1295        base._innerClone
1296        _tryBlock = _tryBlock.clone
1297        _catchBlocks = _catchBlocks.toList
1298        for i in _catchBlocks.count, _catchBlocks[i] = _catchBlocks[i].clone
1299        if _successBlock, _successBlock = _successBlock.clone
1300        if _finallyBlock, _finallyBlock = _finallyBlock.clone
1301
1302    def _bindImp
1303        base._bindImp
1304        _varNumber = .compiler.curBox.makeNextPrivateSerialNumber
1305        _tryBlock.bindImp
1306        for cb in _catchBlocks
1307            cb.bindImp
1308        if _successBlock
1309            _successBlock.bindImp
1310        if _finallyBlock
1311            _finallyBlock.bindImp
1312        # error check
1313        if _catchBlocks.count
1314            for stmt in _tryBlock.stmts
1315                if stmt inherits YieldStmt
1316                    stmt.recordError('Cannot yield a value in the body of a try block with a catch clause.')
1317        # error check
1318        for cb in _catchBlocks
1319            for stmt in cb.block.stmts
1320                if stmt inherits YieldStmt
1321                    stmt.recordError('Cannot yield a value in the body of a catch clause.')
1322        # error check
1323        if _finallyBlock
1324            for stmt in _finallyBlock.stmts
1325                if stmt inherits YieldStmt
1326                    stmt.recordError('Cannot yield in the body of a finally clause.')
1327
1328
1329class CatchBlock
1330    is partial
1331    inherits SyntaxNode
1332
1333    var _var as AbstractLocalVar?
1334    var _typeNode as ITypeProxy?
1335    var _type as IType?
1336    var _block as BlockStmt
1337    var _varName as String?
1338
1339    cue init(token as IToken, block as BlockStmt)
1340        """
1341        This is for `success`, `finally` and typeless `catch` blocks.
1342        """
1343        base.init(token)
1344        _block = block
1345
1346    cue init(token as IToken, varr as AbstractLocalVar, block as BlockStmt)
1347        """
1348        This is for `catch` blocks that specify a type and variable.
1349        """
1350        base.init(token)
1351        _var = varr
1352        _varName = varr.name
1353        _block = block
1354
1355    cue init(token as IToken, typeNode as ITypeProxy, block as BlockStmt)
1356        """
1357        This is for `catch` blocks that specify a type, but no variable.
1358        """
1359        base.init(token)
1360        _typeNode = typeNode
1361        _block = block
1362
1363    get block from var
1364
1365    get type from var
1366
1367    get varName from var
1368
1369    get lastToken as IToken
1370        return _block.lastToken
1371
1372    def _innerClone
1373        base._innerClone
1374        assert _var is nil
1375        _block = _block.clone
1376
1377    def _bindImp
1378        base._bindImp
1379        if _var
1380            _var.bindImp
1381            # TODO: should this be using .bindVar like others?
1382            # TODO: wtf is isTracked again?
1383            if not _var.isTracked
1384                varr = _var to LocalVar  # CC: axe typecast
1385                codePart = .compiler.codeMemberStack.peek
1386                existingVar = codePart.findLocal(varr.name)
1387                if existingVar
1388                    if existingVar.type is not varr.type
1389                        .throwError('Cannot redeclare "[varr.name]" as "[varr.type.name]" because it was declared as "[existingVar.type.name]" earlier.')
1390                else
1391                    codePart.addLocal(varr)
1392            if _type is nil
1393                _type = _var.type
1394                assert _type
1395        else if _typeNode
1396            _typeNode.bindImp
1397            _type = _typeNode.realType  # CC: combine with above: _type = _typeNode.bindImp.realType
1398        _sharpHelperName = '_lh_catch_[.compiler.curBox.makeNextPrivateSerialNumber]'
1399        _block.bindImp
1400
1401
1402class ThrowStmt
1403    is partial
1404    inherits Stmt
1405
1406    var _expr as Expr?
1407
1408    cue init(token as IToken, expr as Expr?)
1409        base.init(token)
1410        _expr = expr
1411
1412    def addSubFields
1413        base.addSubFields
1414        .addField('expr', '_expr')
1415
1416    get expr from var
1417
1418    def _innerClone
1419        base._innerClone
1420        if _expr, _expr = _expr.clone
1421
1422    def _bindImp
1423        base._bindImp
1424        if _expr
1425            _expr.bindImp
1426            if not _expr.hasError
1427                exceptionType = .compiler.exceptionType
1428                typeType = .compiler.typeType
1429                if _expr.canBeAssignedTo(exceptionType)
1430                    pass
1431                else if _expr.canBeAssignedTo(typeType)
1432                    augment = ''
1433/#
1434                    CC:
1435                    if _expr responds to (get definition)
1436                        defin = _expr.definition
1437                        if defin responds to (def isDescendantOf(t as IType))
1438                            augment = 'Try instantiating it with parentheses such as "[_expr.toCobraSource]()".'
1439#/
1440                    defin = _expr.definition
1441                    if defin inherits IType and defin.isDescendantOf(exceptionType)
1442                        augment = 'Try instantiating it with parentheses such as "[_expr.toCobraSource]()".'
1443                    if augment == ''
1444                        augment = 'Also, this type does not inherit from Exception.'
1445                    .throwError('Cannot throw a type. [augment]')
1446                else
1447                    if _expr.definition inherits BoxEvent
1448                        reason = 'it is an event, not an exception. Either throw an exception or use "raise" on the event instead of "throw"'
1449                    else
1450                        reason = 'it does not inherit from Exception'
1451                    .throwError('Cannot throw "[_expr.type.name]" because [reason].')
1452        .compiler.curCodeMember.hasThrowStmt = true
1453
1454
1455class UsingStmt
1456    is partial
1457    inherits Stmt
1458
1459    var _varExpr as NameExpr
1460    var _var as IVar?
1461    var _initExpr as Expr
1462    var _block as BlockStmt
1463
1464    cue init(token as IToken, varExpr as NameExpr, initExpr as Expr, block as BlockStmt)
1465        base.init(token)
1466        _varExpr = varExpr
1467        _initExpr = initExpr
1468        _block = block
1469
1470    get lastToken as IToken is override
1471        return _block.lastToken
1472       
1473    get initExpr from var
1474
1475    get block from var
1476
1477    def _innerClone
1478        assert _var is nil
1479        base._innerClone
1480        _varExpr = _varExpr.clone
1481        _initExpr = _initExpr.clone
1482        _block = _block.clone
1483
1484    def _bindImp
1485        base._bindImp
1486        _initExpr.bindImp
1487        _var = .bindVar(_varExpr)
1488        # TODO: add an error check that the var type and expr type are compatible
1489        _block.bindImp
1490
1491    def inferredType as IType? is override
1492        assert _initExpr.type
1493        return _initExpr.type
1494
1495
1496class AbstractWhileStmt inherits Stmt is partial
1497
1498    cue init(token as IToken, expr as Expr, block as BlockStmt)
1499        base.init(token)
1500        _expr, _block = expr, block
1501
1502    get lastToken as IToken is override
1503        return _block.lastToken
1504
1505    get expr from var as Expr
1506       
1507    get block from var as BlockStmt
1508
1509    def addSubFields
1510        base.addSubFields
1511        .addField('expr', _expr)
1512        .addField('block', _block)
1513
1514    def _innerClone
1515        base._innerClone
1516        _expr = _expr.clone
1517        _block = _block.clone
1518
1519    def _bindExpr
1520        try
1521            _expr.bindImp
1522        catch ne as NodeException
1523            .compiler.recordError(ne)
1524        success
1525            .noAssignmentAllowed(_expr)
1526            if _expr.type is not .compiler.boolType
1527                _expr = TruthExpr(_expr).bindImp to TruthExpr  # CC: axe when I have "as this"
1528
1529
1530class WhileStmt inherits AbstractWhileStmt is partial
1531
1532    cue init(token as IToken, expr as Expr, block as BlockStmt)
1533        base.init(token, expr, block)
1534
1535    def _bindImp
1536        base._bindImp
1537        _bindExpr
1538        _block.bindImp
1539
1540
1541class PostWhileStmt inherits AbstractWhileStmt is partial
1542
1543    cue init(token as IToken, expr as Expr, block as BlockStmt)
1544        base.init(token, expr, block)
1545
1546    def _bindImp
1547        _block.bindImp
1548        _bindExpr
1549        base._bindImp
1550
1551
1552class YieldStmt
1553    is abstract, partial
1554    inherits Stmt
1555
1556    # CC: axe when initializers are inherited
1557    cue init(token as IToken)
1558        base.init(token)
1559
1560    def _bindImp
1561        base._bindImp
1562        if .compiler.curCodeMember inherits Initializer
1563            .throwError('Cannot use yield statements in initializers.')
1564        # CC: should there be a check for property and indexer setters?
1565        .compiler.curCodeMember.hasYieldStmt = true
1566
1567
1568class YieldBreakStmt
1569    is partial
1570    inherits YieldStmt
1571
1572    # CC: axe when initializers are inherited
1573    cue init(token as IToken)
1574        base.init(token)
1575
1576    def _bindImp
1577        base._bindImp
1578
1579
1580class YieldReturnStmt
1581    is partial
1582    inherits YieldStmt
1583
1584    var _expr as Expr?
1585
1586    cue init(token as IToken, expr as Expr?)
1587        base.init(token)
1588        _expr = expr
1589
1590    get expr from var
1591   
1592    def addSubFields
1593        base.addSubFields
1594        .addField('expr', _expr)
1595
1596    def _innerClone
1597        base._innerClone
1598        if _expr, _expr = _expr.clone
1599
1600    def _bindImp
1601        base._bindImp
1602        expr = _expr
1603        curCodeMember = .compiler.codeMemberStack.peek
1604        if expr
1605            _expr.bindImp
1606            resultType = curCodeMember.resultType
1607            if not resultType inherits StreamType and resultType <> .compiler.enumerableType and resultType <> .compiler.enumeratorType
1608                bad = true
1609                if resultType inherits Box
1610                    ienumerableOf = .compiler.enumerableOfType
1611                    ienumeratorOf = .compiler.enumeratorOfType
1612                    bad = resultType is not ienumerableOf and resultType is not ienumeratorOf and not resultType.isDirectConstructionOf(ienumerableOf) and not resultType.isDirectConstructionOf(ienumeratorOf)
1613                if bad, .throwError('Cannot yield unless the return type is an iterator type. Try "[expr.type.name]*".')
1614            if resultType inherits StreamType
1615                elementType = resultType.innerType to !
1616            else if resultType inherits Box and (resultType to Box).isGeneric
1617                elementType = (resultType to Box).genericParams[0]
1618            else
1619                elementType = .compiler.objectType
1620            if not expr.canBeAssignedTo(elementType)
1621                suffix = if(elementType is .compiler.voidType, 'not declared to return anything.', 'declared to return a [elementType.name].')
1622                .throwError('Cannot return [expr.type.name] because "[curCodeMember.name]" is [suffix]')
1623            _expr.contextType = elementType
1624        else
1625            # TODO: can there just be "yield return"?
1626            pass
1627        curCodeMember.hasYieldStmt = true
1628       
1629class MultiTargetAssignStatement inherits Stmt is partial
1630    """
1631    Handle statements of form 
1632        a, b, ... = <list-generating-expr>
1633    or
1634        a, b, ... = <expr>, <expr>, ...
1635
1636    Gets turned into various sequences of assignExpr.
1637        tmp = <List-Generating-Expr>
1638        a = tmp[0]
1639        b = tmp[1]
1640        ...
1641        a = <expr>
1642        b = <expr>
1643        ...
1644    TODO: support a, b[,...] = <Enumerable> 
1645    TODO: support a, b[,...] = <KeyValuePair|DictionaryEntry>
1646    """
1647
1648    var _targets as List<of Expr>       # Lvalues
1649    # exactly one of the following three should be non-nil
1650    var _rightValues as List<of Expr>?  # list of source expressions
1651    var _source as Expr?                # evaluate to IList/Array/String or otherwise indexable by int
1652    var _rvals as List<of Expr>?        # list of source expressions
1653   
1654    var _block as BlockStmt?            # the rewritten/expanded code
1655
1656    cue init(opToken as IToken, args as List<of Expr>, source as Expr?, rvals as List<of Expr>?)
1657        base.init(opToken)
1658        assert opToken.which == 'ASSIGN'
1659        assert (source or rvals) and not (source and rvals) # either but not both
1660        _targets = args
1661        _source = source
1662        _rvals = rvals
1663
1664    def addSubFields
1665        base.addSubFields
1666        .addField('targets', _targets)
1667        .addField('source', _source)
1668        .addField('rvals', _rvals)
1669        .addField('block', _block)
1670
1671    get targets from var
1672
1673    get source from var
1674
1675    get rvals from var
1676
1677    get block from var
1678
1679    def _innerClone
1680        base._innerClone
1681        _targets = _targets.toList2
1682        for i in _targets.count, _targets[i] = _targets[i].clone
1683        if _rightValues
1684            _rightValues = _rightValues.toList
1685            for i in _rightValues.count, _rightValues[i] = _rightValues[i].clone
1686        if _source, _source = _source.clone
1687        if _rvals
1688            _rvals = _rvals.toList
1689            for i in _rvals.count, _rvals[i] = _rvals[i].clone
1690        if _block, _block.clone
1691
1692    def _bindImp
1693        assert _source or _rvals
1694        base._bindImp
1695        for target in _targets
1696            if not _isLValue(target)
1697                .throwError('"[target.toCobraSource]" is not an assignable lvalue (identifier, var, property or indexer).')
1698        if _rvals
1699            if _targets.count <> _rvals.count
1700                .throwError('The number of targets ([_targets.count]) must be the same as the number of expressions ([_rvals.count]) assigned to them')
1701            #If rvals are all safe to assign (no rerefs) we can do the simplest thing and turn 
1702            # them into a sequence of individual assignments.   i.e a,b = 1,2  or a,b = c,d   cases
1703            # If not, we need to turn the rvals list into a literal list, pinning the current values,
1704            # and do the expansion on the list as a single item  i.e  a,b = b,a -> a,b = [b,a]
1705            if _isSafeToSimpleAssign()
1706                _makeBlockForSimpleAssign
1707            else
1708                _makeBlockForSimpleAssign  # to get assignment exprs for error checking and code gen
1709                # to signal to back-end to use RightValues approach:
1710                _rightValues = _rvals
1711                _rvals = nil           
1712        if _source
1713            #TODO: chk _source supports an int indexer  somehow...
1714            #if not .can_be_indexed_by_int_indexer(_source)
1715            #   .throwError(r"rhs expression [_source.toCobraSource] must support being indexed by an integer offset (e.g rhs[0]")
1716            _makeBlockForSource
1717
1718        if _block, _block.bindImp
1719       
1720    def _isLValue(id as Expr) as bool
1721        # TODO: push to Expr.isLValue. probably require .didBindImp
1722        # TODO: for DotExpr and IndexExpr check if there is a setter
1723        # TODO: for MemberExpr check that its a setter property or visible var (not a method)
1724        return id inherits IdentifierExpr or id inherits DotExpr or id inherits IndexExpr
1725       
1726    def _isSafeToSimpleAssign as bool
1727        # conservatively we just presume its safe if the rvals list is all Literals
1728        if all for e in _rvals get e inherits Literal
1729            return true 
1730        # or if identifiers in rvals are not also in target
1731        if _disjointIdentifierLists()
1732            return true
1733        # There are possibly others but above expected to be most common usage 
1734        # TODO: expand this for more wide ranging simple assignment e.g MemberExprs non matching
1735        return false
1736
1737    def _disjointIdentifierLists as bool
1738        # Specifically all of targets list are IdentifierExprs and rvals are Literals, DotExprs,
1739        # MemberExprs or Indentifiers and any Identifiers are not also in target list
1740        tnames = Set<of String>()
1741        for t in _targets
1742            if t inherits IdentifierExpr or t inherits AsExpr
1743                tnames.add((t to NameExpr).name)
1744            else
1745                return false
1746        if all for e in _rvals get e inherits Literal or e inherits DotExpr or e inherits MemberExpr or _
1747            e inherits AbstractToExpr or _
1748            (e inherits IdentifierExpr and (e to IdentifierExpr).name not in tnames)
1749            return true 
1750        return false
1751       
1752    def _makeBlockForSimpleAssign
1753        """
1754        Construct expanded assignment block for a multi target assignment from a list of expressions.
1755        Assignment block contains
1756            id0 = rvals[0] # id0 is contents of targets[0]
1757            id1 = rvals[1] # id1 is contents of targets[1]
1758                (... repeated for number of items in targets specifying ids (lvalues))
1759        """
1760        assert _targets.count == _rvals.count
1761        stmts = List<of Stmt>()
1762        assignTok = .lastToken  # the one we stored in .init
1763        for i, id in _targets.numbered
1764            expr = _rvals[i]
1765            stmts.add(AssignExpr(assignTok, assignTok.which, id, expr))
1766        _block = BlockStmt(.lastToken.copy('INDENT', ''), stmts)
1767       
1768    def _makeBlockForSource
1769        """
1770        Construct expanded assignment block for a multi target assignment from a single source expression.
1771        if fewer targets than exprs in _source - extra exprs (silently) ignored
1772        TODO: give error when rvalues are too many
1773        if more targets than exprs in _source   - will emit runtime exception
1774            above differ fm python - both give errors
1775        Unchecked assumptions; 
1776            source is int indexable (x[n]) - c# compiler pick up currently
1777        Assignment block contains
1778            lh_mt_serialNum = <source>
1779            id0 = lh_mt_serialNum[0] # id0 is contents of targets[0]
1780            id1 = lh_mt_serialNum[1] # id1 is contents of targets[1]
1781                (... repeated for number of items in targets specifying ids (lvalues))
1782        """
1783        stmts = List<of Stmt>()
1784        assignTok = .lastToken  # the one we stored in init
1785        serialNum = .compiler.curBox.makeNextPrivateSerialNumber
1786        tmpName = 'lh_mt_[serialNum]'  # lh=local handler, mt = multiTarget
1787        ttoken = .lastToken.copy('XXX', 'xxx'# invalid template token for copying
1788        idToken = ttoken.copy('ID', tmpName)
1789       
1790        tmpId = IdentifierExpr(idToken, idToken.text)
1791        s = AssignExpr(assignTok, assignTok.which, tmpId, _source)
1792        stmts.add(s
1793
1794        count = 0
1795        for id in _targets
1796            intToken = ttoken.copy('INTEGER_LIT', '[count]')
1797            intToken.value = count
1798            countIntLit = IntegerLit(intToken)
1799            exprs = List<of Expr>()
1800            exprs.add(countIntLit)
1801            tmpId = IdentifierExpr(ttoken.copy('ID', tmpName), tmpName)
1802            idxExpr = IndexExpr(ttoken.copy('LBRACKET', r'['), tmpId, exprs)
1803            stmts.add(AssignExpr(assignTok, assignTok.which, id, idxExpr))
1804            count += 1
1805        _block = BlockStmt(ttoken.copy('INDENT', ''), stmts)   
Note: See TracBrowser for help on using the browser.