Wiki

root/cobra/trunk/Source/Node.cobra

Revision 2633, 31.8 KB (checked in by Charles.Esterbrook, 5 months ago)

Support COBRA_TECHSTRING_INDENT environment variable.
reported-by:hopscc
reference:http://cobra-language.com/forums/viewtopic.php?f=4&t=722&start=40

  • Property svn:eol-style set to native
Line 
1use System.Reflection
2
3
4class SubnodesAttribute inherits Attribute
5    """
6    Put this attribute on properties that represent a collection of subnodes of the declaring node class.
7    Node.replaceChild will use these properties when searching for node references to be replaced (which happens during node transformation).
8    There may be other uses for this in the future.
9    The property value should respond to .indexOf(node) and "subnodes[index] = newNode" which are covered by List<of>.
10    Since dynamic binding is used, inheriting from List<of> is not strictly required.
11    """
12    pass
13
14
15class SourceException inherits SystemException
16    """
17    The abstract base class of all exceptions about the source code that is being compiled.
18    This includes both errors and warnings.
19    """
20
21    var _message as String
22
23    cue init(msg as String?)
24        base.init(msg)
25        _message = msg to !
26
27    get isError as bool
28        return true
29   
30    get hasSourceSite as bool
31        """
32        Return true if the error really has source information (filename and line number). The
33        `fileName` and `lineNum` properties will not be invoked if this returns false.
34        """
35        return false
36       
37    get fileName as String
38        require .hasSourceSite
39        return ''  # CC: use abstract
40
41    get lineNum as int
42        require .hasSourceSite
43        return 0  # CC: use abstract
44   
45    get message as String is override
46        return _message
47
48    def consoleString as String
49        type = if(.isError, 'error', 'warning')
50        if .hasSourceSite
51            return '[.fileName]([.lineNum]): [type]: [.message]'
52        else
53            return '[type]: ' + .message
54
55    def appendToMessage(text as String)
56        _message += text
57       
58    def toString as String is override
59        return '[.getType.name]: [.consoleString]'
60
61    def writeHtmlTo(dest as HtmlWriter)
62        type = if(.isError, 'error', 'warning')
63        tdClass = if(.isError, 'typeError', 'typeWarning')
64        if .hasSourceSite
65            pathName = Path.combine(Environment.currentDirectory, .fileName)
66            fileLink = '<a class=fileName href="txmt://open/?url=file://[_urlEncode(pathName)]&line=[.lineNum]">[_htmlEncode(.fileName)]</a>'  # TODO: eliminate hard coding of TextMate URL scheme
67            dest.writeHtml('<tr class=[type]> <td class=fileName>[fileLink]</td> <td class=lineNum>[.lineNum]</td> <td class=[tdClass]> [type] </td> <td class=message>[_htmlEncode(.message)]</td> </tr>[dest.newLine]')
68        else
69            dest.writeHtml('<tr class=[type]> <td class=fileName></td> <td class=lineNum></td> <td class=[tdClass]> [type] </td> <td class=message>[_htmlEncode(.message)]</td> </tr>[dest.newLine]')
70
71    get trClass as String
72        return 'error'
73
74    def _htmlEncode(s as String) as String
75        return CobraCore.htmlEncode(s)
76
77    def _urlEncode(s as String) as String
78        return s  # TODO
79
80
81class NodeException
82    inherits SourceException
83   
84    var _node as INode
85
86    cue init(node as INode, msg as String)
87        base.init(msg)
88        _node = node
89
90    get node from var
91
92    get hasSourceSite as bool is override
93        return _node inherits ISyntaxNode
94       
95    get fileName as String is override
96        return (_node to ISyntaxNode).token.fileName
97       
98    get lineNum as int is override
99        return (_node to ISyntaxNode).token.lineNum
100       
101    def prefixMessage(s as String)
102        _message = s + _message
103
104
105class DummyNode
106    inherits Node
107    pass
108
109
110class NodeMultiException
111    inherits NodeException
112    """
113    To provide good error recovery and to fully report all errors, it can be useful for parts of
114    the code to catch exceptions that they then may need to throw all at once. Hence, the
115    NodeMultiException. At least BinaryOpExpr and SequenceLit use it and Compiler.recordErrorAndThrow checks
116    for it.
117    """
118
119    # TODO: override .prefixMessage( to do the right thing
120   
121    var _exceptions as List<of NodeException>
122
123    cue init(exceptions as vari NodeException?)
124        """
125        As a convenience to the caller, nil exceptions are quietly ignored and
126        NodeMultiExceptions are recursively processed.
127        """
128        .init(exceptions to IEnumerable<of NodeException?>)
129
130    cue init(exceptions as IEnumerable<of NodeException?>)
131        base.init(DummyNode(), 'multi exception')  # TODO: the use of DummyNode() here shows that the class hierarchy is off
132        _exceptions = List<of NodeException>()
133        _addExceptions(exceptions)
134        assert _exceptions.count, 'At least one exception must exist.'
135
136    def _addExceptions(exceptions as IEnumerable<of NodeException?>)
137        for exc in exceptions
138            if exc
139                if exc inherits NodeMultiException
140                    _addExceptions(exc.exceptions to passthrough)
141                else
142                    _exceptions.add(exc)
143
144    get exceptions from var
145
146
147class SyntaxNodeException
148    inherits NodeException
149
150    var _synNode as ISyntaxNode
151
152    cue init(node as ISyntaxNode, msg as String)
153        require
154            node.token.lineNum
155        body
156            base.init(node, msg)
157            _synNode = node
158
159    get hasSourceSite as bool is override
160        return true
161
162    get fileName as String is override
163        return _synNode.token.fileName
164
165    get lineNum as int is override
166        return _synNode.token.lineNum
167
168
169interface INode
170    """
171    Just about everything in the Cobra compiler that forms a data
172    structure of a program ultimately inherits from Node. For example,
173    all types and ASTs descend from Node.
174
175    However, the tokenizer, parser, compiler object and command line
176    objects do not. They don't form the data structure that represents a
177    program.
178    """
179
180    get serialNum as int
181
182    get addOnValues as Dictionary<of String, dynamic>
183        """
184        Returns a dictionary, initially empty, of named values that add-ons
185        can use to store data related to the node.
186        """
187
188    pro superNode as INode?
189
190    get hasError as bool
191
192    ##
193    ## Binding
194    ##
195
196    def bindInh
197        """
198        Invoke this method; do not override it.
199        """
200
201    def bindInt as INode  # CC: as same
202        """
203        Invoke this method; do not override it.
204        """
205
206    def computeMatchingBaseMembers
207        """
208        Invoke this method; do not override it.
209        """
210
211    def bindImp as dynamic  # CC: as same
212        """
213        Invoke this method; do not override it.
214        """
215
216    def bindAll as INode  # CC: as same
217
218    get didBindInt as bool
219
220    get didBindImp as bool
221
222    def throwError(msg as String)
223        """
224        Sometimes an object detects an error in a subobject or argument. On that occasion,
225        invoke `throwError` on that object. There are other error related methods and
226        properties in Node.
227        """
228
229    def recordError(msg as String)
230        """
231        Records an error message for this node, but does not throw an exception.
232        This is appropriate when the error can be immediately recovered from.
233        """
234
235    ##
236    ## Transformation
237    ##
238
239    def replaceChild(find as INode, replace as INode) as bool
240
241    pro transformedFrom as INode?
242
243    ##
244    ## Converting to string
245    ##
246
247    def toTechString as String
248
249    get idString as String
250
251    get minimalString as String
252        """
253        Includes only atomic data like numbers and strings.
254        """
255
256    get shallowString as String
257        """
258        Includes minimal string plus references to other objects (their minimal strings).
259        """
260
261    get deepString as String
262        """
263        Includes shallow string plus subobjects (their deep strings).
264        """
265
266    def writeDeepString(iw as IndentedWriter)
267
268    def writeDeepString
269        """
270        Wraps Console.out in an IndentedWriter.
271        """
272
273
274class Node
275    is abstract, partial
276    implements INode
277    """
278    Just about everything in the Cobra compiler that forms a data
279    structure of a program ultimately inherits from Node. For example,
280    all types and ASTs descend from Node.
281
282    However, the tokenizer, parser, compiler object and command line
283    objects do not.
284
285    Rules:
286        * Override _bindFoo methods. Invoke base.
287        * Invoke .bindFoo methods. Do not override them except for special circumstances.
288        * Override _replaceChild if the default implementation is not sufficient or performant.
289        * Override .superNode if the default implementation is not sufficient (rare).
290
291    What you can do:
292        * Get .superNode during _bindFoo if you need it for anything.
293        * Invoke _transformTo(newNode) to replace the current node with newNode in the .superNode.
294        * Assign the results of .bindFoo to a local var. This will contain the transformed value, if any.
295          Or you can just rely on the original field such as "_left.bind; print _left".
296
297    Study subclasses for examples of following these rules and using these features.   
298    """
299
300    shared
301        var _nextNodeSerialNum = 1001
302        var _compiler as ICompilerForNodes?
303
304        def setCompiler(c as ICompilerForNodes?)
305            _compiler = c
306
307        def getCompiler as ICompilerForNodes
308            assert _compiler
309            return _compiler to !
310
311        var _typeProvider as ITypeProvider?
312       
313        get hasTypeProvider as bool
314            return _typeProvider is not nil
315
316        pro typeProvider as ITypeProvider?
317            get
318                if _typeProvider, return _typeProvider
319                if _compiler, return _compiler
320                return nil
321            set
322                _typeProvider = value
323
324        var _secondToLast as Node?
325
326        def reset
327            # invoked by Compiler.init
328            .setCompiler(nil)
329            .typeProvider = nil
330            _secondToLast = nil
331
332
333    get compiler as ICompilerForNodes?
334        """
335        Compiler is an object property instead of a shared property so that it shows up in
336        exception reports which then leads to a compiler details table.
337        Eh, but then it's always nil because a finally clause sets it so...
338        TODO: the exception report feature needs a delegate for adding additional information
339        """
340        return _compiler
341
342    get backEnd as BackEnd?
343        return .compiler.backEnd
344
345    # CC: use a `private` section instead
346    var _serialNum as int is private
347
348    var _isImplicit as bool is private
349
350    var _didStartBindInh as bool is private
351    var _didStartBindInt as bool is private
352    var _didStartBindImp as bool is private
353
354    var _isBindingInh as bool is private
355    var _isBindingInt as bool is private
356    var _isBindingImp as bool is private
357
358    var _didBindInhBase as bool is private
359    var _didBindIntBase as bool is private
360    var _didBindImpBase as bool is private
361
362    var _didBindInh as bool is private
363    var _didBindInt as bool is private
364    var _didBindImp as bool is private
365
366    var _superNode as INode?
367        """
368        Possibly points to the node that owns this node.
369        Set automatically in .bindImp.
370        """
371
372    var _transformedTo as INode?
373        """
374        Set by _transformTo and used by .bind.
375        """
376    var _transformedFrom as INode?
377        """
378        Set by _transformTo.
379        """
380    var _willReplaceChild as dynamic?
381        """
382        Used by replaceChild and _finishTransformation.
383        """
384
385    cue init
386        ensure
387            .serialNum > 1000
388            not .didBindInh
389            not .didBindInt
390            not .didBindImp
391        body
392            base.init
393            _serialNum = _nextNodeSerialNum
394            _nextNodeSerialNum += 1
395
396    get serialNum from var
397
398    pro isImplicit from var
399        """
400        Indicates if the source code defined this node. Defaults to false.
401        Implicit nodes can include implicit symbols like `value` and nodes defined for _transfromTo purposes.
402        Checking .isImplicit is used for various purposes including avoiding false warnings.
403        Setting .isImplicit to true is only required in cases where it affects the compiler.
404        """
405
406    def setSerialNum(serialNum as int) is protected
407        """
408        Only call this method if you need to reset the serial number of a cloned/copied node.
409        Otherwise, the serial number should be considered read only.
410        """
411        require
412            .serialNum > 0
413            serialNum > .serialNum
414        ensure
415            .serialNum == serialNum
416        body
417            _serialNum = serialNum
418
419    pro superNode as INode?
420        get
421            return var
422        set
423            var = value
424
425    var _addOnValues as Dictionary<of String, Object>?
426   
427    get addOnValues as Dictionary<of String, dynamic>
428        if _addOnValues is nil
429            _addOnValues = Dictionary<of String, dynamic>()
430        return _addOnValues to !
431
432    ##
433    ## Binding
434    ##
435
436    def _stackPush
437        """
438        Invoked during various bind phases to push this onto the .compiler.nodeStack.
439        Can be overridden by subclasses to maintain other stacks (often of a more specific type).
440        """
441        if .compiler, .compiler.nodeStack.push(this)
442
443    def _stackPop
444        """
445        Invoked at the end of various bind phases to pop this off the .compiler.nodeStack.
446        Can be overridden by subclasses that maintain additional stacks.
447        """
448        if .compiler, .compiler.nodeStack.pop
449
450    def bindInh
451        require
452            .compiler or .typeProvider
453        ensure
454            old .isBindingInh implies .isBindingInh and not .didBindInh
455            not old .didBindInh implies .didBindInh and not .isBindingInh
456        body
457            if not _didBindInh and not _isBindingInh
458                _didStartBindInh = true
459                _isBindingInh = true
460                _stackPush
461                try
462                    _didBindInhBase = false
463                    _bindInh
464                    assert _didBindInhBase
465                    _didBindInh = true
466                finally
467                    _finishChildReplacements
468                    _stackPop
469                    _isBindingInh = false
470
471    def _bindInh
472        """
473        Bind inheritance such as base classes and implemented interfaces.
474        Override this method; do not send it. Invoke base.
475        """
476        require .didStartBindInh and .isBindingInh
477        _didBindInhBase = true
478
479    def bindInt as INode  # CC: as same
480        """
481        Invoke this method; do not override it.
482        Maintains .compiler.nodeStack.
483        """
484        require
485            .compiler
486            not .compiler.isBindingInh
487        body
488            v = .compiler.verbosity
489            v = 0
490            if v > 3
491                print
492            if v > 3 and _isBindingInt
493                print '<> currently in bind int. this = [this]'
494            if not _didBindInt and not _isBindingInt
495                _didStartBindInt = true
496                _isBindingInt = true
497                _stackPush
498                try
499                    _didBindIntBase = false
500                    if v > 3
501                        if .toString.startsWith('Class')
502                            print Environment.stackTrace
503                        print '<> will _bindInt on ' stop
504                        #.writeDeepString
505                    _bindInt
506                    assert _didBindIntBase
507                    _didBindInt = true
508                finally
509                    _finishChildReplacements
510                    _stackPop
511                    _isBindingInt = false
512                    if v > 3
513                        print
514                        print '<< did _bindInt() on [this]'
515            return this
516
517    def _bindInt
518        """
519        Bind interface elements such as return types and parameter types.
520        Override this method; do not send it. Invoke base.
521        """
522        require .didStartBindInt and .isBindingInt
523        _didBindIntBase = true
524
525    def computeMatchingBaseMembers
526        _stackPush
527        try
528            _computeMatchingBaseMembers
529        finally
530            _stackPop
531   
532    def _computeMatchingBaseMembers
533        pass
534   
535    def bindImp as dynamic  # CC: as same
536        """
537        Invoke this method to bind implemenation.
538        Subclasses typically override _bindImp, not this method.
539        However, if they do override this method, they should invoke base and return .bindImpResult.
540        Maintains .compiler.nodeStack.
541       
542        Supports node transformations. Consequently, if you cache an object var in a local (example: `expr = .expr`),
543        be sure to do so *after* invoking .bindImp on it, or to reassign the result of .bindImp to the local
544        (example: `expr = .expr.bindImp`).
545        """
546        require
547            .compiler
548            not .compiler.isBindingInh
549            not .compiler.isBindingInt or this inherits Expr  # TODO: lameness. the Expr escape clause is for box vars like "var _t = Dictionary<of int>()"
550        ensure
551            true # CC: result implements INode
552        body
553            if not _didBindImp and not _isBindingImp
554                _didStartBindImp = true
555                _isBindingImp = true
556                _superNode = if(.compiler.nodeStack.count > 0, .compiler.nodeStack.peek, _superNode ? nil)
557                _stackPush
558                try
559                    _didBindImpBase = false
560                    if .compiler.verbosity>3
561                        print '<> will _bindImp on ' stop
562                        .writeDeepString
563                    _bindImp
564                    assert _didBindImpBase
565                    _didBindImp = true
566                finally
567                    _finishChildReplacements
568                    _stackPop
569                    _isBindingImp = false
570            return .bindImpResult
571
572    def bindImpResult as dynamic is nonvirtual  # CC: as same
573        """
574        Returned by .bindImp. In support of node transformations.
575        """
576        return _transformedTo ? this
577   
578    def _bindImp
579        """
580        Bind implementation elements such as statements and expressions.
581        Override this method; do not send it. Invoke base.
582        """
583        require .didStartBindImp and .isBindingImp
584        _didBindImpBase = true
585
586    def bindAll as INode  # CC: as same
587        .bindInh
588        .bindInt
589        return .bindImp
590
591    get didStartBindInh from var
592
593    get didStartBindInt from var
594   
595    get didStartBindImp from var
596
597    get isBindingInh from var
598
599    get isBindingInt from var
600
601    get isBindingImp from var
602
603    get didBindInh from var
604
605    get didBindInt from var
606
607    get didBindImp from var
608
609    get didBindInhBase from var
610
611    get didBindIntBase from var
612   
613    get didBindImpBase from var
614
615
616    ##
617    ## Transforming
618    ##
619
620    get transformedTo from var
621
622    pro transformedFrom from var
623
624    shared
625        var _verboseTransform = false
626
627    def _transformTo(newNode as INode) is public
628        # Why is this method public? See the TODO in Box.symbolForName regarding accessing protected members.
629        # Expr.cobra(496): error: Cannot access protected member "Node._transformTo(INode)" via a qualifier of type "DotExpr"; the qualifier must be of type "CallExpr" (or derived from it) (C#)
630        if _verboseTransform
631            print 'transform from [this]'
632            print '            to [newNode]'
633        superNode = .superNode
634        if _verboseTransform
635            print '    superNode:', superNode
636        didReplace = superNode.replaceChild(this, newNode)
637        if newNode.superNode is nil or newNode.superNode is this
638            newNode.superNode = superNode
639        assert didReplace, 'superNode=[superNode], newNode=[newNode]'
640        _transformedTo = newNode
641        newNode.transformedFrom = this
642
643    def replaceChild(find as INode, replace as INode) as bool
644        """
645        Received by a node whose child node is being transformed.
646        The default implementation uses reflection of fields and properties to perform the replacement.
647        Nodes with other needs like examining unusual collections or requiring better performance can override this method.
648        See also: class SubnodesAttribute
649        """
650        # TODO: could use some caching on field and property info
651        for field in .getType.getFields(BindingFlags(Instance, NonPublic))
652            if field.fieldType is Node or field.fieldType.isSubclassOf(Node)
653                value = field.getValue(this)
654                if value is find
655                    if _verboseTransform, print '    replacing:', field
656                    try
657                        field.setValue(this, replace)
658                    catch argExc as ArgumentException
659                        throw ArgumentException('[argExc.message] field = "[field]", this type = "[.getType.name]"')
660                    didReplace = true
661        for prop in .getType.getProperties(BindingFlags(Instance, Public, NonPublic))
662            for attr in prop.getCustomAttributes(true)
663                if attr inherits SubnodesAttribute
664                    subnodes = prop.getValue(this, nil) to dynamic?
665                    if subnodes
666                        i = -1
667                        while true
668                            i = subnodes.indexOf(find, i+1)
669                            if i >= 0
670                                if _verboseTransform, print '    replacing [i] of [subnodes] in [prop.name]'
671                                # subnodes[i] = replace
672                                # .NET complains if you modify a collection while its enumerated (Mono does not), so
673                                if _willReplaceChild is nil, _willReplaceChild = []
674                                _willReplaceChild.add([subnodes, i, replace])
675                                didReplace = true
676                            else
677                                break
678/#
679                        I dont think this method needs to be recurse deeply into the nodes.
680                        But if it did, here is the code.
681                        i = 0
682                        count = subnodes.count
683                        while i < count
684                            subnode = subnodes[i]
685                            if subnode == find
686                                if _verboseTransform, print '    replacing [i] of [subnodes] in [prop.name]'
687                                # subnodes[i] = replace
688                                # .NET complains if you modify a collection while its enumerated (Mono does not), so
689                                if _willReplaceChild is nil, _willReplaceChild = []
690                                _willReplaceChild.add([subnodes, i, replace])
691                                didReplace = true
692                            else if subnode inherits INode
693                                if subnode.replaceChild(find, replace)
694                                    didReplace = true
695                            i += 1
696#/
697        return didReplace
698
699    def _finishChildReplacements
700        if _willReplaceChild
701            for pair in _willReplaceChild
702                # CC: subnodes, i, replace = pair
703                subnodes = pair[0]
704                i = pair[1] to int
705                replace = pair[2]
706                subnodes[i] = replace
707            _willReplaceChild = nil
708
709
710    ##
711    ## Errors
712    ##
713
714    var _errors as List<of NodeException>?
715
716    get hasError as bool
717        return _errors and _errors.count > 0
718
719    get errors from var
720
721    def throwError(ne as NodeException)
722        """
723        Subclasses should invoke this method whenever they detect an error during compilation
724        (bindInt and bindImp, but not code gen during which no user errors should occur).
725        The error will be recorded by the node, but not explicitly noted by the compiler.
726        Control flow does not return to the caller because the exception is thrown.
727        In practice, this exception will be caught at some level as part of error recovery.
728        """
729        require ne.node is this
730        _recordError(ne)
731        throw ne
732
733    def throwError(msg as String)
734        .throwError(NodeException(this, msg))
735
736    def recordError(ne as NodeException)
737        """
738        Record an error for this node with the given message.
739        The error will be noted by the compiler.
740        No exception is thrown and control returns to the caller.
741
742        Note that when catching errors from subobjects, the correct invocation is
743        `.compiler.recordError(error)` since the caught error would have already been
744        recorded by that object.
745        """
746        require
747            .compiler
748            ne.node is this
749        body
750            _recordError(ne)
751            .compiler.recordError(ne)
752
753    def recordError(msg as String)
754        .recordError(NodeException(this, msg))
755
756    def _recordError(ne as NodeException)
757        """
758        Records the error without notifying the compiler or throwing an exception--those are done
759        by the public `throwError` and `recordError` methods.
760        """
761        require ne.node is this
762        if _errors is nil
763            _errors = List<of NodeException>()
764        else
765            assert ne not in _errors
766        _errors.add(ne)
767
768
769    ##
770    ## Converting to string
771    ##
772
773    def toString as String is override
774        return .shallowString
775
776    def toTechString as String
777        return .shallowString
778
779    get idString as String
780        return '[.getType.name]([.serialNum])'
781
782    get minimalString as String
783        """
784        Includes only atomic data like numbers and strings.
785        """
786        return _toString(true, false, false)
787
788    get shallowString as String
789        """
790        Includes minimal string plus references to other objects (their minimal strings).
791        """
792        return _toString(true, true, false)
793
794    get deepString as String
795        """
796        Includes shallow string plus subobjects (their deep strings).
797        """
798        return _toString(true, true, true)
799
800    def addMinFields
801        """
802        Subclasses should override to add minimal fields.
803        """
804        if .isImplicit, .addField('isImplicit', .isImplicit)
805
806        if .didStartBindInh, .addField('didStartBindInh', .didStartBindInh)
807        if .isBindingInh, .addField('isBindingInh', .isBindingInh)
808        .addField('didBindInh', .didBindInh)
809
810        if .didStartBindInt, .addField('didStartBindInt', .didStartBindInt)
811        if .isBindingInt, .addField('isBindingInt', .isBindingInt)
812        .addField('didBindInt', .didBindInt)
813
814        if .didStartBindImp, .addField('didStartBindImp', .didStartBindImp)
815        if .isBindingImp, .addField('isBindingImp', .isBindingImp)
816        .addField('didBindImp', .didBindImp)
817
818    def addRefFields
819        pass
820
821    def addSubFields
822        pass
823
824    def addField(name as String, value as Object?)
825        """
826        Subclasses invoke this from their overrides of `addMinFields`,
827        `addRefFields` and `addSubFields`. It's okay for name to be blank.
828        """
829        __curFields.add(Field(name, value))
830
831    def addField(value as Object)
832        .addField('', value)
833
834    var __curFields as List<of Field>?
835
836    var __toStringSep as String?
837
838    def _toString(doMin as bool, doRef as bool, doSub as bool) as String
839        """
840        This is the implementation for minimalString, shallowString and deepString.
841        """
842        if doSub
843            tag = 'de' # for deep
844        else if doRef
845            tag = 'sh' # for shallow
846        else
847            tag = 'mi' # for minimal
848
849        sb = StringBuilder('[.getType.name]-[tag]([.serialNum]')
850        if __toStringSep is nil
851            __toStringSep = Environment.getEnvironmentVariable('COBRA_TECHSTRING_SEPARATOR') ? _
852                if(Environment.getEnvironmentVariable('COBRA_TECHSTRING_INDENT') not in [nil, ''], ',\n\t   ', ',')
853        sep = __toStringSep
854        if doMin
855            __curFields = List<of Field>()
856            try
857                .addMinFields
858                for field in __curFields
859                    if field.name.length
860                        sb.append('[sep] [field.name]=[field.value]')
861                    else
862                        sb.append('[sep] [field.value]')
863            finally
864                __curFields = nil
865
866        if doRef
867            __curFields = List<of Field>()
868            try
869                .addRefFields
870                for field in __curFields
871                    value as Object?
872                    if field.value inherits Node
873                        value = (field.value to Node).minimalString
874                    else
875                        value = field.value
876                    sb.append('[sep] [field.name]=[value]')
877            finally
878                __curFields = nil
879
880        if doSub
881            __curFields = List<of Field>()
882            try
883                .addSubFields
884                for field in __curFields
885                    value as Object?
886                    if field.value inherits Node
887                        value = (field.value to Node).minimalString
888                    else
889                        value = field.value
890                    sb.append('[sep] [field.name]=[field.value]')
891            finally
892                __curFields = nil
893
894        sb.append(')')
895        s = sb.toString
896        if s.length>100
897            s = s.substring(0, s.length-1) + ', [.serialNum])'
898
899        return s
900
901    def writeDeepString
902        .writeDeepString(IndentedWriter(Console.out))
903
904    def writeDeepString(iw as IndentedWriter)
905        _writeDeepString(iw, this, true, true, true)
906
907    def _writeDeepString(iw as IndentedWriter, obj as INode, doMin as bool, doRef as bool, doSub as bool)
908        if doSub
909            iw.write(obj.shallowString)
910            iw.write('\n')
911            iw.indent
912            try
913                __curFields = List<of Field>()
914                try
915                    .addSubFields
916                    for field in __curFields
917                        value = field.value
918                        if value inherits Node
919                            iw.write('[field.name]=')
920                            value._writeDeepString(iw, value, doMin, doRef, doSub)
921                            #iw.write('\n')
922                        else if value inherits System.Collections.IList
923                            iw.write('[field.name]=' + r'[')  # CC: should be able to say: '[name]=\[\n'
924                            iw.writeLine('')
925                            iw.indent
926                            try
927                                for i = 0 .. value.count
928                                    iw.write('[i]=')
929                                    if (node = value[i]) inherits Node
930                                        node._writeDeepString(iw, node, doMin, doRef, doSub)
931                                    else
932                                        # TODO: fix this to work for nested lists like those found in DictLit
933                                        iw.write(node.toString)
934                            finally
935                                iw.dedent
936                            iw.write(']\n')
937                        else
938                            iw.write('[field.name]=[value]\n')
939                finally
940                    __curFields = nil
941            finally
942                iw.dedent
943            return
944        if doRef
945            iw.write(obj.shallowString)
946            return
947        if doMin
948            iw.write(obj.minimalString)
949
950
951    ## Cloning
952   
953    def clone as dynamic
954        require not .didBindImp  # for now, not expecting a clone after binding implementation
955        ensure result is not nil
956        clone = .memberwiseClone to Stmt
957        clone._innerClone
958        return clone
959   
960    def _innerClone
961        pass
962       
963    def memberwiseClone as Object is protected
964        node = base.memberwiseClone to Node
965        node.setSerialNum(_nextNodeSerialNum)
966        _nextNodeSerialNum += 1
967        node._didStartBindInh = false
968        node._didStartBindInt = false
969        node._didStartBindImp = false
970        node._isBindingInh = false
971        node._isBindingInt = false
972        node._isBindingImp = false
973        return node
974
975
976interface ISyntaxNode inherits INode
977
978    get token as IToken
979    pro isHelpRequested as bool
980
981
982class SyntaxNode
983    is abstract, partial
984    inherits Node
985    implements ISyntaxNode
986    """
987    An abstract tree syntax node (AST).
988    Also: a node with a token.
989    Also: a node that came from parsing.
990
991    Descendants include NamedNode, Box (indirectly), Stmt and Expr.
992   
993    This is somewhat misleading as various descendant classes may be instantiated to represent what
994    is inside a DLL. Or down the road, another project might construct code programmatically.
995   
996    TODO: Rename to NodeWithToken or even just merge this up into Node.
997    """
998
999    var _token as IToken
1000
1001    cue init(token as IToken)
1002        base.init
1003        _token = token
1004
1005    cue init
1006        """
1007        If a node is not created from parsing, it will have no meaningful token. One example is
1008        creating nodes from reading DLLs. Another would be the CodeDOM implementation.
1009        """
1010        .init(TokenFix.empty)
1011
1012    get token from var
1013
1014    pro isHelpRequested from var as bool
1015
1016    get idString as String
1017        if .token.isEmpty
1018            return base.idString
1019        else
1020            return '[.getType.name]([.serialNum], [.token.shortLocationString])'
1021
1022    def addMinFields
1023        base.addMinFields
1024        .addField('token', .token.toTechString)
1025
1026    def throwError(msg as String) is override
1027        .throwError(SyntaxNodeException(this, msg))
1028
1029    def recordError(msg as String) is override
1030        .recordError(SyntaxNodeException(this, msg))
1031       
1032
1033interface INamedNode
1034    is partial
1035    inherits INode
1036    """
1037    A NamedNode is just a syntax node plus a name.
1038
1039    There is both a typeForIdentifier and typeForReceiver. That's
1040    because some NamedNodes like Class are a Type in the first case (`t
1041    = Console`) and themselves in the second case (`Console.out').
1042
1043    TODO: can typeForIdentifier and typeForReceiver be moved down to
1044    IMember?
1045    """
1046
1047    get name as String
1048
1049    get typeForIdentifier as IType
1050        """
1051        The type that should be used when an identifier refers to this
1052        NamedNode as in "c = Console". See IdentifierExpr.
1053        """
1054
1055    get typeForReceiver as IType
1056        """
1057        The type that should be used when a dotted expression refers to
1058        this NamedNode on the left side of the dot. See IdentifierExpr.
1059        """
1060
1061    get isMethod as bool
1062        """
1063        Returns true if the named node is effectively a method. You might think
1064        "obj inherits Method" would suffice, but MemberOverload may also return true.
1065        """
1066
1067    pro isUsed as bool
1068        """
1069        Defaults to false and is set to true when a node is used/read.
1070        This can then be inspected to generate warnings about unused declarations.
1071        """
1072
1073
1074interface INamedSyntaxNode
1075    inherits INamedNode, ISyntaxNode
1076
1077
1078class NamedNode
1079    is abstract, partial
1080    inherits SyntaxNode
1081    implements INamedNode
1082
1083#   invariant
1084#       .name.length
1085
1086    var _name as String
1087    var _isUsed as bool
1088
1089    cue init(token as IToken, name as String)
1090        require name.length
1091        base.init(token)
1092        _name = name
1093
1094    cue init(token as IToken)
1095        require token.value inherits String
1096        base.init(token)
1097        _name = token.value to String
1098
1099    cue init(name as String)
1100        base.init
1101        _name = name
1102
1103    get idString as String is override
1104        return '[.getType.name]([.serialNum], "[.name]")'
1105
1106    get name from var
1107
1108    def addMinFields
1109        .addField('name', .name)
1110        base.addMinFields
1111
1112    get typeForIdentifier as IType is abstract
1113
1114    get typeForReceiver as IType is abstract
1115
1116    get isMethod as bool
1117        return false
1118
1119    pro isUsed from var
1120
1121
1122class Field
1123
1124    cue init(name as String, value as Object?)
1125        base.init
1126        _name, _value = name, value
1127
1128    get name from var as String
1129
1130    get value from var as Object?
1131
1132
1133interface ICompilerForNodes inherits ITypeProvider, IWarningRecorder, IErrorRecorder
1134
1135    # TODO: Support `require` on `get` decl.
1136
1137
1138    ## Property queries
1139   
1140    pro options as OptionValues
1141
1142    def embedRunTimeSuffix as String  # TODO: make a 'get'?
1143
1144    get globalNS as NameSpace
1145
1146    def isBindingInh as bool
1147    def isBindingInt as bool
1148    def isBindingImp as bool
1149
1150    pro verbosity as int
1151
1152    get willTrackLocals as bool
1153        # the box stack can be empty because of assembly; has SomeAttr
1154
1155
1156    ## Options - TODO: move to OptionValues
1157   
1158    get includeTests as bool
1159    get hasDetailedStackTraceOption as bool
1160    get hasExceptionReportOption as bool
1161
1162
1163    ## Additional queries
1164   
1165    def findLocal(name as String) as AbstractLocalVar?
1166    def suggestionForUnknown(word as String) as String?
1167    def symbolForName(name as String, haveThis as bool) as IMember?
1168    def symbolForName(name as String, haveThis as bool, isLowerOkay as bool) as IMember?
1169
1170
1171    ## Current objects
1172
1173    get curModule as Module?
1174    get curNameSpace as NameSpace
1175    get curBox as Box
1176        # require .boxStack.count
1177    get curBoxMember as IBoxMember
1178        # require .boxMemberStack.count
1179    get curCodeMember as AbstractMethod
1180        # require .codeMemberStack.count
1181
1182
1183    ## Stacks and levels
1184   
1185    get nodeStack as Stack<of INode>
1186    get nameSpaceStack as Stack<of NameSpace>
1187    get boxStack as Stack<of Box>
1188    get boxMemberStack as Stack<of IBoxMember>
1189    get codeMemberStack as Stack<of AbstractMethod>
1190    pro refExprLevel as int
1191
1192
1193    ## Warnings, errors and corrections
1194   
1195    def correctSource(token as IToken, replace as String)
1196    def warning(node as ISyntaxNode, msg as String)  # TODO: move to IWarningRecorder
1197
1198    get errors as List<of SourceException>  # TODO: review the usages of this
1199
1200    def augmentWarning(node as ISyntaxNode, lookFor as String, search as String, augment as String) as bool
1201        require 
1202            lookFor.length
1203            augment.length
1204
1205
1206    ## More types
1207    # TODO: move up to ITypeProvider?
1208
1209    get primitiveToITypeCache as IDictionary<of dynamic, IType>?
1210
1211    get basicTypes as IList<of PrimitiveType>
1212
1213    get anyFloatType as AnyFloatType
1214    get anyIntType as AnyIntType
1215    def attributeType as Box
1216    def collectionType as Box
1217    def collectionOfType as Box
1218    def delegateType as Class
1219    def dictEnumeratorType as Box
1220    def dictionaryOfType as Class
1221    def enumerableType as Box
1222    def enumeratorType as Box
1223    def enumerableOfType as Box
1224    def enumeratorOfType as Box
1225    def exceptionType as Class
1226    def idictionaryType as Box
1227    def idictionaryOfType as Box
1228    def ilistType as Box
1229    def ilistOfType as Box
1230    def listOfType as Class
1231    def nilableDynamicType as NilableType
1232    def objectClass as Class
1233    def setOfType as Class
1234    def stringType as Class
1235
1236    # multiple backends
1237    get backEnd as BackEnd
1238    def objectTypeProxy as AbstractTypeProxy 
1239    def typeTypeProxy as AbstractTypeProxy 
1240    def installNativeMethods(box as Box, nativeType as NativeType)
1241
1242    ## Other
1243
1244    def addIntermediateFile(path as String)
1245    def loadReference(reference as String, addExtn as bool) as bool
Note: See TracBrowser for help on using the browser.