interface INameSpaceMember inherits IMember, ISyntaxNode """ Anything that can be a member of a namespace should implement INameSpaceMember. This includes boxes, enums and namespaces. """ def extensionMemberFor(box as Box, name as String) as IMember? get isFromBinaryLibrary as bool get isNames as String* class NameSpace is partial inherits Container implements IParentSpace, INameSpaceMember """ Two flavours of NameSpace Unified - holds decls + NameSpaces Non-unified - holds decls + NameSpaces and list of Use Directives done in the namespace. - points to its corresponding unified namespace (Most) modules have a topLevel namespace that holds the modules contents The compiler holds a global namespace for the program containing all top level namespaces and (system) namespaces referenced. """ var _unifiedNameSpace as NameSpace? var _superNameSpace as NameSpace? var _subNameSpacesByName as Dictionary var _subNameSpacesList as List var _useDirectives as List cue init(unifiedNameSpace as NameSpace, name as String) .init(TokenFix.empty, name) _unifiedNameSpace = unifiedNameSpace _useDirectives = List() cue init(token as IToken, name as String) .init(token, name, nil, nil) cue init(token as IToken, name as String, superNameSpace as NameSpace?) .init(token, name, superNameSpace, nil) cue init(token as IToken, name as String, superNameSpace as NameSpace?, docString as String?) base.init(token, name, List(), docString) _superNameSpace = superNameSpace _subNameSpacesByName = Dictionary() _subNameSpacesList = List() _useDirectives = List() get canDeclNamesDifferOnlyByCase as bool is override """ Returns true. While Cobra normally disallows this, assemblies created in other languages may have this characteristic. Also, Cobra normally disallows declarations that differ only in their case. This works fine for source code, but not for reading assemblies. """ return true get englishName as String is override return 'namespace' def isDescendantOf(type as IType) as bool is override or require true return this is type get isFromBinaryLibrary as bool # this is undetermined as namespaces can be spread across binaries and source # but for our purposes, returning false is fine # (see _checkForDeclarationsThatDifferOnlyByCase where this method is invoked) return false get isGlobal as bool return _superNameSpace is nil and _name=='(global)' get isReference as bool is override # TODO: remove when not a type assert false return true get defaultAccessLevel as String is override # TODO: remove when not a type assert false return '' get isRoot as bool """ Returns true if this is a root namespace with no parent namespace. All module top-level namespaces are root as well as their common, unified namespace, the global namespace. """ return _superNameSpace is nil get isUnified as bool return _unifiedNameSpace is nil get superNameSpace from var get unifiedNameSpace as NameSpace? ensure result is nil or result.isUnified return var get useDirectives as UseDirective* return _useDirectives def addMinFields base.addMinFields .addField('subCount', _subNameSpacesList.count) .addField('isUnified', .isUnified) .addField('didBindUse', .didBindUse) def addRefFields base.addRefFields .addField('superNameSpace', _superNameSpace) .addField('unifiedNameSpace', _unifiedNameSpace) var _fullName as String? # TODO: put in .fullName property when that is supported get fullName as String ensure not .isRoot implies .name in result not result.startsWith('.') not result.endsWith('.') body if _superNameSpace is nil return 'global' if not _fullName t = List() ns = this to ? while ns and not ns.isRoot t.add(ns.name) ns = ns._superNameSpace t.reverse _fullName = t.join('.') return _fullName to ! def getOrMakeNameSpaceNamed(token as IToken, name as String) as NameSpace # CC: same """ Returns the existing sub-namespace with the given name or creates it. Raises ValueError if there is a declaration in the namespace with the given name that is not a namespace. """ ensure result.superNameSpace is this # 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 if _subNameSpacesByName.containsKey(name) ns = _subNameSpacesByName[name] else if _declsByName.containsKey(name) return .backEnd.handleNameSpaceNameCollision(this, token, name) ns = NameSpace(token, name, this) if not .isUnified # mirror in the unified namespace uns = _unifiedNameSpace.getOrMakeNameSpaceNamed(token, name) assert uns.isUnified ns._unifiedNameSpace = uns .addDecl(ns) return ns def addDocString(docString as String?) doc = (docString ? '').trim if not doc.length, return if not .docString.length .docString = doc else if .docString <> doc sb = StringBuilder(.docString) sb.append('\n') sb.append(doc) .docString = sb.toString get typeForIdentifier as IType? is override # TODO: in reality, you can't say "x = SomeNameSpace" or pass a # namespace as an argument. That should be fixed by allowing those # things perhaps as some form of reflection where namespaces # become runtime objects. Or this property should return a new # .compiler.invalidType which then triggers an error if you try to # use it. return this get typeForReceiver as IType? is override return this def adoptDefaultNameSpace as NameSpace # support -namespace option if .isRoot and .declForName('Cobra') is nil # the test for 'Cobra' above is needed to support -embed-run-time:yes which has files with 'namespace Cobra.Lang' ns = .compiler.options.getDefault('namespace', '') to String if ns <> '' newTop = NameSpace(.unifiedNameSpace, .name) _name = ns _unifiedNameSpace = .compiler.globalNS.getOrMakeNameSpaceNamed(.token.copy(.token.which, ns), ns) assert _unifiedNameSpace.isUnified for decl in .declsInOrder if not decl inherits NameSpace _unifiedNameSpace.addDecl(decl) _superNameSpace = newTop newTop.addDecl(this) return newTop return this def _stackPush base._stackPush .compiler.nameSpaceStack.push(this) def _stackPop base._stackPop .compiler.nameSpaceStack.pop def _bindInh is override base._bindInh assert .didBindUse _checkCase _checkForDeclarationsThatDifferOnlyByCase for decl in _declsInOrder, decl.bindInh for ns in _subNameSpacesList, ns.bindInh def _checkCase if .name.startsWithLowerLetter and not .token.isEmpty # TODO: make this an error eventually. warning added 2009-01-04 .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.') def _checkForDeclarationsThatDifferOnlyByCase if _declsByNameCI.count < _declsByName.count nameList = for d in _declsInOrder get d.name remainingNames = Set(nameList) for name in nameList if name not in remainingNames, continue lowerName = name.toLower sameNames = for n in remainingNames where n.toLower == lowerName if sameNames.count > 1 sameNames.sort for n in sameNames, remainingNames.remove(n) # at this point we have, for example, ['Foo', 'foo', 'FOO'] namesMsgPart = (for n in sameNames get '"[n]"').join(' ') + '.' if all for n in sameNames get _declsByName[n] inherits NameSpace # namespaces can be spread across DLLs and source files in any combination, so they only get a warning .compiler.warning(this, 'Multiple namespaces in "[.fullName]" differ only by case: ' + namesMsgPart) else # if any of the decls comes from source then it's an error # but if they all come from binaries, the user may not be able to correct it msg = 'Multiple declarations in "[.fullName]" differ only by case: ' + namesMsgPart anySource = any for n in sameNames get not _declsByName[n].isFromBinaryLibrary if anySource, .recordError(msg) else, .compiler.warning(this, msg) def _bindInt base._bindInt assert .didBindInh for ud in _useDirectives ud.bindInt # not logically needed (bindUse takes care of use directives), but nodes like to get at least one of .bindInt or .bindImp for decl in _declsInOrder decl.bindInt def _computeMatchingBaseMembers base._computeMatchingBaseMembers for decl in .declsInOrder decl.computeMatchingBaseMembers def _bindImp base._bindImp for decl in _declsInOrder decl.bindImp var _cacheForExtensionMembers = Dictionary() def extensionMemberFor(box as Box, name as String) as IMember? key = box.serialNum.toString + name # names can't start with a digit, so no separator is needed cacheHit as IMember? if _cacheForExtensionMembers.tryGetValue(key, out cacheHit), return cacheHit m = _extensionMemberFor(box, name) _cacheForExtensionMembers[key] = m return m def _extensionMemberFor(box as Box, name as String) as IMember? # TODO: pick *closest* extension member if not .isUnified m = _unifiedNameSpace.extensionMemberFor(box, name) if m, return m return _extensionMemberFromUseDirectives(box, name) else # our decl? for decl in .declsInOrder if decl inherits Box m = decl.extensionMemberFor(box, name) if m, return m if _superNameSpace m = _superNameSpace.extensionMemberFor(box, name) if m, return m return nil def _extensionMemberFromUseDirectives(box as Box, name as String) as IMember? for ud in _useDirectives m = ud.extensionMemberFor(box, name) if m, return m return if(_superNameSpace, _superNameSpace._extensionMemberFromUseDirectives(box, name), nil) def symbolForName(name as String) as IMember? if not .isUnified x = _unifiedNameSpace.symbolForName(name) if x, return x # inside a use directive? members = Set() _symbolsForNameFromUseDirectives(name, members) if members.count == 1 return List(members)[0] # TODO: feels silly. maybe Set should have a .only method with require .count==1 else if members.count > 1 membersList = List(members) membersList.sort(do(a as IMember, b as IMember)) return a.parentNameSpace.fullName.compareTo(b.parentNameSpace.fullName) spaces = (for m in membersList get '"[m.parentNameSpace.fullName]"').join(', ', ' and ') if .compiler and .compiler.nodeStack.peek inherits ISyntaxNode node = .compiler.nodeStack.peek else node = this node.throwError('Ambiguous reference "[name]" found in namespaces [spaces].') else # our decl? # TODO: should this come before checking our name? what does C# do? x = .declForName(name) if x, return x /# # wrong. see ticket:128 # a parent namespace? if _superNameSpace x = _superNameSpace.symbolForName(name) if x, return x #/ return nil def _symbolsForNameFromUseDirectives(name as String, members as Set) """ Populates `members` with all symbols from `use` directives that match `name`. """ for ud in _useDirectives x = ud.findSymbol(name) if x, members.add(x) if _superNameSpace _superNameSpace._symbolsForNameFromUseDirectives(name, members) def addDecl(decl as INameSpaceMember) is override base.addDecl(decl) if decl inherits NameSpace assert decl.superNameSpace is this assert .isUnified == decl.isUnified _subNameSpacesByName.add(decl.name, decl) # TODO: add some require and ensure around the subNameSpaces _subNameSpacesList.add(decl) if decl inherits NameSpace assert decl.parentNameSpace is this or decl.parentNameSpace is nil decl.parentNameSpace = this else if decl.parentNameSpace assert .isUnified assert decl.parentNameSpace.unifiedNameSpace is this else assert not .isUnified decl.parentNameSpace = this if not .isUnified # mirror the declarations in the unified namespace if decl inherits NameSpace pass else _unifiedNameSpace.addDecl(decl) def addUseDirective(ud as UseDirective) _useDirectives.add(ud) def subNameSpaceForName(name as String) as NameSpace? require name.length if _subNameSpacesByName.containsKey(name) return _subNameSpacesByName[name] else return nil def unifyInto(newGlobal as NameSpace) """ This is built for the very specific purpose of caching modules during "testify" (see CommandLine.cobra and Compiler.init). """ require newGlobal.isGlobal newGlobal is not this not .isUnified newGlobal.isUnified body _unifyInto(newGlobal) def _unifyInto(uni as NameSpace) require uni.isUnified not .isUnified body _unifiedNameSpace = uni for decl in .declsInOrder if decl inherits NameSpace newUniForDecl = uni.getOrMakeNameSpaceNamed(decl.token, decl.name) decl._unifyInto(newUniForDecl) else uni.addDecl(decl) class UseDirective is partial inherits SyntaxNode var _nameParts as List var _boundNameSpace as NameSpace? var _fullName as String var _fileName as String var _explicitUses as bool cue init(token as IToken, nameParts as List) .init(token, nameParts, '', false) cue init(token as IToken, nameParts as List, fileName as String?) .init(token, nameParts, fileName, false) cue init(token as IToken, nameParts as List, fileName as String?, explicitUses as bool) base.init(token) _nameParts = nameParts _fullName = nameParts.join('.') _fileName = fileName ? '' _explicitUses = explicitUses def addMinFields base.addMinFields .addField('fullName', _fullName) .addField('didBindUse', .didBindUse) .addField('fileName', _fileName) def addRefFields base.addRefFields .addField('boundNameSpace', _boundNameSpace) get boundNameSpace from var def extensionMemberFor(box as Box, name as String) as IMember? require .didBindUse return _boundNameSpace.extensionMemberFor(box, name) def findSymbol(name as String) as IMember? require .didBindUse return _boundNameSpace.symbolForName(name)