| 1 | interface IContainer |
|---|
| 2 | inherits IMember |
|---|
| 3 | """ |
|---|
| 4 | Containers are named nodes that respond to .declForName. |
|---|
| 5 | Containers include: |
|---|
| 6 | * NameSpace |
|---|
| 7 | * Box |
|---|
| 8 | * Class |
|---|
| 9 | * Struct |
|---|
| 10 | * Interface |
|---|
| 11 | * Enum |
|---|
| 12 | """ |
|---|
| 13 | def declForName(name as String) as IMember? |
|---|
| 14 | """ |
|---|
| 15 | Returns the member declared in this container with the given name. |
|---|
| 16 | May return nil if no such member exists. |
|---|
| 17 | Does not follow inheritance chains. |
|---|
| 18 | Case-sensitive. |
|---|
| 19 | """ |
|---|
| 20 | |
|---|
| 21 | def memberForName(name as String) as IMember? |
|---|
| 22 | """ |
|---|
| 23 | Returns the member of this container with the given name, including any inherited members. |
|---|
| 24 | Since Object is the root of even primitive types and enumerations, then this method may return a Method such as .toString or .getHashCode. |
|---|
| 25 | May return nil if no such member exists. |
|---|
| 26 | Case-sensitive. |
|---|
| 27 | """ |
|---|
| 28 | # TODO: |
|---|
| 29 | # require |
|---|
| 30 | # # TODO: not .compiler.isParsing |
|---|
| 31 | # not .compiler.isBindingInh |
|---|
| 32 | |
|---|
| 33 | |
|---|
| 34 | interface IParentSpace |
|---|
| 35 | is partial |
|---|
| 36 | inherits IContainer |
|---|
| 37 | """ |
|---|
| 38 | Basically namespaces and boxes are "parent spaces" meaning they can contain others. |
|---|
| 39 | For example, any box can be contained by a namespace. And its anticipated that in the future, Cobra will support nested boxes. |
|---|
| 40 | Any namespace can be contained by another namespace. |
|---|
| 41 | But this is not a perfect model of how things work since a NameSpace cannot have a Box as a parent. |
|---|
| 42 | """ |
|---|
| 43 | |
|---|
| 44 | pass |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | class Container<of TMember> |
|---|
| 48 | is abstract, partial |
|---|
| 49 | where TMember must be class, IMember |
|---|
| 50 | inherits NamedNode |
|---|
| 51 | implements IContainer, IType |
|---|
| 52 | """ |
|---|
| 53 | A container of declarations. |
|---|
| 54 | |
|---|
| 55 | A container has (optionally) a parent namespace or box, declarations preserved in order and |
|---|
| 56 | indexed by name, a doc string and "is names". |
|---|
| 57 | |
|---|
| 58 | The declForName[CI] methods do not follow inheritance, but |
|---|
| 59 | .memberForName should (subclasses have to override). |
|---|
| 60 | |
|---|
| 61 | IMember is implemented (brought in via IContainer). |
|---|
| 62 | |
|---|
| 63 | Subclasses are responsible for passing bindInt and bindImp to _declsInOrder. TODO: should that be the case |
|---|
| 64 | |
|---|
| 65 | TODO: Should Container inherit from CobraType? I guess the problem is that NamedNode is a |
|---|
| 66 | SyntaxNode and CobraType inherits from Node. However, IType does specify a name... Maybe |
|---|
| 67 | NamedNode should inherit Node and the token thing done via ISyntaxNode only. At the very |
|---|
| 68 | least, .greatestCommonDenominatorWith is duplicated. Others may be as well. |
|---|
| 69 | |
|---|
| 70 | More thoughts on this. NameSpace inherits from Container, but NameSpace is *not* a type. |
|---|
| 71 | Also, there are several members here that are duplicated from CobraType. |
|---|
| 72 | I wish .NET had natural support for multiple inheritance |
|---|
| 73 | So my current thoughts: |
|---|
| 74 | Container should not implement IType |
|---|
| 75 | Box should implement IType |
|---|
| 76 | Live with some duplication for now |
|---|
| 77 | Later, move duplicated members to the IType interface when Cobra supports interface code |
|---|
| 78 | """ |
|---|
| 79 | |
|---|
| 80 | var _parentNameSpace as NameSpace? |
|---|
| 81 | var _parentBox as Box? |
|---|
| 82 | |
|---|
| 83 | var _declsInOrder as List<of TMember> |
|---|
| 84 | var _declsByName as Dictionary<of String, TMember> |
|---|
| 85 | var _declsByNameCI as Dictionary<of String, TMember> |
|---|
| 86 | var _docString as String |
|---|
| 87 | var _isNames as IList<of String> |
|---|
| 88 | |
|---|
| 89 | cue init(token as IToken, name as String, isNames as String*, docString as String?) |
|---|
| 90 | .init(nil, token, name, isNames, docString) |
|---|
| 91 | |
|---|
| 92 | cue init(parent as IParentSpace?, token as IToken, name as String, isNames as String*, docString as String?) |
|---|
| 93 | base.init(token, name) |
|---|
| 94 | _initParent(parent) |
|---|
| 95 | _declsInOrder = List<of TMember>() |
|---|
| 96 | _declsByName = Dictionary<of String, TMember>() |
|---|
| 97 | _declsByNameCI = Dictionary<of String, TMember>() |
|---|
| 98 | _isNames = isNames.toList |
|---|
| 99 | _docString = docString ? '' |
|---|
| 100 | |
|---|
| 101 | def _initParent(parent as IParentSpace?) |
|---|
| 102 | if parent |
|---|
| 103 | if parent inherits NameSpace |
|---|
| 104 | # _parentNameSpace = parent |
|---|
| 105 | # don't set the parentNameSpace because NameSpace.addDecl does that *and* there it will substitute its .unifiedNameSpace |
|---|
| 106 | pass |
|---|
| 107 | else if parent inherits Box |
|---|
| 108 | _parentBox = parent |
|---|
| 109 | else |
|---|
| 110 | throw FallThroughException(parent) |
|---|
| 111 | |
|---|
| 112 | get attributes as AttributeList |
|---|
| 113 | return AttributeList() |
|---|
| 114 | |
|---|
| 115 | get canDeclNamesDifferOnlyByCase as bool |
|---|
| 116 | """ |
|---|
| 117 | Cobra normally disallows declarations that differ only in their case. |
|---|
| 118 | This works fine for source code, but not for reading assemblies. |
|---|
| 119 | """ |
|---|
| 120 | return false |
|---|
| 121 | |
|---|
| 122 | get declsInOrder from var |
|---|
| 123 | |
|---|
| 124 | get isNames as String* |
|---|
| 125 | return _isNames |
|---|
| 126 | |
|---|
| 127 | pro parentNameSpace from var |
|---|
| 128 | """ |
|---|
| 129 | The namespace this container was declared inside of, if any. |
|---|
| 130 | If nil, the .parentBox should not be. |
|---|
| 131 | """ |
|---|
| 132 | |
|---|
| 133 | pro parentBox from var |
|---|
| 134 | """ |
|---|
| 135 | The box this container was declared inside of, if any. |
|---|
| 136 | Consider, for example, an enumeration that is declared inside a class. |
|---|
| 137 | If nil, the .parentNameSpace should not be. |
|---|
| 138 | """ |
|---|
| 139 | |
|---|
| 140 | get parent as IParentSpace? |
|---|
| 141 | if .parentBox, return .parentBox |
|---|
| 142 | if .parentNameSpace, return .parentNameSpace |
|---|
| 143 | return nil |
|---|
| 144 | |
|---|
| 145 | def addDecl(decl as TMember) |
|---|
| 146 | require |
|---|
| 147 | decl.name.length |
|---|
| 148 | not .declsInOrder.contains(decl) |
|---|
| 149 | .declForName(decl.name) is nil |
|---|
| 150 | not .canDeclNamesDifferOnlyByCase implies .declForNameCI(decl.name) is nil |
|---|
| 151 | ensure |
|---|
| 152 | .declsInOrder.contains(decl) |
|---|
| 153 | .declForName(decl.name) is decl |
|---|
| 154 | .declForNameCI(decl.name) is decl |
|---|
| 155 | .declsInOrder.count == old .declsInOrder.count + 1 |
|---|
| 156 | body |
|---|
| 157 | _declsInOrder.add(decl) |
|---|
| 158 | _declsByName.add(decl.name, decl) |
|---|
| 159 | if .canDeclNamesDifferOnlyByCase |
|---|
| 160 | _declsByNameCI[decl.name.toLower] = decl |
|---|
| 161 | else |
|---|
| 162 | _declsByNameCI.add(decl.name.toLower, decl) |
|---|
| 163 | |
|---|
| 164 | def declForName(name as String) as TMember? |
|---|
| 165 | require |
|---|
| 166 | name.length |
|---|
| 167 | ensure |
|---|
| 168 | result implies result.name==name |
|---|
| 169 | body |
|---|
| 170 | #print _declsByName.keys |
|---|
| 171 | return if(_declsByName.containsKey(name), _declsByName[name], nil) |
|---|
| 172 | |
|---|
| 173 | # for debug |
|---|
| 174 | def dumpDeclsNameKeys |
|---|
| 175 | print _declsByName.keys |
|---|
| 176 | |
|---|
| 177 | def declForName(name as String) as IMember? |
|---|
| 178 | implements IContainer |
|---|
| 179 | return if(_declsByName.containsKey(name), _declsByName[name], nil) |
|---|
| 180 | |
|---|
| 181 | def declForNameCI(name as String) as TMember? |
|---|
| 182 | require |
|---|
| 183 | name.length |
|---|
| 184 | ensure |
|---|
| 185 | result implies result.name.toLower==name.toLower |
|---|
| 186 | result is nil implies .declForName(name) is nil |
|---|
| 187 | body |
|---|
| 188 | decl as TMember? |
|---|
| 189 | _declsByNameCI.tryGetValue(name.toLower, out decl) |
|---|
| 190 | return decl |
|---|
| 191 | |
|---|
| 192 | pro docString from var |
|---|
| 193 | |
|---|
| 194 | def addSubFields is override |
|---|
| 195 | base.addSubFields |
|---|
| 196 | .addField('declsInOrder', _declsInOrder) |
|---|
| 197 | |
|---|
| 198 | def dumpDeclNames |
|---|
| 199 | print |
|---|
| 200 | print this |
|---|
| 201 | for decl in _declsInOrder |
|---|
| 202 | print ' [decl.name] --> [decl.getType.name]' |
|---|
| 203 | |
|---|
| 204 | |
|---|
| 205 | ## IMember |
|---|
| 206 | |
|---|
| 207 | get englishName as String is abstract |
|---|
| 208 | |
|---|
| 209 | get isCallable as bool |
|---|
| 210 | return false |
|---|
| 211 | |
|---|
| 212 | get requiresThis as bool |
|---|
| 213 | return false |
|---|
| 214 | |
|---|
| 215 | get resultType as IType |
|---|
| 216 | return this |
|---|
| 217 | |
|---|
| 218 | |
|---|
| 219 | ## IType |
|---|
| 220 | |
|---|
| 221 | def greatestCommonDenominatorWith(type as IType) as IType |
|---|
| 222 | # TODO: the duplication with CobraType here feels wrong |
|---|
| 223 | if this is type |
|---|
| 224 | return this |
|---|
| 225 | if type inherits NilableType |
|---|
| 226 | return type.greatestCommonDenominatorWith(this) |
|---|
| 227 | if type inherits NilType |
|---|
| 228 | return .typeProvider.nilableType(this) |
|---|
| 229 | if .isDescendantOf(type) |
|---|
| 230 | return type |
|---|
| 231 | if type.isDescendantOf(this) |
|---|
| 232 | return this |
|---|
| 233 | # TODO: following is a guess |
|---|
| 234 | if .superType and type.superType |
|---|
| 235 | return .superType.greatestCommonDenominatorWith(type.superType to !) |
|---|
| 236 | else |
|---|
| 237 | return .compiler.objectType |
|---|
| 238 | |
|---|
| 239 | def isAssignableTo(type as IType) as bool |
|---|
| 240 | require |
|---|
| 241 | .compiler |
|---|
| 242 | body |
|---|
| 243 | # TODO: some code dup with IType |
|---|
| 244 | if .serialNum == type.serialNum |
|---|
| 245 | assert this is type |
|---|
| 246 | if this is type |
|---|
| 247 | return true |
|---|
| 248 | if type is .compiler.passThroughType |
|---|
| 249 | return true |
|---|
| 250 | if type is .compiler.dynamicType |
|---|
| 251 | return true |
|---|
| 252 | if type is .compiler.objectType |
|---|
| 253 | return true |
|---|
| 254 | if type inherits NilableType |
|---|
| 255 | return .isAssignableTo(type.theWrappedType to passthrough) # CC: weird cast. bug. has to do with if-inherits and invoking the same method |
|---|
| 256 | return .isDescendantOf(type) |
|---|
| 257 | |
|---|
| 258 | # CC: duplicated from Type |
|---|
| 259 | def isComparableTo(t as IType) as bool |
|---|
| 260 | t = t.nonNil |
|---|
| 261 | if t inherits NilableType |
|---|
| 262 | t = t.theWrappedType |
|---|
| 263 | compareTo = .memberForName('compareTo') |
|---|
| 264 | if compareTo and compareTo.isMethod and compareTo.resultType is .compiler.intType and t.isDescendantOf(this) |
|---|
| 265 | # TODO: check that the compareTo can take a `t` as an argument |
|---|
| 266 | return true |
|---|
| 267 | return false |
|---|
| 268 | |
|---|
| 269 | # CC: duplicated from Type |
|---|
| 270 | def isEquatableTo(t as IType) as bool |
|---|
| 271 | t = t.nonNil |
|---|
| 272 | if .isAssignableTo(t) or t.isAssignableTo(this) # CC: to ! or assert above with nil flow analysis |
|---|
| 273 | return true |
|---|
| 274 | return false |
|---|
| 275 | |
|---|
| 276 | # CC: duplicated from Type |
|---|
| 277 | get nonNil as IType |
|---|
| 278 | return this |
|---|
| 279 | |
|---|
| 280 | def isDescendantOf(type as IType) as bool |
|---|
| 281 | require .didBindInh |
|---|
| 282 | return type is this |
|---|
| 283 | |
|---|
| 284 | # CC: duplicated from Type |
|---|
| 285 | def isStrictDescendantOf(type as IType) as bool |
|---|
| 286 | if type is this |
|---|
| 287 | return false |
|---|
| 288 | else |
|---|
| 289 | return .isDescendantOf(type) |
|---|
| 290 | |
|---|
| 291 | get isDynamic as bool |
|---|
| 292 | return false |
|---|
| 293 | |
|---|
| 294 | get isReference as bool is abstract |
|---|
| 295 | |
|---|
| 296 | get isSystemObjectClass as bool |
|---|
| 297 | return false |
|---|
| 298 | |
|---|
| 299 | get isSystemTypeClass as bool |
|---|
| 300 | return false |
|---|
| 301 | |
|---|
| 302 | get isUninitializedForLocalVars as bool |
|---|
| 303 | return .isReference |
|---|
| 304 | |
|---|
| 305 | get innerType as IType? |
|---|
| 306 | return nil |
|---|
| 307 | |
|---|
| 308 | get realType as IType |
|---|
| 309 | return this |
|---|
| 310 | |
|---|
| 311 | get superType as IType? |
|---|
| 312 | require .didBindInh |
|---|
| 313 | return nil |
|---|
| 314 | |
|---|
| 315 | def secondaryConstructedTypeFor(box as Box, gpToType as Dictionary<of GenericParam, IType>) as IType |
|---|
| 316 | return this |
|---|
| 317 | |
|---|
| 318 | def memberForName(name as String) as IMember? |
|---|
| 319 | require .canAccessMemberForName |
|---|
| 320 | return .declForName(name) |
|---|
| 321 | |
|---|
| 322 | get canAccessMemberForName as bool |
|---|
| 323 | # TODO: return not .compiler.isParsing # use .declForName instead |
|---|
| 324 | return true |
|---|
| 325 | |
|---|
| 326 | def suggestionsForBadMemberName(name as String) as List<of String> |
|---|
| 327 | name = name.toLower |
|---|
| 328 | suggs = List<of String>() |
|---|
| 329 | for decl in _declsInOrder |
|---|
| 330 | declName = decl.name.toLower |
|---|
| 331 | if declName == name or declName in name or name in declName |
|---|
| 332 | suggs.add(decl.name) |
|---|
| 333 | # TODO: would be nice to pick up on extraneous chars, missing chars, transposed chars |
|---|
| 334 | suggs.sort |
|---|
| 335 | return suggs |
|---|
| 336 | |
|---|
| 337 | # TODO: _isNames, .writeSharpIsNames and defaultAccessLevel should be moved out to a mixin since NameSpace, a subclass of Container, has no isNames. Box and Enum do. So does ClassMember which duplicates this. |
|---|
| 338 | |
|---|
| 339 | get defaultAccessLevel as String is abstract |
|---|
| 340 | |
|---|
| 341 | get isShared as bool |
|---|
| 342 | return 'shared' in _isNames |
|---|
| 343 | |
|---|
| 344 | def unNilReturnType |
|---|
| 345 | # TODO: can this be axed when IMember gets broken up? |
|---|
| 346 | pass |
|---|
| 347 | |
|---|
| 348 | |
|---|
| 349 | ## Other |
|---|
| 350 | |
|---|
| 351 | def cloneCollections |
|---|
| 352 | _declsInOrder = List<of TMember>(_declsInOrder) |
|---|
| 353 | _declsByName = Dictionary<of String, TMember>(_declsByName) |
|---|
| 354 | _declsByNameCI = Dictionary<of String, TMember>(_declsByNameCI) |
|---|
| 355 | |
|---|
| 356 | |
|---|
| 357 | interface IMember |
|---|
| 358 | is partial |
|---|
| 359 | inherits INamedNode |
|---|
| 360 | """ |
|---|
| 361 | TODO: this really needs to be split up into INameSpaceMember and IBoxMember. *Maybe* there is a logical IMember parent interface to both of those. |
|---|
| 362 | |
|---|
| 363 | Anything that could conceivably be a member of namespace, class, |
|---|
| 364 | struct or interface, implements IMember. |
|---|
| 365 | |
|---|
| 366 | That includes namespaces, classes, structs and interfaces |
|---|
| 367 | themselves. As well as the obvious choices of methods, inits, |
|---|
| 368 | properties and indexers. Also, enums and generic parameters, which |
|---|
| 369 | themselves can be specific types such as `int`. |
|---|
| 370 | |
|---|
| 371 | That mostly leaves statements and expressions as the nodes that |
|---|
| 372 | cannot be IMembers. |
|---|
| 373 | |
|---|
| 374 | IContainer and Container both reference IMember extensively. |
|---|
| 375 | |
|---|
| 376 | Furthermore, IContainer happens to implement IMember because, as it |
|---|
| 377 | turns out, all of the containers may be members of other containers |
|---|
| 378 | (though not without restrictions (a namespace can belong to only |
|---|
| 379 | another namespace)). |
|---|
| 380 | """ |
|---|
| 381 | |
|---|
| 382 | get attributes as AttributeList |
|---|
| 383 | """ |
|---|
| 384 | Return the attributes of this member. |
|---|
| 385 | """ |
|---|
| 386 | |
|---|
| 387 | get isCallable as bool |
|---|
| 388 | """ |
|---|
| 389 | Returns true if this member can be called with parens: () or (args) |
|---|
| 390 | """ |
|---|
| 391 | |
|---|
| 392 | get isShared as bool |
|---|
| 393 | """ |
|---|
| 394 | Returns true if this member is shared (non-shared members can be |
|---|
| 395 | called instance members). |
|---|
| 396 | """ |
|---|
| 397 | |
|---|
| 398 | get englishName as String # TODO: rename to englishTypeName |
|---|
| 399 | """ |
|---|
| 400 | Return the type of member as a plain, lowercase English word: |
|---|
| 401 | class, property, method, namespace, etc. |
|---|
| 402 | """ |
|---|
| 403 | |
|---|
| 404 | pro parentNameSpace as NameSpace? |
|---|
| 405 | """ |
|---|
| 406 | The NameSpace that the member directly belongs to, if any. |
|---|
| 407 | """ |
|---|
| 408 | |
|---|
| 409 | get requiresThis as bool |
|---|
| 410 | """ |
|---|
| 411 | Returns true if accessing the member requires a `this` |
|---|
| 412 | reference. True of non-shared properties, methods and fields. |
|---|
| 413 | """ |
|---|
| 414 | |
|---|
| 415 | get resultType as IType # TODO: rename to dottedAccessResultType |
|---|
| 416 | """ |
|---|
| 417 | Returns the result of accessing this member in the source |
|---|
| 418 | expression "foo.bar" where this is the bar. For a method or |
|---|
| 419 | property, this would be the return type. For other members like |
|---|
| 420 | enums and classes, this would be the member itself. |
|---|
| 421 | """ |
|---|
| 422 | |
|---|
| 423 | def unNilReturnType |
|---|
| 424 | """ |
|---|
| 425 | This is invoked to fix up the CLR library which does not indicate, for example, that StringBuilder.toString returns String not String? |
|---|
| 426 | """ |
|---|