Wiki

root/cobra/trunk/Source/NameSpace.cobra

Revision 2638, 14.1 KB (checked in by Charles.Esterbrook, 4 weeks ago)

Add support for namespace doc strings.
ticket:277
credit:hopscc

  • Property svn:eol-style set to native
Line 
1interface INameSpaceMember inherits IMember, ISyntaxNode
2    """
3    Anything that can be a member of a namespace should implement INameSpaceMember.
4    This includes boxes, enums and namespaces.
5    """
6
7    def extensionMemberFor(box as Box, name as String) as IMember?
8    get isFromBinaryLibrary as bool
9    get isNames as String*
10
11
12class NameSpace
13    is partial
14    inherits Container<of INameSpaceMember>
15    implements IParentSpace, INameSpaceMember
16   
17    """
18    Two flavours of NameSpace
19        Unified -  holds decls + NameSpaces
20        Non-unified - holds decls + NameSpaces and list of Use Directives done in the namespace.
21                    - points to its corresponding unified namespace
22   
23        (Most) modules have a topLevel namespace that holds the modules contents
24        The compiler holds a global namespace for the program containing all top level namespaces and (system) namespaces referenced.
25    """
26   
27    var _unifiedNameSpace as NameSpace?
28    var _superNameSpace as NameSpace?
29    var _subNameSpacesByName as Dictionary<of String, NameSpace>
30    var _subNameSpacesList as List<of NameSpace>
31    var _useDirectives as List<of UseDirective>
32
33    cue init(unifiedNameSpace as NameSpace, name as String)
34        .init(TokenFix.empty, name)
35        _unifiedNameSpace = unifiedNameSpace
36        _useDirectives = List<of UseDirective>()
37
38    cue init(token as IToken, name as String)
39        .init(token, name, nil, nil)
40
41    cue init(token as IToken, name as String, superNameSpace as NameSpace?)
42        .init(token, name, superNameSpace, nil)
43
44    cue init(token as IToken, name as String, superNameSpace as NameSpace?, docString as String?)
45        base.init(token, name, List<of String>(), docString)
46        _superNameSpace = superNameSpace
47        _subNameSpacesByName = Dictionary<of String, NameSpace>()
48        _subNameSpacesList = List<of NameSpace>()
49        _useDirectives = List<of UseDirective>()
50
51    get canDeclNamesDifferOnlyByCase as bool is override
52        """
53        Returns true. While Cobra normally disallows this, assemblies created in other languages may have this characteristic. Also,
54        Cobra normally disallows declarations that differ only in their case.
55        This works fine for source code, but not for reading assemblies.
56        """
57        return true
58
59    get englishName as String is override
60        return 'namespace'
61
62    def isDescendantOf(type as IType) as bool is override
63        or require true
64        return this is type
65
66    get isFromBinaryLibrary as bool
67        # this is undetermined as namespaces can be spread across binaries and source
68        # but for our purposes, returning false is fine
69        # (see _checkForDeclarationsThatDifferOnlyByCase where this method is invoked)
70        return false
71
72    get isGlobal as bool
73        return _superNameSpace is nil and _name=='(global)'
74
75    get isReference as bool is override  # TODO: remove when not a type
76        assert false
77        return true
78
79    get defaultAccessLevel as String is override  # TODO: remove when not a type
80        assert false
81        return ''
82
83    get isRoot as bool
84        """
85        Returns true if this is a root namespace with no parent namespace.
86        All module top-level namespaces are root as well as their common, unified namespace, the global namespace.
87        """
88        return _superNameSpace is nil
89       
90    get isUnified as bool
91        return _unifiedNameSpace is nil
92
93    get superNameSpace from var
94
95    get unifiedNameSpace as NameSpace?
96        ensure result is nil or result.isUnified
97        return var
98
99    get useDirectives as UseDirective*
100        return _useDirectives
101
102    def addMinFields
103        base.addMinFields
104        .addField('subCount', _subNameSpacesList.count)
105        .addField('isUnified', .isUnified)
106        .addField('didBindUse', .didBindUse)
107
108    def addRefFields
109        base.addRefFields
110        .addField('superNameSpace', _superNameSpace)
111        .addField('unifiedNameSpace', _unifiedNameSpace)
112
113    var _fullName as String? # TODO: put in .fullName property when that is supported
114
115    get fullName as String
116        ensure
117            not .isRoot implies .name in result
118            not result.startsWith('.')
119            not result.endsWith('.')
120        body
121            if _superNameSpace is nil
122                return 'global'
123            if not _fullName
124                t = List<of String>()
125                ns = this to ?
126                while ns and not ns.isRoot
127                    t.add(ns.name)
128                    ns = ns._superNameSpace
129                t.reverse
130                _fullName = t.join('.')
131            return _fullName to !
132
133    def getOrMakeNameSpaceNamed(token as IToken, name as String) as NameSpace  # CC: same
134        """
135        Returns the existing sub-namespace with the given name or creates it.
136        Raises ValueError if there is a declaration in the namespace with the given name that is not a namespace.
137        """
138        ensure result.superNameSpace is this
139        # TODO: enforce that case must match and 2 decls cannot be the same but for case. You cannot have a namespace FooBar and class Foobar under the same namespace
140        if _subNameSpacesByName.containsKey(name)
141            ns = _subNameSpacesByName[name]
142        else
143            if _declsByName.containsKey(name)
144                return .backEnd.handleNameSpaceNameCollision(this, token, name)
145            ns = NameSpace(token, name, this)
146            if not .isUnified
147                # mirror in the unified namespace
148                uns = _unifiedNameSpace.getOrMakeNameSpaceNamed(token, name)
149                assert uns.isUnified
150                ns._unifiedNameSpace = uns
151            .addDecl(ns)
152        return ns
153
154    def addDocString(docString as String?)
155        doc = (docString ? '').trim
156        if not doc.length, return
157        if not .docString.length
158            .docString = doc
159        else if .docString <> doc
160            sb = StringBuilder(.docString)
161            sb.append('\n')
162            sb.append(doc)
163            .docString = sb.toString
164
165    get typeForIdentifier as IType? is override
166        # TODO: in reality, you can't say "x = SomeNameSpace" or pass a
167        # namespace as an argument. That should be fixed by allowing those
168        # things perhaps as some form of reflection where namespaces
169        # become runtime objects. Or this property should return a new
170        # .compiler.invalidType which then triggers an error if you try to
171        # use it.
172        return this
173
174    get typeForReceiver as IType? is override
175        return this
176
177    def adoptDefaultNameSpace as NameSpace
178        # support -namespace option
179        if .isRoot and .declForName('Cobra') is nil
180            # the test for 'Cobra' above is needed to support -embed-run-time:yes which has files with 'namespace Cobra.Lang'
181            ns = .compiler.options.getDefault('namespace', '') to String
182            if ns <> ''
183                newTop = NameSpace(.unifiedNameSpace, .name)
184                _name = ns
185                _unifiedNameSpace = .compiler.globalNS.getOrMakeNameSpaceNamed(.token.copy(.token.which, ns), ns)
186                assert _unifiedNameSpace.isUnified
187                for decl in .declsInOrder
188                    if not decl inherits NameSpace
189                        _unifiedNameSpace.addDecl(decl)
190                _superNameSpace = newTop
191                newTop.addDecl(this)
192                return newTop
193        return this
194
195    def _stackPush
196        base._stackPush
197        .compiler.nameSpaceStack.push(this)
198
199    def _stackPop
200        base._stackPop
201        .compiler.nameSpaceStack.pop
202       
203    def _bindInh is override
204        base._bindInh
205        assert .didBindUse
206        _checkCase
207        _checkForDeclarationsThatDifferOnlyByCase
208        for decl in _declsInOrder, decl.bindInh
209        for ns in _subNameSpacesList, ns.bindInh
210
211    def _checkCase
212        if .name.startsWithLowerLetter and not .token.isEmpty
213            # TODO: make this an error eventually. warning added 2009-01-04
214            .compiler.warning(this, 'Namespace names should start with an uppercase letter in order to avoid collisions with other identifiers such as arguments and local variables.')
215
216    def _checkForDeclarationsThatDifferOnlyByCase
217        if _declsByNameCI.count < _declsByName.count
218            nameList = for d in _declsInOrder get d.name
219            remainingNames = Set<of String>(nameList)
220            for name in nameList
221                if name not in remainingNames, continue
222                lowerName = name.toLower
223                sameNames = for n in remainingNames where n.toLower == lowerName
224                if sameNames.count > 1
225                    sameNames.sort
226                    for n in sameNames, remainingNames.remove(n)
227                    # at this point we have, for example, ['Foo', 'foo', 'FOO']
228                   
229                    namesMsgPart = (for n in sameNames get '"[n]"').join(' ') + '.'
230                    if all for n in sameNames get _declsByName[n] inherits NameSpace
231                        # namespaces can be spread across DLLs and source files in any combination, so they only get a warning
232                        .compiler.warning(this, 'Multiple namespaces in "[.fullName]" differ only by case: ' + namesMsgPart)
233                    else
234                        # if any of the decls comes from source then it's an error
235                        # but if they all come from binaries, the user may not be able to correct it
236                        msg = 'Multiple declarations in "[.fullName]" differ only by case: ' + namesMsgPart
237                        anySource = any for n in sameNames get not _declsByName[n].isFromBinaryLibrary
238                        if anySource, .recordError(msg)
239                        else, .compiler.warning(this, msg)
240
241    def _bindInt
242        base._bindInt
243        assert .didBindInh
244        for ud in _useDirectives
245            ud.bindInt  # not logically needed (bindUse takes care of use directives), but nodes like to get at least one of .bindInt or .bindImp
246        for decl in _declsInOrder
247            decl.bindInt
248
249    def _computeMatchingBaseMembers
250        base._computeMatchingBaseMembers
251        for decl in .declsInOrder
252            decl.computeMatchingBaseMembers
253
254    def _bindImp
255        base._bindImp
256        for decl in _declsInOrder
257            decl.bindImp
258
259    var _cacheForExtensionMembers = Dictionary<of String, IMember?>()
260
261    def extensionMemberFor(box as Box, name as String) as IMember?
262        key = box.serialNum.toString + name  # names can't start with a digit, so no separator is needed
263        cacheHit as IMember?
264        if _cacheForExtensionMembers.tryGetValue(key, out cacheHit), return cacheHit
265        m = _extensionMemberFor(box, name)
266        _cacheForExtensionMembers[key] = m
267        return m
268
269    def _extensionMemberFor(box as Box, name as String) as IMember?
270        # TODO: pick *closest* extension member
271        if not .isUnified
272            m = _unifiedNameSpace.extensionMemberFor(box, name)
273            if m, return m
274            return _extensionMemberFromUseDirectives(box, name)
275        else
276            # our decl?
277            for decl in .declsInOrder
278                if decl inherits Box
279                    m = decl.extensionMemberFor(box, name)
280                    if m, return m
281            if _superNameSpace
282                m = _superNameSpace.extensionMemberFor(box, name)
283                if m, return m
284        return nil
285
286    def _extensionMemberFromUseDirectives(box as Box, name as String) as IMember?
287        for ud in _useDirectives
288            m = ud.extensionMemberFor(box, name)
289            if m, return m
290        return if(_superNameSpace, _superNameSpace._extensionMemberFromUseDirectives(box, name), nil)
291       
292    def symbolForName(name as String) as IMember?
293        if not .isUnified
294            x = _unifiedNameSpace.symbolForName(name)
295            if x, return x
296            # inside a use directive?
297            members = Set<of IMember>()
298            _symbolsForNameFromUseDirectives(name, members)
299            if members.count == 1
300                return List<of IMember>(members)[0]  # TODO: feels silly. maybe Set should have a .only method with require .count==1
301            else if members.count > 1
302                membersList = List<of IMember>(members)
303                membersList.sort(do(a as IMember, b as IMember))
304                    return a.parentNameSpace.fullName.compareTo(b.parentNameSpace.fullName)
305                spaces = (for m in membersList get '"[m.parentNameSpace.fullName]"').join(', ', ' and ')
306                if .compiler and .compiler.nodeStack.peek inherits ISyntaxNode
307                    node = .compiler.nodeStack.peek
308                else
309                    node = this
310                node.throwError('Ambiguous reference "[name]" found in namespaces [spaces].')
311        else
312            # our decl?
313            # TODO: should this come before checking our name? what does C# do?
314            x = .declForName(name)
315            if x, return x
316/#
317            # wrong. see ticket:128
318            # a parent namespace?
319            if _superNameSpace
320                x = _superNameSpace.symbolForName(name)
321                if x, return x
322#/
323        return nil
324
325    def _symbolsForNameFromUseDirectives(name as String, members as Set<of IMember>)
326        """
327        Populates `members` with all symbols from `use` directives that match `name`.
328        """
329        for ud in _useDirectives
330            x = ud.findSymbol(name)
331            if x, members.add(x)
332        if _superNameSpace
333            _superNameSpace._symbolsForNameFromUseDirectives(name, members)
334
335    def addDecl(decl as INameSpaceMember) is override
336        base.addDecl(decl)
337        if decl inherits NameSpace
338            assert decl.superNameSpace is this
339            assert .isUnified == decl.isUnified
340            _subNameSpacesByName.add(decl.name, decl# TODO: add some require and ensure around the subNameSpaces
341            _subNameSpacesList.add(decl)
342        if decl inherits NameSpace
343            assert decl.parentNameSpace is this or decl.parentNameSpace is nil
344            decl.parentNameSpace = this
345        else
346            if decl.parentNameSpace
347                assert .isUnified
348                assert decl.parentNameSpace.unifiedNameSpace is this
349            else
350                assert not .isUnified
351                decl.parentNameSpace = this
352        if not .isUnified
353            # mirror the declarations in the unified namespace
354            if decl inherits NameSpace
355                pass
356            else
357                _unifiedNameSpace.addDecl(decl)
358
359    def addUseDirective(ud as UseDirective)
360        _useDirectives.add(ud)
361
362    def subNameSpaceForName(name as String) as NameSpace?
363        require name.length
364        if _subNameSpacesByName.containsKey(name)
365            return _subNameSpacesByName[name]
366        else
367            return nil
368
369    def unifyInto(newGlobal as NameSpace)
370        """
371        This is built for the very specific purpose of caching modules during "testify" (see CommandLine.cobra and Compiler.init).
372        """
373        require
374            newGlobal.isGlobal
375            newGlobal is not this
376            not .isUnified
377            newGlobal.isUnified
378        body
379            _unifyInto(newGlobal)
380           
381    def _unifyInto(uni as NameSpace)
382        require
383            uni.isUnified
384            not .isUnified
385        body
386            _unifiedNameSpace = uni
387            for decl in .declsInOrder
388                if decl inherits NameSpace
389                    newUniForDecl = uni.getOrMakeNameSpaceNamed(decl.token, decl.name)
390                    decl._unifyInto(newUniForDecl)
391                else
392                    uni.addDecl(decl)
393
394
395class UseDirective
396    is partial
397    inherits SyntaxNode
398
399    var _nameParts as List<of String>
400    var _boundNameSpace as NameSpace?
401    var _fullName as String
402    var _fileName as String
403    var _explicitUses as bool
404
405    cue init(token as IToken, nameParts as List<of String>)
406        .init(token, nameParts, '', false)
407
408    cue init(token as IToken, nameParts as List<of String>, fileName as String?)
409        .init(token, nameParts, fileName, false)
410
411    cue init(token as IToken, nameParts as List<of String>, fileName as String?, explicitUses as bool)
412        base.init(token)
413        _nameParts = nameParts
414        _fullName = nameParts.join('.')
415        _fileName = fileName ? ''
416        _explicitUses = explicitUses
417
418    def addMinFields
419        base.addMinFields
420        .addField('fullName', _fullName)
421        .addField('didBindUse', .didBindUse)
422        .addField('fileName', _fileName)
423
424    def addRefFields
425        base.addRefFields
426        .addField('boundNameSpace', _boundNameSpace)
427
428    get boundNameSpace from var
429
430    def extensionMemberFor(box as Box, name as String) as IMember?
431        require .didBindUse
432        return _boundNameSpace.extensionMemberFor(box, name)
433
434    def findSymbol(name as String) as IMember?
435        require .didBindUse
436        return _boundNameSpace.symbolForName(name)
Note: See TracBrowser for help on using the browser.