| 1 | """ |
|---|
| 2 | Rules: |
|---|
| 3 | * Do not invoke .bindFoo on any type that an expression discovers during its own .bindImp. |
|---|
| 4 | """ |
|---|
| 5 | |
|---|
| 6 | interface IExpr inherits ISyntaxNode |
|---|
| 7 | """ |
|---|
| 8 | This is mainly used for categorization. |
|---|
| 9 | To see what really makes an expression, see the Expr class. |
|---|
| 10 | """ |
|---|
| 11 | |
|---|
| 12 | get definition |
|---|
| 13 | """ |
|---|
| 14 | Returns the definition corresponding to this expression. |
|---|
| 15 | Dynamically typed. |
|---|
| 16 | Returns nil by default. |
|---|
| 17 | Not all expressions have definitions. |
|---|
| 18 | """ |
|---|
| 19 | |
|---|
| 20 | pro type as IType? |
|---|
| 21 | |
|---|
| 22 | pro receiverType as IType? # TODO: consider renaming to typeForReceiver |
|---|
| 23 | """ |
|---|
| 24 | This property returns the .type if there was no receiver type explicitly set because the type of an expression is its receiver type by default. |
|---|
| 25 | """ |
|---|
| 26 | |
|---|
| 27 | pro contextType as IType? |
|---|
| 28 | """ |
|---|
| 29 | The type expected where this expression is being used. |
|---|
| 30 | Normally this is the same type as the expression, but it can be different, for example when |
|---|
| 31 | assigning a dynamic-typed expression to a static-typed variable, the type of the variable |
|---|
| 32 | becomes the context type. |
|---|
| 33 | """ |
|---|
| 34 | |
|---|
| 35 | def toCobraSource as String |
|---|
| 36 | |
|---|
| 37 | |
|---|
| 38 | class Expr inherits Stmt implements IExpr is abstract, partial |
|---|
| 39 | |
|---|
| 40 | var _type as IType? |
|---|
| 41 | var _receiverType as IType? |
|---|
| 42 | """ |
|---|
| 43 | The type for receiving messages which can be different than the |
|---|
| 44 | _type in a static situation such as `ChessPiece.Color` (where |
|---|
| 45 | Color is an enum). The type of that expression, if assigned to a |
|---|
| 46 | local var, is Type. But if accessed with a further dot |
|---|
| 47 | `ChessPiece.Color.Black`, then the type is the Color |
|---|
| 48 | enumeration itself--the receiverType. |
|---|
| 49 | |
|---|
| 50 | For a runtime access (ex: `user.name`) the two types are |
|---|
| 51 | conceptually the same (ex: `String`) and _receiverType is left |
|---|
| 52 | nil. |
|---|
| 53 | |
|---|
| 54 | _receiverType is used in memberForName() to look up members such |
|---|
| 55 | as "Black". |
|---|
| 56 | |
|---|
| 57 | TODO: rename to _typeForMemberAccess |
|---|
| 58 | """ |
|---|
| 59 | var _contextType as IType? |
|---|
| 60 | var _isParened as bool |
|---|
| 61 | var _direction = Direction.In |
|---|
| 62 | |
|---|
| 63 | cue init(token as IToken) |
|---|
| 64 | base.init(token) |
|---|
| 65 | |
|---|
| 66 | def addRefFields is override |
|---|
| 67 | base.addRefFields |
|---|
| 68 | .addField('type', _type) |
|---|
| 69 | if .definition, .addField('definition', .definition) |
|---|
| 70 | |
|---|
| 71 | get allExprs as Expr* |
|---|
| 72 | """ Subclasses should override to yield the .allExprs of their direct sub-expressions. """ |
|---|
| 73 | yield this |
|---|
| 74 | |
|---|
| 75 | pro direction from var |
|---|
| 76 | """ |
|---|
| 77 | Indicates the label for an argument such as 'out' or 'inout'. |
|---|
| 78 | """ |
|---|
| 79 | |
|---|
| 80 | get definition |
|---|
| 81 | return nil |
|---|
| 82 | |
|---|
| 83 | get hasError as bool is override |
|---|
| 84 | for expr in .allExprs, if expr._innerHasError, return true |
|---|
| 85 | return false |
|---|
| 86 | |
|---|
| 87 | get _innerHasError as bool |
|---|
| 88 | return .errors and .errors.count > 0 |
|---|
| 89 | |
|---|
| 90 | get willChangeVar as bool |
|---|
| 91 | """ |
|---|
| 92 | Return true if this expression will change a variable. |
|---|
| 93 | Used by `assert` and others that won't accept expressions with such side effects from assignment, `out` parameters, etc. |
|---|
| 94 | Subclasses that contain subexpressions must override to check their subexpressions. |
|---|
| 95 | """ |
|---|
| 96 | return false |
|---|
| 97 | |
|---|
| 98 | get canBeStatement as bool |
|---|
| 99 | return false |
|---|
| 100 | |
|---|
| 101 | def afterStatementBindImp is override |
|---|
| 102 | base.afterStatementBindImp |
|---|
| 103 | if not .hasError and not .canBeStatement |
|---|
| 104 | if .isHelpRequested |
|---|
| 105 | .compiler.warning(this, '@help expression ignored due to not being a viable statement.') |
|---|
| 106 | _transformTo(DotExpr(.token, 'DOT', IdentifierExpr(.token, 'CobraCore'), MemberExpr(.token, 'noOp'), isImplicit=true).bindAll) |
|---|
| 107 | else |
|---|
| 108 | .recordError('Only assignment, call and new object expressions can be used as a statement.') |
|---|
| 109 | |
|---|
| 110 | pro type from var |
|---|
| 111 | |
|---|
| 112 | pro receiverType as IType? |
|---|
| 113 | get |
|---|
| 114 | return _receiverType ? _type |
|---|
| 115 | set |
|---|
| 116 | _receiverType = value |
|---|
| 117 | |
|---|
| 118 | pro contextType from var |
|---|
| 119 | |
|---|
| 120 | get curBox as Box? is protected |
|---|
| 121 | """ |
|---|
| 122 | Returns the current code member during "bind imp" and "write sharp". |
|---|
| 123 | """ |
|---|
| 124 | return if(.compiler.boxStack.count, .compiler.boxStack.peek, nil) |
|---|
| 125 | |
|---|
| 126 | get curCodeMember as AbstractMethod? is protected |
|---|
| 127 | """ |
|---|
| 128 | Returns the current code member during "bind imp" and "write sharp". |
|---|
| 129 | """ |
|---|
| 130 | return if(.compiler.codeMemberStack.count, .compiler.codeMemberStack.peek, nil) |
|---|
| 131 | |
|---|
| 132 | def canBeAssignedTo(type as IType) as bool |
|---|
| 133 | require |
|---|
| 134 | .didBindImp |
|---|
| 135 | .type |
|---|
| 136 | body |
|---|
| 137 | return .type.isAssignableTo(type) |
|---|
| 138 | |
|---|
| 139 | def toCobraSource as String |
|---|
| 140 | return '[.getType.name].toCobraSource' |
|---|
| 141 | |
|---|
| 142 | get isCalling as bool |
|---|
| 143 | """ |
|---|
| 144 | Return true is this expression is making a call. |
|---|
| 145 | The default implementation returns false. |
|---|
| 146 | You might think "inherits CallExpr" would suffice, but MemberExpr and IdentifierExpr can |
|---|
| 147 | also return true when they refer to methods or inits. |
|---|
| 148 | """ |
|---|
| 149 | return false |
|---|
| 150 | |
|---|
| 151 | def isKindOf(type as IType) as bool |
|---|
| 152 | require |
|---|
| 153 | .type |
|---|
| 154 | .compiler |
|---|
| 155 | body |
|---|
| 156 | return .type.isDescendantOf(type) |
|---|
| 157 | |
|---|
| 158 | def isKindOfCollection as bool |
|---|
| 159 | require |
|---|
| 160 | .type |
|---|
| 161 | .compiler |
|---|
| 162 | body |
|---|
| 163 | return .isKindOf(.compiler.collectionType) or .isKindOf(.compiler.collectionOfType) |
|---|
| 164 | |
|---|
| 165 | def isKindOfNonNil(type as IType) as bool |
|---|
| 166 | require |
|---|
| 167 | .type |
|---|
| 168 | .compiler |
|---|
| 169 | body |
|---|
| 170 | return .type.nonNil.isDescendantOf(type) |
|---|
| 171 | |
|---|
| 172 | get isObjectLiteral as bool |
|---|
| 173 | return false |
|---|
| 174 | |
|---|
| 175 | pro isParened from var |
|---|
| 176 | """ |
|---|
| 177 | Set by the parser if this expression was wrapped in parenthesis. |
|---|
| 178 | TODO: Consider enhancing toCobraSource to make use of this! |
|---|
| 179 | """ |
|---|
| 180 | |
|---|
| 181 | def isEquivalentTo(expr as Expr) as bool |
|---|
| 182 | # TODO: should compare reductions of expressions (or be used after a reduction phase) |
|---|
| 183 | return expr is this or (expr.typeOf == .typeOf and expr.serialNum == .serialNum) |
|---|
| 184 | |
|---|
| 185 | def memberForName(name as String) as IMember? |
|---|
| 186 | require |
|---|
| 187 | .didBindImp |
|---|
| 188 | .type |
|---|
| 189 | body |
|---|
| 190 | return .receiverType.memberForName(name) |
|---|
| 191 | |
|---|
| 192 | def suggestionsForBadMemberName(name as String) as List<of String> |
|---|
| 193 | require |
|---|
| 194 | .didBindImp |
|---|
| 195 | .type |
|---|
| 196 | body |
|---|
| 197 | return .receiverType.suggestionsForBadMemberName(name) |
|---|
| 198 | |
|---|
| 199 | get binarySuperNode as BinaryOpExpr |
|---|
| 200 | require |
|---|
| 201 | .superNode inherits BinaryOpExpr |
|---|
| 202 | body |
|---|
| 203 | return .superNode to BinaryOpExpr |
|---|
| 204 | |
|---|
| 205 | def postBindImp is override |
|---|
| 206 | .postBindImpAssertType |
|---|
| 207 | .help |
|---|
| 208 | |
|---|
| 209 | def postBindImpAssertType |
|---|
| 210 | """ |
|---|
| 211 | This is broken out because in rare cases, it doesn't apply. (Those classes override this to |
|---|
| 212 | do nothing.) |
|---|
| 213 | """ |
|---|
| 214 | if not .hasError |
|---|
| 215 | assert .type or .transformedTo |
|---|
| 216 | |
|---|
| 217 | def help is override |
|---|
| 218 | if not .hasError and .isHelpRequested |
|---|
| 219 | hg = HelpGenerator(.toCobraSource, this, .compiler.curBoxMember, .curBox) |
|---|
| 220 | _help(hg) |
|---|
| 221 | hg.generate |
|---|
| 222 | .compiler.warning(this, '@help at "[hg.path]".') |
|---|
| 223 | |
|---|
| 224 | def _help(hg as HelpGenerator) |
|---|
| 225 | """ Subclasses can override this method to augment the help generator's information. """ |
|---|
| 226 | if .receiverType |
|---|
| 227 | _helpType(hg, .receiverType) |
|---|
| 228 | if .type and not .type == .receiverType |
|---|
| 229 | _helpType(hg, .type) |
|---|
| 230 | |
|---|
| 231 | def _helpType(hg as HelpGenerator, type as IType?) |
|---|
| 232 | if type |
|---|
| 233 | hg.types.add(type) |
|---|
| 234 | hg.searchTerms.add(type.name) |
|---|
| 235 | ns = type.parentNameSpace |
|---|
| 236 | if ns |
|---|
| 237 | if not ns.isRoot, hg.searchTerms.add(ns.fullName + ' ' + type.name) |
|---|
| 238 | else if type inherits Box |
|---|
| 239 | pb = type.parentBox |
|---|
| 240 | if pb, hg.searchTerms.add(pb.qualifiedName + ' ' + type.name) |
|---|
| 241 | |
|---|
| 242 | def _bindImp is override |
|---|
| 243 | # TODO: |
|---|
| 244 | # ensure |
|---|
| 245 | # .type |
|---|
| 246 | body |
|---|
| 247 | base._bindImp |
|---|
| 248 | |
|---|
| 249 | def _suggestionsMessage(suggs as List<of String>) as String |
|---|
| 250 | """ |
|---|
| 251 | Shared by MemberExpr, CallExpr and IdentifierExpr to give suggestions in an error message about an unknown member. |
|---|
| 252 | """ |
|---|
| 253 | if suggs.count |
|---|
| 254 | if suggs.count == 1 |
|---|
| 255 | sugg = suggs[0] |
|---|
| 256 | prefix = if(sugg.startsWith('_'), '', '.') |
|---|
| 257 | msg = ' There is a member named "[prefix][sugg]" with a similar name.' |
|---|
| 258 | else |
|---|
| 259 | msg = ' There are members with similar names including ' |
|---|
| 260 | sep = '' |
|---|
| 261 | for i, sugg in suggs.numbered |
|---|
| 262 | prefix = if(sugg.startsWith('_'), '', '.') |
|---|
| 263 | msg += '[sep]"[prefix][sugg]"' |
|---|
| 264 | sep = if(i<suggs.count-2, ', ', ' and ') |
|---|
| 265 | msg += '.' |
|---|
| 266 | else |
|---|
| 267 | msg = '' |
|---|
| 268 | return msg |
|---|
| 269 | |
|---|
| 270 | def whoseTypeIsMessage as String |
|---|
| 271 | """ |
|---|
| 272 | Returns a suffix for member-not-found error messages. |
|---|
| 273 | Returns ' whose type is "[.receiverType.name]"' |
|---|
| 274 | Overridden by IdentifierExpr to return '' when accessing types. |
|---|
| 275 | """ |
|---|
| 276 | return ' whose type is "[.receiverType.name]"' |
|---|
| 277 | |
|---|
| 278 | def throwCannotFindMemberError(receiverExpr as Expr, memberName as String) |
|---|
| 279 | __throwOrRecordCannotFindMemberError(receiverExpr, memberName, 1) |
|---|
| 280 | |
|---|
| 281 | def recordCannotFindMemberError(receiverExpr as Expr, memberName as String) |
|---|
| 282 | __throwOrRecordCannotFindMemberError(receiverExpr, memberName, 2) |
|---|
| 283 | |
|---|
| 284 | def __throwOrRecordCannotFindMemberError(receiverExpr as Expr, memberName as String, throwOrRecord as int) is nonvirtual |
|---|
| 285 | require throwOrRecord in [1, 2] |
|---|
| 286 | suggs = _suggestionsMessage(receiverExpr.suggestionsForBadMemberName(memberName)) |
|---|
| 287 | errorMsg = 'Cannot find a definition for "[memberName]" in "[receiverExpr.toCobraSource]"[receiverExpr.whoseTypeIsMessage].[suggs]' |
|---|
| 288 | branch throwOrRecord |
|---|
| 289 | on 1, .throwError(errorMsg) |
|---|
| 290 | on 2, .recordError(errorMsg) |
|---|
| 291 | else, throw FallThroughException(throwOrRecord) |
|---|
| 292 | |
|---|
| 293 | |
|---|
| 294 | interface IPotentialTypeExpr inherits IExpr |
|---|
| 295 | """ |
|---|
| 296 | An implementor of IPotentialType *could* be a representation of a type. |
|---|
| 297 | This is implemented by expressions such as DotExpr, IdentifierExpr and TypeExpr. |
|---|
| 298 | It "inherits IExpr" to emphasize and verify that fact. This interface is not for statements, classes, etc. |
|---|
| 299 | """ |
|---|
| 300 | |
|---|
| 301 | get potentialType as IType? |
|---|
| 302 | """ |
|---|
| 303 | Returns the type that this object represents, or nil if it does not represent a type. |
|---|
| 304 | Should be asked for only during "bind implementation" or later. |
|---|
| 305 | """ |
|---|
| 306 | |
|---|
| 307 | |
|---|
| 308 | class NameExpr |
|---|
| 309 | is abstract, partial |
|---|
| 310 | inherits Expr |
|---|
| 311 | """ |
|---|
| 312 | The base class for IdentifierExpr and AsExpr. |
|---|
| 313 | """ |
|---|
| 314 | |
|---|
| 315 | var _name as String |
|---|
| 316 | var _definition as INamedNode? |
|---|
| 317 | """ |
|---|
| 318 | In practice, definitions include: |
|---|
| 319 | * Class, Struct, Interface |
|---|
| 320 | * NameSpace, EnumDecl |
|---|
| 321 | * ClassVar, LocalVar, Param |
|---|
| 322 | There may be others, but at least those have been observed in practice. |
|---|
| 323 | """ |
|---|
| 324 | |
|---|
| 325 | cue init(token as IToken) |
|---|
| 326 | base.init(token) |
|---|
| 327 | _name = token.text |
|---|
| 328 | |
|---|
| 329 | get idString as String is override |
|---|
| 330 | if .token.isEmpty |
|---|
| 331 | return '[.getType.name]([.serialNum], [.token.shortLocationString])' |
|---|
| 332 | else |
|---|
| 333 | return '[.getType.name]([.serialNum], "[.name]", [.token.shortLocationString])' |
|---|
| 334 | |
|---|
| 335 | def addMinFields |
|---|
| 336 | base.addMinFields |
|---|
| 337 | .addField('name', _name) |
|---|
| 338 | |
|---|
| 339 | get definition is override |
|---|
| 340 | return _definition |
|---|
| 341 | |
|---|
| 342 | get namedDefinition from _definition |
|---|
| 343 | |
|---|
| 344 | get name from var |
|---|
| 345 | |
|---|
| 346 | def memberForName(name as String) as IMember? is override |
|---|
| 347 | or require |
|---|
| 348 | .namedDefinition |
|---|
| 349 | body |
|---|
| 350 | return _definition.typeForReceiver.memberForName(name) |
|---|
| 351 | |
|---|
| 352 | |
|---|
| 353 | class AnonymousExpr |
|---|
| 354 | is abstract, partial |
|---|
| 355 | inherits Expr |
|---|
| 356 | """ |
|---|
| 357 | The abstract base class for LambdaExpr and AnonymousMethodExpr. |
|---|
| 358 | """ |
|---|
| 359 | |
|---|
| 360 | var _returnTypeProxy as ITypeProxy? |
|---|
| 361 | |
|---|
| 362 | cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?) |
|---|
| 363 | base.init(token) |
|---|
| 364 | _params = params |
|---|
| 365 | _returnTypeProxy = returnTypeProxy |
|---|
| 366 | |
|---|
| 367 | get params from var as List<of Param> |
|---|
| 368 | |
|---|
| 369 | def addSubFields |
|---|
| 370 | base.addSubFields |
|---|
| 371 | .addField('params', .params) |
|---|
| 372 | |
|---|
| 373 | def toCobraSource as String is override |
|---|
| 374 | sb = StringBuilder('do(') |
|---|
| 375 | sep = '' |
|---|
| 376 | for param in .params |
|---|
| 377 | sb.append(sep) |
|---|
| 378 | #sb.append(param.toCobraSource) # TODO |
|---|
| 379 | sb.append(param.name) |
|---|
| 380 | sep = ', ' |
|---|
| 381 | sb.append(')') |
|---|
| 382 | return sb.toString |
|---|
| 383 | |
|---|
| 384 | |
|---|
| 385 | class AnonymousMethodExpr inherits AnonymousExpr is partial |
|---|
| 386 | implements HasAddStmt |
|---|
| 387 | """ |
|---|
| 388 | Also called "closures". |
|---|
| 389 | """ |
|---|
| 390 | |
|---|
| 391 | var _method as AnonymousMethod? |
|---|
| 392 | |
|---|
| 393 | cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?) |
|---|
| 394 | base.init(token, params, returnTypeProxy) |
|---|
| 395 | _stmts = List<of Stmt>() |
|---|
| 396 | |
|---|
| 397 | get stmts from var as List<of Stmt> |
|---|
| 398 | |
|---|
| 399 | def addStmt(stmt as Stmt) |
|---|
| 400 | _stmts.add(stmt) |
|---|
| 401 | |
|---|
| 402 | get _innerHasError as bool |
|---|
| 403 | return base._innerHasError or _method.hasError |
|---|
| 404 | |
|---|
| 405 | def _bindImp is override |
|---|
| 406 | base._bindImp |
|---|
| 407 | _method = AnonymousMethod(.token, .curCodeMember, .params, _returnTypeProxy, if(.curCodeMember.isShared, ['shared'], List<of String>())) |
|---|
| 408 | for stmt in .stmts, _method.addStmt(stmt) |
|---|
| 409 | _method.bindInt |
|---|
| 410 | _method.bindImp |
|---|
| 411 | _type = _method.resultType |
|---|
| 412 | |
|---|
| 413 | |
|---|
| 414 | class LambdaExpr inherits AnonymousExpr is partial |
|---|
| 415 | """ |
|---|
| 416 | ... do(a as int, b as int)=a.compareTo(b) ... |
|---|
| 417 | """ |
|---|
| 418 | |
|---|
| 419 | cue init(token as IToken, params as List<of Param>, returnTypeProxy as ITypeProxy?, expr as Expr) |
|---|
| 420 | base.init(token, params, returnTypeProxy) |
|---|
| 421 | _expr = expr |
|---|
| 422 | |
|---|
| 423 | get allExprs as Expr* |
|---|
| 424 | for expr in base.allExprs, yield expr |
|---|
| 425 | for expr in .expr.allExprs, yield expr |
|---|
| 426 | |
|---|
| 427 | get expr from var as Expr |
|---|
| 428 | |
|---|
| 429 | get method from var as AnonymousMethod? |
|---|
| 430 | # a reference to the method created in _bindImp is maintained purely for |
|---|
| 431 | # inspection/debugging purposes |
|---|
| 432 | |
|---|
| 433 | def addSubFields |
|---|
| 434 | base.addSubFields |
|---|
| 435 | .addField('expr', .expr) |
|---|
| 436 | |
|---|
| 437 | def toCobraSource as String is override |
|---|
| 438 | sb = StringBuilder(base.toCobraSource) |
|---|
| 439 | sb.append('=') |
|---|
| 440 | sb.append(.expr.toCobraSource) |
|---|
| 441 | return sb.toString |
|---|
| 442 | |
|---|
| 443 | def _bindImp is override |
|---|
| 444 | base._bindImp |
|---|
| 445 | _method = AnonymousMethod(.token, .curCodeMember, .params, .compiler.dynamicType, if(.curCodeMember.isShared, ['shared'], List<of String>())) |
|---|
| 446 | ret = ReturnStmt(.token.copy('RETURN', 'return'), .expr) |
|---|
| 447 | _method.addStmt(ret) |
|---|
| 448 | _method.bindInt |
|---|
| 449 | _method.bindImp |
|---|
| 450 | # the expression may have been transformed, but if so, only the containing ReturnStmt got the notice |
|---|
| 451 | _expr = ret.expr to ! |
|---|
| 452 | _type = .compiler.passThroughType # TODO: Set a real type such as .compiler.delegateType |
|---|
| 453 | # method.lambdaReturnType |
|---|
| 454 | |
|---|
| 455 | |
|---|
| 456 | class AsExpr |
|---|
| 457 | is partial |
|---|
| 458 | inherits NameExpr |
|---|
| 459 | """ |
|---|
| 460 | i as int = 5 |
|---|
| 461 | The "i as int" is an AsExpr. |
|---|
| 462 | """ |
|---|
| 463 | |
|---|
| 464 | var _asToken as IToken # the identifier vs. the `as` keyword which is the main token |
|---|
| 465 | var _typeNode as ITypeProxy |
|---|
| 466 | |
|---|
| 467 | cue init(token as IToken, nameToken as IToken, typeNode as ITypeProxy) |
|---|
| 468 | base.init(nameToken) |
|---|
| 469 | _asToken = token |
|---|
| 470 | _typeNode = typeNode |
|---|
| 471 | |
|---|
| 472 | def addMinFields is override |
|---|
| 473 | base.addMinFields |
|---|
| 474 | .addField('name', _name) |
|---|
| 475 | |
|---|
| 476 | def addSubFields is override |
|---|
| 477 | base.addSubFields |
|---|
| 478 | .addField('typeNode', _typeNode) |
|---|
| 479 | |
|---|
| 480 | get canBeStatement as bool is override |
|---|
| 481 | return true |
|---|
| 482 | |
|---|
| 483 | def _bindImp is override |
|---|
| 484 | base._bindImp |
|---|
| 485 | _type = _typeNode.realType |
|---|
| 486 | assert _type, _typeNode |
|---|
| 487 | if not .name.startsWithLowerLetter, .throwError(ErrorMessages.localVariablesMustStartLowercase) |
|---|
| 488 | # make the local var if necessary (usually is) |
|---|
| 489 | definition = .compiler.findLocal(_name) |
|---|
| 490 | if definition is nil |
|---|
| 491 | newVar = LocalVar(_token, _type) |
|---|
| 492 | newVar.bindAll |
|---|
| 493 | .compiler.codeMemberStack.peek.addLocal(newVar) |
|---|
| 494 | _definition = newVar |
|---|
| 495 | else |
|---|
| 496 | if definition.type <> _type |
|---|
| 497 | .throwError('Cannot redeclare "[.name]" as a different type. Earlier type is "[definition.type.name]".') |
|---|
| 498 | # to-do: would be nice to point to earlier location. definition would need to carry source location info |
|---|
| 499 | else |
|---|
| 500 | _definition = definition |
|---|
| 501 | |
|---|
| 502 | def postBindImp is override |
|---|
| 503 | _computeIsUsed |
|---|
| 504 | .help |
|---|
| 505 | |
|---|
| 506 | def _computeIsUsed |
|---|
| 507 | # don't want to say isUsed=true if the AsExpr is the left hand side like in: |
|---|
| 508 | # x as int = 5 |
|---|
| 509 | # but if it is the right hand side like so: |
|---|
| 510 | # _x = x as int = 5 |
|---|
| 511 | # then it is, in fact, being used. |
|---|
| 512 | if _definition and .superNode inherits BinaryOpExpr |
|---|
| 513 | if .binarySuperNode.right is this |
|---|
| 514 | # case: x = y as int |
|---|
| 515 | _definition.isUsed = true |
|---|
| 516 | else if .binarySuperNode.superNode inherits BinaryOpExpr and .binarySuperNode is .binarySuperNode.binarySuperNode.right |
|---|
| 517 | # case: x = y as int = 5 |
|---|
| 518 | # recall that assign is right-to-left associative |
|---|
| 519 | _definition.isUsed = true |
|---|
| 520 | |
|---|
| 521 | def afterStatementBindImp is override |
|---|
| 522 | base.afterStatementBindImp |
|---|
| 523 | if .type.isUninitializedForLocalVars |
|---|
| 524 | .throwError('Must initialize this non-nil object type, or change the type to nilable (suffix with ?).') |
|---|
| 525 | |
|---|
| 526 | |
|---|
| 527 | class CallOrMemberExpr inherits Expr is abstract, partial |
|---|
| 528 | |
|---|
| 529 | cue init(token as IToken, name as String) |
|---|
| 530 | base.init(token) |
|---|
| 531 | _name = name |
|---|
| 532 | |
|---|
| 533 | get name from var as String |
|---|
| 534 | |
|---|
| 535 | def _mangleName(name as String) as String |
|---|
| 536 | if .compiler.boxMemberStack.count > 0 and .binarySuperNode.left inherits ThisOrBaseLit |
|---|
| 537 | return .compiler.curBoxMember.mangleName(name) |
|---|
| 538 | # may have empty boxMemberStack for attributes like: |
|---|
| 539 | # has AttributeUsage(AttributeTargets.Method, allowMultiple=false) |
|---|
| 540 | return name |
|---|
| 541 | |
|---|
| 542 | def _checkVisibility(defn as BoxMember, left as Expr) |
|---|
| 543 | """ Check visibility: public, protected, private, internal. """ |
|---|
| 544 | if defn.isPublic, return |
|---|
| 545 | # search for 'def pub' and/or 'def prot' for the relevant test cases for this |
|---|
| 546 | curBox, defnParentBox = .compiler.curBox, defn.parentBox |
|---|
| 547 | if defn.isProtected |
|---|
| 548 | if not left inherits BaseLit _ |
|---|
| 549 | and not left inherits ThisLit _ |
|---|
| 550 | and not curBox is defnParentBox _ |
|---|
| 551 | and not defnParentBox.isDescendantOf(curBox) _ |
|---|
| 552 | and not (left.type.isDescendantOf(curBox) and curBox.isDescendantOf(defnParentBox)) _ |
|---|
| 553 | and not _subCheckVisibility(defn, left, curBox) |
|---|
| 554 | # then |
|---|
| 555 | msg = 'Cannot access protected "[_name]" in "[left.toCobraSource]"[left.whoseTypeIsMessage].' |
|---|
| 556 | if curBox.isDescendantOf(defnParentBox) or _ |
|---|
| 557 | (curBox.isGeneric and defnParentBox.isConstructed and curBox.isIndirectConstructionOf(defnParentBox.genericDef to !)) |
|---|
| 558 | # then |
|---|
| 559 | msg += ' The qualifier must be of type "[curBox.nameWithGenericParams]" or derived from it.' |
|---|
| 560 | .throwError(msg) |
|---|
| 561 | else if defn.isPrivate |
|---|
| 562 | if not curBox is defnParentBox and not curBox is defnParentBox.genericDef |
|---|
| 563 | .throwError('Cannot access private "[_name]" in "[left.toCobraSource]"[left.whoseTypeIsMessage].') |
|---|
| 564 | else if defn.isInternal |
|---|
| 565 | # TODO |
|---|
| 566 | pass |
|---|
| 567 | |
|---|
| 568 | def _subCheckVisibility(defn as BoxMember, left as Expr, curBox as Box) as bool |
|---|
| 569 | leftBox = left.type to? Box |
|---|
| 570 | if leftBox |
|---|
| 571 | return leftBox.isGeneric and curBox.isGenericDef and leftBox.isIndirectConstructionOf(curBox) and leftBox.isDescendantOf(defn.parentBox) |
|---|
| 572 | return false |
|---|
| 573 | |
|---|
| 574 | |
|---|
| 575 | class CallExpr inherits CallOrMemberExpr implements IDotRightExpr is partial |
|---|
| 576 | """ |
|---|
| 577 | A CallExpr may be on the right side of a dot expression as in `obj.foo(x, y)`. |
|---|
| 578 | |
|---|
| 579 | This expression will transform into PostCallExpr for the case where in Foo.Bar() the Bar is a type, not a method. |
|---|
| 580 | |
|---|
| 581 | The parser can't always know the exact context and semantics of a call. So transformations include: |
|---|
| 582 | EnumCallExpr |
|---|
| 583 | DotExpr -- for _foo(args) --> ._foo(args) |
|---|
| 584 | PostCallExpr |
|---|
| 585 | |
|---|
| 586 | A valid call expr may have a nil .definition because the receiver is dynamic. |
|---|
| 587 | """ |
|---|
| 588 | |
|---|
| 589 | var _genericArgProxies as List<of ITypeProxy>? |
|---|
| 590 | var _genericArgTypes as List<of IType>? |
|---|
| 591 | var _args as List<of Expr> |
|---|
| 592 | var _hasParens as bool |
|---|
| 593 | var _definition as IMember? |
|---|
| 594 | |
|---|
| 595 | cue init(token as IToken, name as String, args as List<of Expr>, hasParens as bool) |
|---|
| 596 | .init(token, name, nil, args, hasParens as bool) |
|---|
| 597 | |
|---|
| 598 | cue init(token as IToken, name as String, genericArgProxies as List<of ITypeProxy>?, args as List<of Expr>, hasParens as bool) |
|---|
| 599 | base.init(token, name) |
|---|
| 600 | _genericArgProxies = genericArgProxies |
|---|
| 601 | _args = args |
|---|
| 602 | _hasParens = hasParens |
|---|
| 603 | _definition = nil |
|---|
| 604 | |
|---|
| 605 | def addMinFields |
|---|
| 606 | base.addMinFields |
|---|
| 607 | .addField('name', _name) |
|---|
| 608 | |
|---|
| 609 | def addSubFields |
|---|
| 610 | base.addSubFields |
|---|
| 611 | .addField('args', _args) |
|---|
| 612 | |
|---|
| 613 | get allExprs as Expr* |
|---|
| 614 | for expr in base.allExprs, yield expr |
|---|
| 615 | for arg in .args, for expr in arg.allExprs, yield expr |
|---|
| 616 | |
|---|
| 617 | get genericArgProxies from var |
|---|
| 618 | |
|---|
| 619 | get genericArgTypes from var |
|---|
| 620 | |
|---|
| 621 | get hasParens from var |
|---|
| 622 | |
|---|
| 623 | get willChangeVar as bool is override |
|---|
| 624 | for arg in _args, if arg.willChangeVar, return true |
|---|
| 625 | for arg in _args, if arg.direction <> Direction.In, return true |
|---|
| 626 | return false |
|---|
| 627 | |
|---|
| 628 | get isCalling as bool is override |
|---|
| 629 | return true |
|---|
| 630 | |
|---|
| 631 | get args from var |
|---|
| 632 | has Subnodes |
|---|
| 633 | |
|---|
| 634 | get definition is override |
|---|
| 635 | return _definition |
|---|
| 636 | |
|---|
| 637 | get memberDefinition from _definition |
|---|
| 638 | |
|---|
| 639 | def _innerClone |
|---|
| 640 | assert _definition is nil |
|---|
| 641 | base._innerClone |
|---|
| 642 | # to-do: _genericArgProxies |
|---|
| 643 | _args = _args.toList2 |
|---|
| 644 | for i in _args.count, _args[i] = _args[i].clone |
|---|
| 645 | |
|---|
| 646 | def _bindImp is override |
|---|
| 647 | base._bindImp |
|---|
| 648 | assert _superNode inherits DotExpr # otherwise, for something like List<of int>(), the parser creates a PostCallExpr |
|---|
| 649 | if _genericArgProxies, _setGenericArgTypes |
|---|
| 650 | dotNode = _superNode to DotExpr |
|---|
| 651 | assert this is dotNode.right |
|---|
| 652 | |
|---|
| 653 | if dotNode.left.didBindImp |
|---|
| 654 | enumDefi = dotNode.left.memberForName(_name) |
|---|
| 655 | if enumDefi inherits EnumDecl |
|---|
| 656 | _transformToEnumDecl(dotNode, enumDefi) |
|---|
| 657 | return |
|---|
| 658 | |
|---|
| 659 | needInferOutArgs = false # to-do: after next snapshot remove this line. 2010-10-20 |
|---|
| 660 | hasErrors = _bindImpArgs(out needInferOutArgs) |
|---|
| 661 | definition as IMember? |
|---|
| 662 | type as IType? |
|---|
| 663 | |
|---|
| 664 | if _definition is nil or _type is nil |
|---|
| 665 | # handle foo.bar() where this is the `bar()` part |
|---|
| 666 | if not dotNode.left.didBindImp |
|---|
| 667 | assert dotNode.left.hasError, dotNode.left |
|---|
| 668 | # we get here for Cobra code like "obj.foo.bar(x)" where "foo" is not found |
|---|
| 669 | _type = .compiler.passThroughType |
|---|
| 670 | return # TODO: I don't think I want a return here |
|---|
| 671 | |
|---|
| 672 | definition = _inferDefinitionAndType(dotNode, out type) |
|---|
| 673 | assert type |
|---|
| 674 | if definition inherits IType # Box, IVar or GenericParam |
|---|
| 675 | _transformToPostCallExprOnType(definition) |
|---|
| 676 | return |
|---|
| 677 | |
|---|
| 678 | if definition |
|---|
| 679 | # definition should never be a Box, IVar or GenericParam as those cases would be handled by the transformation to PostCallExpr above |
|---|
| 680 | # there is a FallThroughException down below that would report this if it happened |
|---|
| 681 | if definition inherits BoxMember |
|---|
| 682 | if not .hasError and not hasErrors |
|---|
| 683 | args = _args |
|---|
| 684 | if definition inherits MemberOverload |
|---|
| 685 | # to-do: Correct this dependency; Need args bindImp done to calc bestOverload, |
|---|
| 686 | # but need best overload to infer (undefined out) variable type |
|---|
| 687 | if needInferOutArgs |
|---|
| 688 | moDefn = definition.members[0] # not guaranteed to have the best param list |
|---|
| 689 | if moDefn.hasParams |
|---|
| 690 | params = moDefn.params |
|---|
| 691 | _inferOutArgs(args, params, true) |
|---|
| 692 | |
|---|
| 693 | # Note that overloads can have different return types (even if more than just the return type is needed to distinguish them). Example: Math. |
|---|
| 694 | # Plenty of info here: |
|---|
| 695 | # http://www.google.com/search?hl=en&q=C%23+overloaded+method+resolution |
|---|
| 696 | # TODO: handle type inference for generic members. See the C# spec for details. |
|---|
| 697 | winner = definition.computeBestOverload(.args, .genericArgTypes, true) |
|---|
| 698 | sharp'definition = winner' |
|---|
| 699 | type = winner.resultType |
|---|
| 700 | else |
|---|
| 701 | type = definition.resultType |
|---|
| 702 | if not _didVariArgs(definition) |
|---|
| 703 | definition = _checkGenerics(definition, inout type) |
|---|
| 704 | if not definition.hasParams |
|---|
| 705 | _checkNoArgs(definition, args) |
|---|
| 706 | else |
|---|
| 707 | params = definition.params |
|---|
| 708 | if _checkParamsCount(definition, args, params) |
|---|
| 709 | if needInferOutArgs, _inferOutArgs(args, params, false) |
|---|
| 710 | _checkArgsAssignable(definition, args, params) |
|---|
| 711 | _checkInOutArguments |
|---|
| 712 | _checkVisibility(definition, dotNode.left) |
|---|
| 713 | else |
|---|
| 714 | throw FallThroughException(definition) |
|---|
| 715 | if type is nil, type = .compiler.passThroughType |
|---|
| 716 | _definition = definition |
|---|
| 717 | if _definition, _definition.isUsed = true |
|---|
| 718 | _type = type |
|---|
| 719 | assert _type, _definition |
|---|
| 720 | |
|---|
| 721 | if .args.count == 0 and .hasParens |
|---|
| 722 | # TODO: |
|---|
| 723 | # if .definition inherits BoxMember and .definition.isMethod and not dotNode.isImplicit |
|---|
| 724 | if (.definition inherits AbstractMethod or .definition inherits MemberOverload) and not dotNode.isImplicit |
|---|
| 725 | .compiler.warning(this, 'Unnecessary parentheses. You can remove them.') |
|---|
| 726 | else |
|---|
| 727 | for arg in _args |
|---|
| 728 | if arg inherits AssignExpr |
|---|
| 729 | .recordError('Cannot make assignments in arguments. This syntax is reserved in the future for keyword arguments.') |
|---|
| 730 | |
|---|
| 731 | ## _bindImp support |
|---|
| 732 | |
|---|
| 733 | def _checkNoArgs(definition as BoxMember, args as IList<of Expr>) |
|---|
| 734 | if .name == 'toString' |
|---|
| 735 | # HACK |
|---|
| 736 | # this enables someFloat.toString('0.0') |
|---|
| 737 | # can remove when primitives know their CLR types and look up their methods |
|---|
| 738 | pass |
|---|
| 739 | else if args.count |
|---|
| 740 | .throwError('The method "[definition.name]" is expecting 0 arguments, but [args.count] are being supplied in this call.') |
|---|
| 741 | |
|---|
| 742 | def _setGenericArgTypes |
|---|
| 743 | _genericArgTypes = List<of IType>() |
|---|
| 744 | for num, typeProxy in _genericArgProxies.numbered |
|---|
| 745 | try |
|---|
| 746 | _genericArgTypes.add(typeProxy.realType) |
|---|
| 747 | catch ne as NodeException |
|---|
| 748 | ne.prefixMessage('For "[_name]" type arg [num+1]: ') |
|---|
| 749 | .compiler.recordError(ne) |
|---|
| 750 | # TODO: Check types are compatible with call |
|---|
| 751 | |
|---|
| 752 | def _transformToEnumDecl(dotNode as DotExpr, enumDefi as EnumDecl) |
|---|
| 753 | # Foo.EnumType(MemberA, MemberB) |
|---|
| 754 | # Change to an EnumCallExpr |
|---|
| 755 | # Roll up Foo.Bar.Baz() into one EnumCallExpr |
|---|
| 756 | transformTarget = dotNode |
|---|
| 757 | while transformTarget.superNode inherits DotExpr |
|---|
| 758 | transformTarget = transformTarget.superNode to DotExpr |
|---|
| 759 | enumCall = EnumCallExpr(.token, _name, _args, enumDefi).bindImp |
|---|
| 760 | _type = enumCall.type to IType |
|---|
| 761 | transformTarget._transformTo(enumCall) |
|---|
| 762 | |
|---|
| 763 | def _inferDefinitionAndType(dotNode as DotExpr, type as out IType?) as IMember? |
|---|
| 764 | type = nil |
|---|
| 765 | possibleDefinition = dotNode.left.memberForName(_mangleName(.name)) |
|---|
| 766 | if possibleDefinition is nil |
|---|
| 767 | lrt = dotNode.left.receiverType |
|---|
| 768 | if lrt.isDynamic |
|---|
| 769 | type = .compiler.nilableDynamicType |
|---|
| 770 | else if lrt is .compiler.passThroughType |
|---|
| 771 | if _name == 'toString' |
|---|
| 772 | type = .compiler.stringType |
|---|
| 773 | else |
|---|
| 774 | type = .compiler.passThroughType |
|---|
| 775 | else |
|---|
| 776 | .throwCannotFindMemberError(dotNode.left, _name) |
|---|
| 777 | else |
|---|
| 778 | if not possibleDefinition.isCallable |
|---|
| 779 | .throwError('Cannot call "[_name]" because it is a "[possibleDefinition.englishName]".') |
|---|
| 780 | |
|---|
| 781 | type = possibleDefinition.resultType |
|---|
| 782 | return possibleDefinition |
|---|
| 783 | |
|---|
| 784 | def _transformToPostCallExprOnType(definition as IType?) |
|---|
| 785 | transformTarget = .superNode to DotExpr |
|---|
| 786 | postCall = PostCallExpr(.token, TypeExpr(.token, definition), .args).bindAll to PostCallExpr |
|---|
| 787 | _type = postCall.type |
|---|
| 788 | transformTarget._transformTo(postCall) |
|---|
| 789 | |
|---|
| 790 | def _undefinedOutArg(arg as Expr) as bool |
|---|
| 791 | if arg.direction == Direction.Out |
|---|
| 792 | if arg inherits IdentifierExpr |
|---|
| 793 | if not arg.findDefinition |
|---|
| 794 | return true |
|---|
| 795 | return false |
|---|
| 796 | |
|---|
| 797 | def _bindImpArgs(needInferOutArgs as out bool) as bool |
|---|
| 798 | """ |
|---|
| 799 | Do bindImp on callExprs args. |
|---|
| 800 | Defer binding on undefined out args but flag that is inference needed later (see _bindImp). |
|---|
| 801 | """ |
|---|
| 802 | hasErrors = needInferOutArgs = false |
|---|
| 803 | for num, arg in _args.clone.numbered |
|---|
| 804 | try |
|---|
| 805 | if arg inherits AssignExpr |
|---|
| 806 | arg.right.bindImp # 'x=y' has special treatment in arguments |
|---|
| 807 | else |
|---|
| 808 | if needInferOutArgs == false and _undefinedOutArg(arg) |
|---|
| 809 | needInferOutArgs = true |
|---|
| 810 | continue |
|---|
| 811 | _bindImpArg(num, arg) |
|---|
| 812 | catch ne as NodeException |
|---|
| 813 | ne.prefixMessage('For "[_name]" arg [num+1]: ') |
|---|
| 814 | .compiler.recordError(ne) |
|---|
| 815 | hasErrors = true |
|---|
| 816 | return hasErrors |
|---|
| 817 | |
|---|
| 818 | def _bindImpArg(num as int, arg as Expr) |
|---|
| 819 | boundArg = arg.bindImp |
|---|
| 820 | if boundArg is not arg # transformation |
|---|
| 821 | _args[num] = boundArg |
|---|
| 822 | assert boundArg.didBindImp |
|---|
| 823 | |
|---|
| 824 | def _didVariArgs(definition as BoxMember) as bool |
|---|
| 825 | hasVari = false |
|---|
| 826 | for param in definition.params |
|---|
| 827 | if param.type inherits VariType |
|---|
| 828 | hasVari = true |
|---|
| 829 | break |
|---|
| 830 | if hasVari |
|---|
| 831 | # TODO handle variable number of args (4) |
|---|
| 832 | return true |
|---|
| 833 | else |
|---|
| 834 | return false |
|---|
| 835 | |
|---|
| 836 | def _checkGenerics(definition as BoxMember, type as inout IType?) as BoxMember |
|---|
| 837 | if .genericArgTypes and .genericArgTypes.count |
|---|
| 838 | if definition inherits Method |
|---|
| 839 | definition = definition.constructedMethodWith(.genericArgTypes to !) |
|---|
| 840 | type = definition.resultType |
|---|
| 841 | else |
|---|
| 842 | .throwError('Cannot pass type arguments to "[definition.name]" because it is a [definition.getType.name].') # @@ _definition.englishName; also might need this error in other locations |
|---|
| 843 | return definition |
|---|
| 844 | |
|---|
| 845 | def _checkParamsCount(definition as BoxMember, args as IList<of Expr>, params as IList<of Param>) as bool |
|---|
| 846 | if args.count <> params.count |
|---|
| 847 | if _name=='toString' # TODO because many structs like Decimal have a toString() overload which cannot currently be expressed in SystemInterfaces.cobra |
|---|
| 848 | return false |
|---|
| 849 | else |
|---|
| 850 | .throwError('The method "[definition.name]" is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.') |
|---|
| 851 | return true |
|---|
| 852 | |
|---|
| 853 | def _inferOutArgs(args as IList<of Expr>, params as IList<of Param>, warn as bool) |
|---|
| 854 | for num, arg in args.numbered |
|---|
| 855 | if _undefinedOutArg(arg) |
|---|
| 856 | inferredType = params[num].type |
|---|
| 857 | newDefn = LocalVar(arg.token, inferredType).bindAll to LocalVar |
|---|
| 858 | .compiler.codeMemberStack.peek.addLocal(newDefn) |
|---|
| 859 | (arg to IdentifierExpr).setDefinition(newDefn) |
|---|
| 860 | arg.type = newDefn.type |
|---|
| 861 | _bindImpArg(num, arg) |
|---|
| 862 | # the warning below is temporary till we sort out arg.bindImp vs calculating Best overload |
|---|
| 863 | if warn |
|---|
| 864 | sugg = 'You may want to disambiguate this by declaring the variable explicitly.' |
|---|
| 865 | .compiler.warning(this, 'Cobra has implicitly declared a variable "[newDefn.name] as [newDefn.type.name]" for an undefined out parameter variable but the type inferred may not be correct as the method called is overloaded. [sugg]') |
|---|
| 866 | |
|---|
| 867 | def _checkArgsAssignable(definition as BoxMember, args as IList<of Expr>, params as IList<of Param>) |
|---|
| 868 | for i in args.count |
|---|
| 869 | arg = args[i] |
|---|
| 870 | param = params[i] |
|---|
| 871 | if arg.hasError |
|---|
| 872 | break |
|---|
| 873 | if arg inherits AssignExpr # assignments in arguments have special treatment |
|---|
| 874 | break |
|---|
| 875 | assert arg.didBindImp |
|---|
| 876 | assert param.didBindInt |
|---|
| 877 | # check any in/out/inout expectations are matched |
|---|
| 878 | if param.direction <> arg.direction |
|---|
| 879 | arg.recordError('Argument [i+1] of method "[_name]" expects [_toErrorPhrase(param.direction)] parameter, but the call is supplying [_toErrorPhrase(arg.direction)] one.') |
|---|
| 880 | if arg.canBeAssignedTo(param.type) |
|---|
| 881 | arg.contextType = param.type |
|---|
| 882 | else if param.type inherits GenericParam |
|---|
| 883 | # ..\Tests\240-generics\400-generic-methods\100-generic-method.cobra |
|---|
| 884 | # HACK for generic method args which need proper type checking like anything else |
|---|
| 885 | # Cobra needs to add type inference on generic method args instead of dropping this down to C# |
|---|
| 886 | pass |
|---|
| 887 | else |
|---|
| 888 | if false |
|---|
| 889 | print |
|---|
| 890 | print '<> definition = [definition]' |
|---|
| 891 | print '<> arg = ' stop |
|---|
| 892 | arg.writeDeepString |
|---|
| 893 | print '<> arg.type =', arg.type |
|---|
| 894 | print '<> param = ' stop |
|---|
| 895 | param.writeDeepString |
|---|
| 896 | print '<> param.type =', param.type |
|---|
| 897 | print '<> param.ifInheritsStack.count =', param.ifInheritsStack.count |
|---|
| 898 | if arg.type inherits NilableType and not param.type inherits NilableType and arg.type.nonNil.isAssignableTo(param.type) |
|---|
| 899 | arg.recordError('Argument [i+1] of method "[_name]" expects a non-nilable type ([param.type.name]), but the call is supplying a nilable type ([arg.type.name]).') |
|---|
| 900 | else |
|---|
| 901 | arg.recordError('Argument [i+1] of method "[_name]" expects type [param.type.name], but the call is supplying type [arg.type.name].') |
|---|
| 902 | |
|---|
| 903 | def _toErrorPhrase(d as Direction) as String |
|---|
| 904 | branch d |
|---|
| 905 | on Direction.In, return 'a regular' |
|---|
| 906 | on Direction.Out, return 'an "out"' |
|---|
| 907 | on Direction.InOut, return 'an "inout"' |
|---|
| 908 | else, throw FallThroughException(d) |
|---|
| 909 | |
|---|
| 910 | def _checkInOutArguments |
|---|
| 911 | for arg in .args |
|---|
| 912 | if arg.direction <> Direction.In |
|---|
| 913 | if not arg.hasError |
|---|
| 914 | error as String? |
|---|
| 915 | argDefi = arg.definition |
|---|
| 916 | if argDefi inherits Property, error = 'A property' |
|---|
| 917 | else if argDefi inherits Indexer, error = 'An indexer' |
|---|
| 918 | if error, arg.recordError('[error] cannot be passed as an "[arg.direction.toString.toLower]" parameter.') |
|---|
| 919 | |
|---|
| 920 | def toCobraSource as String is override |
|---|
| 921 | sb = StringBuilder() |
|---|
| 922 | sb.append(_name) |
|---|
| 923 | sb.append('(') |
|---|
| 924 | sep = '' |
|---|
| 925 | for arg in _args |
|---|
| 926 | sb.append(sep) |
|---|
| 927 | sb.append(arg.toCobraSource) |
|---|
| 928 | sep = ', ' |
|---|
| 929 | sb.append(')') |
|---|
| 930 | return sb.toString |
|---|
| 931 | |
|---|
| 932 | |
|---|
| 933 | class EnumCallExpr |
|---|
| 934 | is partial |
|---|
| 935 | inherits Expr |
|---|
| 936 | """ |
|---|
| 937 | Represents an enumeration call like: AnchorStyle(Left, Right) |
|---|
| 938 | Created by CallExpr._bindImp |
|---|
| 939 | """ |
|---|
| 940 | |
|---|
| 941 | var _name as String |
|---|
| 942 | var _args as List<of Expr> |
|---|
| 943 | var _definition as EnumDecl? |
|---|
| 944 | var _members as List<of EnumMember> |
|---|
| 945 | """ |
|---|
| 946 | Parallels the args with the enum members found during _bindImp. |
|---|
| 947 | """ |
|---|
| 948 | |
|---|
| 949 | cue init(token as IToken, name as String, args as List<of Expr>, definition as EnumDecl) |
|---|
| 950 | require name.isCapitalized |
|---|
| 951 | base.init(token) |
|---|
| 952 | _name = name |
|---|
| 953 | _args = args |
|---|
| 954 | _definition = definition |
|---|
| 955 | _members = List<of EnumMember>() |
|---|
| 956 | |
|---|
| 957 | get allExprs as Expr* |
|---|
| 958 | for expr in base.allExprs, yield expr |
|---|
| 959 | for arg in .args, for expr in arg.allExprs, yield expr |
|---|
| 960 | |
|---|
| 961 | get name from var |
|---|
| 962 | |
|---|
| 963 | get args from var |
|---|
| 964 | has Subnodes |
|---|
| 965 | |
|---|
| 966 | get definition is override |
|---|
| 967 | return _definition |
|---|
| 968 | |
|---|
| 969 | get enumDefinition from _definition |
|---|
| 970 | |
|---|
| 971 | get members from _members |
|---|
| 972 | |
|---|
| 973 | def toCobraSource as String is override |
|---|
| 974 | sb = StringBuilder() |
|---|
| 975 | sb.append(_name) |
|---|
| 976 | sb.append('(') |
|---|
| 977 | sep = '' |
|---|
| 978 | for arg in _args |
|---|
| 979 | sb.append(sep) |
|---|
| 980 | sb.append(arg.toCobraSource) |
|---|
| 981 | sep = ', ' |
|---|
| 982 | sb.append(')') |
|---|
| 983 | return sb.toString |
|---|
| 984 | |
|---|
| 985 | def _bindImp |
|---|
| 986 | base._bindImp |
|---|
| 987 | if not _definition |
|---|
| 988 | possible = .compiler.symbolForName(.name, false) |
|---|
| 989 | if not possible |
|---|
| 990 | .throwError('Cannot locate enumeration type "[.name]".') |
|---|
| 991 | else if possible inherits EnumDecl |
|---|
| 992 | _definition = possible |
|---|
| 993 | else |
|---|
| 994 | .throwError('"[.name]" is not an enumeration type.') |
|---|
| 995 | if not _type |
|---|
| 996 | _type = _definition |
|---|
| 997 | # TODO: support EnumName(someInt) and EnumName(someString) (and multiple args) |
|---|
| 998 | for arg in _args |
|---|
| 999 | if arg inherits IdentifierExpr |
|---|
| 1000 | member = _definition.memberForName(arg.name) to EnumMember? |
|---|
| 1001 | if member |
|---|
| 1002 | arg.setDefinition(member) |
|---|
| 1003 | _members.add(member) # for use in code generation |
|---|
| 1004 | else |
|---|
| 1005 | arg.recordError('Cannot find "[arg.name]" in enumeration "[.name]"') |
|---|
| 1006 | |
|---|
| 1007 | |
|---|
| 1008 | class ForExpr inherits Expr is partial |
|---|
| 1009 | |
|---|
| 1010 | var _nameExpr as NameExpr |
|---|
| 1011 | var _var as IVar? |
|---|
| 1012 | var _varNumber as int |
|---|
| 1013 | var _what as Expr |
|---|
| 1014 | var _whereExpr as Expr? |
|---|
| 1015 | var _getExpr as Expr |
|---|
| 1016 | var _start as Expr? |
|---|
| 1017 | var _stop as Expr? |
|---|
| 1018 | var _step as Expr? |
|---|
| 1019 | |
|---|
| 1020 | cue init(token as IToken, nameExpr as NameExpr, what as Expr, stopExpr as Expr?, |
|---|
| 1021 | stepExpr as Expr?, whereExpr as Expr?, getExpr as Expr) |
|---|
| 1022 | base.init(token) |
|---|
| 1023 | _nameExpr = nameExpr |
|---|
| 1024 | _what = what |
|---|
| 1025 | _stop = stopExpr |
|---|
| 1026 | _step = stepExpr |
|---|
| 1027 | _whereExpr = whereExpr |
|---|
| 1028 | _getExpr = getExpr |
|---|
| 1029 | |
|---|
| 1030 | def addSubFields |
|---|
| 1031 | base.addSubFields |
|---|
| 1032 | .addField('nameExpr', _nameExpr) |
|---|
| 1033 | .addField('var', _var) |
|---|
| 1034 | .addField('what', _what) |
|---|
| 1035 | .addField('whereExpr', _whereExpr) |
|---|
| 1036 | .addField('getExpr', _getExpr) |
|---|
| 1037 | .addField('start', _start) |
|---|
| 1038 | .addField('stop', _stop) |
|---|
| 1039 | .addField('step', _step) |
|---|
| 1040 | |
|---|
| 1041 | get allExprs as Expr* |
|---|
| 1042 | for expr in base.allExprs, yield expr |
|---|
| 1043 | for expr in .nameExpr.allExprs, yield expr |
|---|
| 1044 | for expr in .what.allExprs, yield expr |
|---|
| 1045 | if .whereExpr, for expr in .whereExpr.allExprs, yield expr |
|---|
| 1046 | for expr in .getExpr.allExprs, yield expr |
|---|
| 1047 | if .start, for expr in .start.allExprs, yield expr |
|---|
| 1048 | if .stop, for expr in .stop.allExprs, yield expr |
|---|
| 1049 | if .step, for expr in .step.allExprs, yield expr |
|---|
| 1050 | |
|---|
| 1051 | get nameExpr from _nameExpr |
|---|
| 1052 | |
|---|
| 1053 | get what from _what |
|---|
| 1054 | |
|---|
| 1055 | get whereExpr from _whereExpr |
|---|
| 1056 | |
|---|
| 1057 | get getExpr from _getExpr |
|---|
| 1058 | |
|---|
| 1059 | get start from _start |
|---|
| 1060 | |
|---|
| 1061 | get stop from _stop |
|---|
| 1062 | |
|---|
| 1063 | get step from _step |
|---|
| 1064 | |
|---|
| 1065 | get willChangeVar as bool is override |
|---|
| 1066 | # TODO: ack. kind of weird. if the variable is a new one and its not read afterwards (without first being set) then no side effects. |
|---|
| 1067 | if _what.willChangeVar, return true |
|---|
| 1068 | if _whereExpr and _whereExpr.willChangeVar, return true |
|---|
| 1069 | if _getExpr.willChangeVar, return true |
|---|
| 1070 | return false |
|---|
| 1071 | |
|---|
| 1072 | def _bindImp |
|---|
| 1073 | base._bindImp |
|---|
| 1074 | _what.bindImp # bind first because it may be needed for type inference |
|---|
| 1075 | whatType = _what.type to ! |
|---|
| 1076 | if whatType inherits AnyIntType |
|---|
| 1077 | if _stop |
|---|
| 1078 | _start = _what |
|---|
| 1079 | _stop.bindImp |
|---|
| 1080 | else |
|---|
| 1081 | _start = IntegerLit(.token.copy('INTEGER_LIT', '0'), 0).bindImp |
|---|
| 1082 | _stop = _what |
|---|
| 1083 | if not _step |
|---|
| 1084 | _step = IntegerLit(.token.copy('INTEGER_LIT', '1'), 1) |
|---|
| 1085 | _step.bindImp |
|---|
| 1086 | else if not whatType.isEnumerable |
|---|
| 1087 | .throwError('Cannot enumerate values of type "[whatType.name]". You can enumerate anything enumerable (IEnumerable, IList, arrays, strings, etc.).') |
|---|
| 1088 | _var = .bindVar(_nameExpr) |
|---|
| 1089 | if _nameExpr.definition |
|---|
| 1090 | if _nameExpr.definition inherits IVar |
|---|
| 1091 | _var = _nameExpr.definition |
|---|
| 1092 | else |
|---|
| 1093 | .throwError('Expecting a variable not a [_nameExpr.definition.getType.name].') # TODO: what's the best way to report what was found? |
|---|
| 1094 | else |
|---|
| 1095 | assert _nameExpr.hasError, _nameExpr |
|---|
| 1096 | _varNumber = .compiler.curBox.makeNextPrivateSerialNumber |
|---|
| 1097 | if _whereExpr |
|---|
| 1098 | _whereExpr.bindImp |
|---|
| 1099 | if _whereExpr.type is not .compiler.boolType |
|---|
| 1100 | _whereExpr = TruthExpr(_whereExpr).bindAll to TruthExpr # CC: axe cast when have "as same" |
|---|
| 1101 | _getExpr.bindImp |
|---|
| 1102 | ilist = .compiler.listOfType |
|---|
| 1103 | _type = ilist.constructedTypeFor([_getExpr.type to !]) |
|---|
| 1104 | |
|---|
| 1105 | def inferredType as IType? is override |
|---|
| 1106 | assert _what.type |
|---|
| 1107 | return if(_what.type inherits AnyIntType, _what.type, _what.type.innerType) |
|---|
| 1108 | |
|---|
| 1109 | |
|---|
| 1110 | class IdentifierExpr |
|---|
| 1111 | is partial |
|---|
| 1112 | inherits NameExpr |
|---|
| 1113 | implements IPotentialTypeExpr |
|---|
| 1114 | |
|---|
| 1115 | cue init(token as IToken) |
|---|
| 1116 | .init(token, token.text) |
|---|
| 1117 | |
|---|
| 1118 | cue init(token as IToken, name as String) |
|---|
| 1119 | base.init(token) |
|---|
| 1120 | _name = name |
|---|
| 1121 | |
|---|
| 1122 | cue init(token as IToken, definition as INamedNode) |
|---|
| 1123 | base.init(token) |
|---|
| 1124 | _name = definition.name |
|---|
| 1125 | _definition = definition |
|---|
| 1126 | |
|---|
| 1127 | def setDefinition(value as INamedNode?) |
|---|
| 1128 | # cannot override definition with a set, because base does not do a set |
|---|
| 1129 | _definition = value |
|---|
| 1130 | |
|---|
| 1131 | get canBeStatement as bool is override |
|---|
| 1132 | return _definition inherits Method |
|---|
| 1133 | |
|---|
| 1134 | get isCalling as bool is override |
|---|
| 1135 | assert _definition |
|---|
| 1136 | return _definition.isMethod |
|---|
| 1137 | |
|---|
| 1138 | get isTypeReference as bool |
|---|
| 1139 | """ |
|---|
| 1140 | Returns true if this identifier directly names a type. |
|---|
| 1141 | """ |
|---|
| 1142 | require .didBindImp |
|---|
| 1143 | return .definition implements IType and .type.isDescendantOf(.compiler.typeType) |
|---|
| 1144 | |
|---|
| 1145 | get potentialType as IType? |
|---|
| 1146 | # overridden to return the type this identifier represents in those cases when it does represent a type such an "int" or a class |
|---|
| 1147 | assert .didBindImp |
|---|
| 1148 | assert .type |
|---|
| 1149 | assert .compiler |
|---|
| 1150 | if .type.isDescendantOf(.compiler.typeType) or .type inherits Box |
|---|
| 1151 | assert .definition |
|---|
| 1152 | defi = .definition |
|---|
| 1153 | if defi inherits IType |
|---|
| 1154 | return defi |
|---|
| 1155 | else if defi inherits BoxEvent |
|---|
| 1156 | # TODO: does execution ever get here? |
|---|
| 1157 | return defi.handlerType |
|---|
| 1158 | else |
|---|
| 1159 | return nil |
|---|
| 1160 | else |
|---|
| 1161 | return nil |
|---|
| 1162 | |
|---|
| 1163 | def _bindImp is override |
|---|
| 1164 | base._bindImp |
|---|
| 1165 | if _definition is nil |
|---|
| 1166 | _definition = .findDefinition |
|---|
| 1167 | canBeUndottedMember = _name.canBeUndottedMemberName |
|---|
| 1168 | if _definition is nil and not canBeUndottedMember |
|---|
| 1169 | if _superNode inherits BinaryOpExpr |
|---|
| 1170 | if _superNode.right is this |
|---|
| 1171 | curBox = .compiler.boxStack.peek |
|---|
| 1172 | definition = curBox.symbolForName(_name, true) |
|---|
| 1173 | if definition |
|---|
| 1174 | .throwError('You must refer to non-underscored members with a leading dot (.). Member name is "[_name]".') |
|---|
| 1175 | #print _name, canBeUndottedMember, _definition # hops |
|---|
| 1176 | if _definition is nil and (not _superNode inherits BinaryOpExpr or .binarySuperNode.op <> 'ASSIGN') |
|---|
| 1177 | .throwUnknownIdError |
|---|
| 1178 | throw FallThroughException() |
|---|
| 1179 | if _type is nil |
|---|
| 1180 | if _definition |
|---|
| 1181 | if _definition inherits AbstractMethod or _definition inherits MemberOverload |
|---|
| 1182 | _transformTo(DotExpr(.token, 'DOT', ThisLit(.token, isImplicit=true), MemberExpr(.token), isImplicit=true).bindAll) |
|---|
| 1183 | else |
|---|
| 1184 | _type = _definition.typeForIdentifier |
|---|
| 1185 | _receiverType = _definition.typeForReceiver |
|---|
| 1186 | else |
|---|
| 1187 | if .binarySuperNode inherits AssignExpr and this is .binarySuperNode.left |
|---|
| 1188 | pass # let the AssignExpr have its chance at type inference |
|---|
| 1189 | else |
|---|
| 1190 | .throwUnknownIdError |
|---|
| 1191 | throw FallThroughException() |
|---|
| 1192 | |
|---|
| 1193 | def findDefinition as INamedNode? |
|---|
| 1194 | definition as INamedNode? |
|---|
| 1195 | canBeUndottedMember = _name.canBeUndottedMemberName |
|---|
| 1196 | if canBeUndottedMember |
|---|
| 1197 | # assert .compiler.boxStack.count TODO: identifier expr is being used by PostCallExpr which is used for attribute calls |
|---|
| 1198 | definition = .compiler.symbolForName(_name, false) |
|---|
| 1199 | else |
|---|
| 1200 | # local var ref: foo |
|---|
| 1201 | if .compiler.codeMemberStack.count # could be: var _x = y or: has foo |
|---|
| 1202 | definition = .compiler.findLocal(_name) |
|---|
| 1203 | if definition is nil |
|---|
| 1204 | definition = .compiler.symbolForName(_name, false, true) # 3rd party DLLs have lowercase class names like iConnection |
|---|
| 1205 | return definition |
|---|
| 1206 | |
|---|
| 1207 | def postBindImp is override |
|---|
| 1208 | if _definition and not (.superNode inherits AbstractAssignExpr and .binarySuperNode.left is this) |
|---|
| 1209 | _definition.isUsed = true |
|---|
| 1210 | .help |
|---|
| 1211 | |
|---|
| 1212 | def throwUnknownIdError |
|---|
| 1213 | require .name.length |
|---|
| 1214 | name = .name |
|---|
| 1215 | msg = 'Cannot find "[name]".' |
|---|
| 1216 | sug = .compiler.suggestionForUnknown(name) |
|---|
| 1217 | if sug and sug.length |
|---|
| 1218 | msg += ' Maybe you should try "[sug]".' |
|---|
| 1219 | else |
|---|
| 1220 | # TODO: check for a local with same name but different case |
|---|
| 1221 | if .compiler.boxStack.count # could be empty stack for assembly level attributes |
|---|
| 1222 | sugg = _suggestionsMessage(.compiler.curBox.suggestionsForBadMemberName(name)) |
|---|
| 1223 | # example suggs: |
|---|
| 1224 | # There is a member named "x" with a similar name. |
|---|
| 1225 | # There are members with similar names including "x" and "y". |
|---|
| 1226 | assert sugg.length implies sugg.endsWith('.') |
|---|
| 1227 | msg += sugg |
|---|
| 1228 | .throwError(msg) |
|---|
| 1229 | |
|---|
| 1230 | def postBindImpAssertType is override |
|---|
| 1231 | # TODO: document why this is disabled |
|---|
| 1232 | pass |
|---|
| 1233 | |
|---|
| 1234 | def toCobraSource as String is override |
|---|
| 1235 | return _name |
|---|
| 1236 | |
|---|
| 1237 | def whoseTypeIsMessage as String |
|---|
| 1238 | """ |
|---|
| 1239 | Customized to return '' for boxes. |
|---|
| 1240 | """ |
|---|
| 1241 | # TODO: should cover other types like `int`. maybe that's a TypeExpr? |
|---|
| 1242 | return if(.definition inherits Box, '', base.whoseTypeIsMessage) |
|---|
| 1243 | |
|---|
| 1244 | |
|---|
| 1245 | class IfExpr |
|---|
| 1246 | is partial |
|---|
| 1247 | inherits Expr |
|---|
| 1248 | |
|---|
| 1249 | var _cond as Expr |
|---|
| 1250 | var _tpart as Expr |
|---|
| 1251 | var _fpart as Expr |
|---|
| 1252 | |
|---|
| 1253 | cue init(token as IToken, cond as Expr, tpart as Expr, fpart as Expr) |
|---|
| 1254 | ensure .cond == cond and .tpart == tpart and .fpart == fpart |
|---|
| 1255 | base.init(token) |
|---|
| 1256 | _cond, _tpart, _fpart = cond, tpart, fpart |
|---|
| 1257 | |
|---|
| 1258 | get allExprs as Expr* |
|---|
| 1259 | for expr in base.allExprs, yield expr |
|---|
| 1260 | for expr in .cond.allExprs, yield expr |
|---|
| 1261 | for expr in .tpart.allExprs, yield expr |
|---|
| 1262 | for expr in .fpart.allExprs, yield expr |
|---|
| 1263 | |
|---|
| 1264 | get cond from var |
|---|
| 1265 | |
|---|
| 1266 | get tpart from var |
|---|
| 1267 | |
|---|
| 1268 | get fpart from var |
|---|
| 1269 | |
|---|
| 1270 | def addSubFields is override |
|---|
| 1271 | base.addSubFields |
|---|
| 1272 | .addField('cond', .cond) |
|---|
| 1273 | .addField('tpart', .tpart) |
|---|
| 1274 | .addField('fpart', .fpart) |
|---|
| 1275 | |
|---|
| 1276 | get willChangeVar as bool is override |
|---|
| 1277 | if base.willChangeVar, return true |
|---|
| 1278 | if .cond.willChangeVar, return true |
|---|
| 1279 | if .tpart.willChangeVar, return true |
|---|
| 1280 | if .fpart.willChangeVar, return true |
|---|
| 1281 | return false |
|---|
| 1282 | |
|---|
| 1283 | def _bindImp is override |
|---|
| 1284 | and ensure |
|---|
| 1285 | .cond.hasError or .tpart.hasError or .fpart.hasError implies .hasError |
|---|
| 1286 | .hasError implies .type == .compiler.passThroughType |
|---|
| 1287 | body |
|---|
| 1288 | base._bindImp |
|---|
| 1289 | hadError = false |
|---|
| 1290 | try |
|---|
| 1291 | _cond.bindImp |
|---|
| 1292 | catch ne as NodeException |
|---|
| 1293 | .compiler.recordError(ne) |
|---|
| 1294 | hadError = true |
|---|
| 1295 | if not hadError and not _cond.type.isDescendantOf(.compiler.boolType) |
|---|
| 1296 | _cond = TruthExpr(_cond).bindAll to TruthExpr # CC: axe cast when "as same" |
|---|
| 1297 | try |
|---|
| 1298 | _tpart.bindImp |
|---|
| 1299 | catch ne as NodeException |
|---|
| 1300 | .compiler.recordError(ne) |
|---|
| 1301 | hadError = true |
|---|
| 1302 | try |
|---|
| 1303 | _fpart.bindImp |
|---|
| 1304 | catch ne as NodeException |
|---|
| 1305 | .compiler.recordError(ne) |
|---|
| 1306 | hadError = true |
|---|
| 1307 | if hadError |
|---|
| 1308 | _type = .compiler.passThroughType |
|---|
| 1309 | else |
|---|
| 1310 | assert _tpart.type |
|---|
| 1311 | assert _fpart.type |
|---|
| 1312 | _type = _tpart.type.greatestCommonDenominatorWith(_fpart.type to !) |
|---|
| 1313 | |
|---|
| 1314 | def toCobraSource as String is override |
|---|
| 1315 | return 'if([.cond.toCobraSource], [.tpart.toCobraSource], [.fpart.toCobraSource])' |
|---|
| 1316 | |
|---|
| 1317 | |
|---|
| 1318 | class IndexExpr inherits Expr is partial |
|---|
| 1319 | """ |
|---|
| 1320 | May transform to a TypeExpr for cases like "int[]". |
|---|
| 1321 | """ |
|---|
| 1322 | |
|---|
| 1323 | var _target as Expr |
|---|
| 1324 | var _args as List<of Expr> |
|---|
| 1325 | var _definition as IMember? |
|---|
| 1326 | |
|---|
| 1327 | cue init(token as IToken, target as Expr, args as List<of Expr>) |
|---|
| 1328 | base.init(token) |
|---|
| 1329 | _target, _args = target, args |
|---|
| 1330 | |
|---|
| 1331 | def addSubFields is override |
|---|
| 1332 | base.addSubFields |
|---|
| 1333 | .addField('target', _target) |
|---|
| 1334 | .addField('args', _args) |
|---|
| 1335 | |
|---|
| 1336 | get allExprs as Expr* |
|---|
| 1337 | for expr in base.allExprs, yield expr |
|---|
| 1338 | for expr in .target.allExprs, yield expr |
|---|
| 1339 | for arg in .args, for expr in arg.allExprs, yield expr |
|---|
| 1340 | |
|---|
| 1341 | get definition is override |
|---|
| 1342 | return _definition |
|---|
| 1343 | |
|---|
| 1344 | get memberDefinition from _definition |
|---|
| 1345 | |
|---|
| 1346 | get target from var |
|---|
| 1347 | |
|---|
| 1348 | get args from var |
|---|
| 1349 | has Subnodes |
|---|
| 1350 | |
|---|
| 1351 | get willChangeVar as bool is override |
|---|
| 1352 | if _target.willChangeVar, return true |
|---|
| 1353 | for arg in _args, if arg.willChangeVar, return true |
|---|
| 1354 | return false |
|---|
| 1355 | |
|---|
| 1356 | def _bindImp is override |
|---|
| 1357 | base._bindImp |
|---|
| 1358 | _target.bindImp |
|---|
| 1359 | args = _args |
|---|
| 1360 | if args.count == 0 |
|---|
| 1361 | if _target.isKindOf(.compiler.typeType) |
|---|
| 1362 | if _target inherits IPotentialTypeExpr and (pt = (_target to IPotentialTypeExpr).potentialType) |
|---|
| 1363 | _transformTo(TypeExpr(.token, .typeProvider.arrayType(pt to !))) |
|---|
| 1364 | return |
|---|
| 1365 | else |
|---|
| 1366 | .throwError('Unknown array type.') |
|---|
| 1367 | else |
|---|
| 1368 | .throwError('Invalid index expression or array type.') |
|---|
| 1369 | hasArgError = false |
|---|
| 1370 | for arg in args |
|---|
| 1371 | try |
|---|
| 1372 | arg.bindImp |
|---|
| 1373 | catch ne as NodeException |
|---|
| 1374 | hasArgError = true |
|---|
| 1375 | .compiler.recordError(ne) |
|---|
| 1376 | target = .target |
|---|
| 1377 | if hasArgError or target.hasError |
|---|
| 1378 | _type = .compiler.passThroughType |
|---|
| 1379 | else if _type is nil |
|---|
| 1380 | _definition = target.memberForName(r'[]') |
|---|
| 1381 | if _definition is nil |
|---|
| 1382 | if target.receiverType is .compiler.passThroughType |
|---|
| 1383 | _type = .compiler.passThroughType |
|---|
| 1384 | return |
|---|
| 1385 | if target.receiverType.isDynamic |
|---|
| 1386 | _type = .compiler.nilableDynamicType |
|---|
| 1387 | return |
|---|
| 1388 | .throwError('Cannot find an indexer in "[target.toCobraSource]" whose type is "[target.receiverType.name]".') |
|---|
| 1389 | assert _definition |
|---|
| 1390 | if _definition inherits MemberOverload |
|---|
| 1391 | # to-do |
|---|
| 1392 | pass |
|---|
| 1393 | else if _definition inherits Indexer |
|---|
| 1394 | _bindImpIndexer(args, _definition) |
|---|
| 1395 | else |
|---|
| 1396 | throw FallThroughException(_definition) |
|---|
| 1397 | _type = _definition.resultType |
|---|
| 1398 | assert _type |
|---|
| 1399 | |
|---|
| 1400 | def _bindImpIndexer(args as List<of Expr>, indexer as Indexer) |
|---|
| 1401 | params = indexer.params |
|---|
| 1402 | hasVari = any for p in params get p.type inherits VariType |
|---|
| 1403 | if hasVari |
|---|
| 1404 | # to-do |
|---|
| 1405 | return |
|---|
| 1406 | |
|---|
| 1407 | if not hasVari and args.count <> params.count |
|---|
| 1408 | .throwError('The indexer is expecting [params.count] argument[Utils.plural(params)], but [args.count] are being supplied in this call.') |
|---|
| 1409 | for i in 0 : args.count |
|---|
| 1410 | arg = args[i] |
|---|
| 1411 | param = params[i] |
|---|
| 1412 | if arg.hasError, break |
|---|
| 1413 | assert arg.didBindImp, arg |
|---|
| 1414 | assert param.didBindInt, param |
|---|
| 1415 | if arg.canBeAssignedTo(param.type) |
|---|
| 1416 | arg.contextType = param.type |
|---|
| 1417 | else |
|---|
| 1418 | if false |
|---|
| 1419 | print '<> arg = ' stop |
|---|
| 1420 | arg.writeDeepString |
|---|
| 1421 | print '<> param = ' stop |
|---|
| 1422 | param.writeDeepString |
|---|
| 1423 | if arg.type inherits NilableType and not param.type inherits NilableType and arg.type.nonNil.isAssignableTo(param.type) |
|---|
| 1424 | .throwError('Argument [i+1] of indexer expects a non-nilable type ([param.type.name]), but the call is supplying a nilable type ([arg.type.name]).') |
|---|
| 1425 | else |
|---|
| 1426 | .throwError('Argument [i+1] of indexer expects type [param.type.name], but the call is supplying type [arg.type.name].') |
|---|
| 1427 | |
|---|
| 1428 | def toCobraSource as String is override |
|---|
| 1429 | sb = StringBuilder() |
|---|
| 1430 | sb.append(_target.toCobraSource) |
|---|
| 1431 | sb.append(r'[') |
|---|
| 1432 | sep = '' |
|---|
| 1433 | for arg in _args |
|---|
| 1434 | sb.append(sep) |
|---|
| 1435 | sb.append(arg.toCobraSource) |
|---|
| 1436 | sep = ', ' |
|---|
| 1437 | sb.append(']') |
|---|
| 1438 | return sb.toString |
|---|
| 1439 | |
|---|
| 1440 | |
|---|
| 1441 | class IsNilExpr |
|---|
| 1442 | is partial |
|---|
| 1443 | inherits Expr |
|---|
| 1444 | |
|---|
| 1445 | var _expr as Expr |
|---|
| 1446 | get expr from var |
|---|
| 1447 | |
|---|
| 1448 | |
|---|
| 1449 | cue init(token as IToken, expr as Expr) |
|---|
| 1450 | base.init(token) |
|---|
| 1451 | _expr = expr |
|---|
| 1452 | |
|---|
| 1453 | get allExprs as Expr* |
|---|
| 1454 | for expr in base.allExprs, yield expr |
|---|
| 1455 | for expr in .expr.allExprs, yield expr |
|---|
| 1456 | |
|---|
| 1457 | get willChangeVar as bool is override |
|---|
| 1458 | if _expr.willChangeVar, return true |
|---|
| 1459 | return false |
|---|
| 1460 | |
|---|
| 1461 | def _bindImp is override |
|---|
| 1462 | base._bindImp |
|---|
| 1463 | _expr.bindImp |
|---|
| 1464 | _type = .compiler.boolType |
|---|
| 1465 | |
|---|
| 1466 | |
|---|
| 1467 | class IsNotNilExpr |
|---|
| 1468 | is partial |
|---|
| 1469 | inherits Expr |
|---|
| 1470 | |
|---|
| 1471 | var _expr as Expr |
|---|
| 1472 | |
|---|
| 1473 | cue init(token as IToken, expr as Expr) |
|---|
| 1474 | base.init(token) |
|---|
| 1475 | _expr = expr |
|---|
| 1476 | |
|---|
| 1477 | get allExprs as Expr* |
|---|
| 1478 | for expr in base.allExprs, yield expr |
|---|
| 1479 | for expr in .expr.allExprs, yield expr |
|---|
| 1480 | |
|---|
| 1481 | get expr from var |
|---|
| 1482 | |
|---|
| 1483 | get willChangeVar as bool is override |
|---|
| 1484 | if _expr.willChangeVar, return true |
|---|
| 1485 | return false |
|---|
| 1486 | |
|---|
| 1487 | def _bindImp is override |
|---|
| 1488 | base._bindImp |
|---|
| 1489 | _expr.bindImp |
|---|
| 1490 | _type = .compiler.boolType |
|---|
| 1491 | |
|---|
| 1492 | def toCobraSource as String is override |
|---|
| 1493 | return '[_expr.toCobraSource] is not nil' |
|---|
| 1494 | |
|---|
| 1495 | |
|---|
| 1496 | class MemberExpr inherits CallOrMemberExpr implements IDotRightExpr is partial |
|---|
| 1497 | """ |
|---|
| 1498 | Example members are fields, properties and methods without arguments. |
|---|
| 1499 | |
|---|
| 1500 | A trace of .definiton.getType.name at the end of _bindImp gives the set: |
|---|
| 1501 | BoxVar |
|---|
| 1502 | Class |
|---|
| 1503 | EnumMember |
|---|
| 1504 | Initializer |
|---|
| 1505 | Interface |
|---|
| 1506 | MemberOverload |
|---|
| 1507 | Method |
|---|
| 1508 | NameSpace |
|---|
| 1509 | Property |
|---|
| 1510 | |
|---|
| 1511 | Presumable Struct could be in there as well. |
|---|
| 1512 | |
|---|
| 1513 | Note that DotExpr will do some transformations such as Foo.Bar to a TypeExpr if Bar is a type. |
|---|
| 1514 | """ |
|---|
| 1515 | |
|---|
| 1516 | var _definition as IMember? |
|---|
| 1517 | var _isReference as bool |
|---|
| 1518 | |
|---|
| 1519 | cue init(token as IToken) |
|---|
| 1520 | .init(token, token.text) |
|---|
| 1521 | |
|---|
| 1522 | cue init(token as IToken, name as String) |
|---|
| 1523 | base.init(token, name) |
|---|
| 1524 | |
|---|
| 1525 | def addMinFields is override |
|---|
| 1526 | base.addMinFields |
|---|
| 1527 | .addField('name', _name) |
|---|
| 1528 | .addField('isReference', _isReference) |
|---|
| 1529 | |
|---|
| 1530 | get args as List<of Expr> |
|---|
| 1531 | return List<of Expr>() |
|---|
| 1532 | |
|---|
| 1533 | get definition is override |
|---|
| 1534 | return _definition |
|---|
| 1535 | |
|---|
| 1536 | get memberDefinition from _definition |
|---|
| 1537 | |
|---|
| 1538 | pro isReference from var |
|---|
| 1539 | """ |
|---|
| 1540 | Returns true if this member expression is a _reference_ to a member instead of an invocation to it. |
|---|
| 1541 | This is set by RefExpr and otherwise is false. |
|---|
| 1542 | """ |
|---|
| 1543 | |
|---|
| 1544 | get isCalling as bool is override |
|---|
| 1545 | assert _definition |
|---|
| 1546 | return _definition.isMethod |
|---|
| 1547 | |
|---|
| 1548 | def _bindImp is override |
|---|
| 1549 | base._bindImp |
|---|
| 1550 | assert .superNode inherits DotExpr |
|---|
| 1551 | assert .binarySuperNode.op == 'DOT' |
|---|
| 1552 | assert .binarySuperNode.right is this |
|---|
| 1553 | left = .binarySuperNode.left |
|---|
| 1554 | if _definition is nil or _type is nil |
|---|
| 1555 | if not .binarySuperNode.left.didBindImp |
|---|
| 1556 | assert .binarySuperNode.left.hasError, .binarySuperNode.left |
|---|
| 1557 | # we get here for Cobra code like "obj.foo.bar" where "foo" is not found |
|---|
| 1558 | _type = .compiler.passThroughType |
|---|
| 1559 | return |
|---|
| 1560 | _definition = .binarySuperNode.left.memberForName(_mangleName(.name)) |
|---|
| 1561 | if _definition |
|---|
| 1562 | _definition.isUsed = true |
|---|
| 1563 | else |
|---|
| 1564 | if .binarySuperNode.left.receiverType is .compiler.passThroughType |
|---|
| 1565 | _type = .compiler.passThroughType |
|---|
| 1566 | return |
|---|
| 1567 | if .binarySuperNode.left.receiverType.isDynamic |
|---|
| 1568 | _type = .compiler.nilableDynamicType |
|---|
| 1569 | return |
|---|
| 1570 | .throwCannotFindMemberError(left, _name) |
|---|
| 1571 | assert _definition |
|---|
| 1572 | if _definition inherits IType |
|---|
| 1573 | effectiveType as IType = .compiler.typeType # namespace, class, interface |
|---|
| 1574 | receiverType as IType? = _definition |
|---|
| 1575 | else if _definition inherits MemberOverload and _definition.name == 'getType' |
|---|
| 1576 | # see Box.prepSystemObjectClass |
|---|
| 1577 | if .binarySuperNode.left.type.isSystemTypeClass |
|---|
| 1578 | sysType = .compiler.typeType |
|---|
| 1579 | for member in (_definition to MemberOverload).members |
|---|
| 1580 | if member.isShared and member.resultType is sysType and member.params.count == 0 |
|---|
| 1581 | _definition = member |
|---|
| 1582 | break |
|---|
| 1583 | effectiveType = _definition.resultType |
|---|
| 1584 | receiverType = nil |
|---|
| 1585 | else |
|---|
| 1586 | effectiveType = _definition.resultType |
|---|
| 1587 | receiverType = nil |
|---|
| 1588 | _type = effectiveType |
|---|
| 1589 | |
|---|
| 1590 | if _definition inherits BoxMember |
|---|
| 1591 | _bindImpBoxMember(_definition, left) |
|---|
| 1592 | _checkVisibility(_definition, left) |
|---|
| 1593 | else |
|---|
| 1594 | # TODO: type access like enum, class, delegate |
|---|
| 1595 | pass |
|---|
| 1596 | |
|---|
| 1597 | # TODO: there should be a subclass of BinaryOpExpr called DotExpr and it should do the following work and maybe even the work above. |
|---|
| 1598 | # TODO: _receiverType = receiverType |
|---|
| 1599 | .binarySuperNode.type = .type # the type of foo.bar is what bar returns. A MemberExpr is the "bar" part. |
|---|
| 1600 | .binarySuperNode.receiverType = receiverType |
|---|
| 1601 | |
|---|
| 1602 | assert _type |
|---|
| 1603 | |
|---|
| 1604 | def _bindImpBoxMember(defn as BoxMember, left as Expr) |
|---|
| 1605 | if .compiler.refExprLevel < 1 |
|---|
| 1606 | # resolve overloads |
|---|
| 1607 | if defn inherits MemberOverload |
|---|
| 1608 | for member in defn.members |
|---|
| 1609 | if member inherits AbstractMethod |
|---|
| 1610 | if member.params.count == 0 |
|---|
| 1611 | _definition = member |
|---|
| 1612 | didResolveOverload = true |
|---|
| 1613 | break |
|---|
| 1614 | if not didResolveOverload |
|---|
| 1615 | for member in defn.members |
|---|
| 1616 | if member inherits AbstractMethod |
|---|
| 1617 | if member.params.count == 1 and member.params[0].type inherits VariType |
|---|
| 1618 | _definition = member |
|---|
| 1619 | didResolveOverload = true |
|---|
| 1620 | break |
|---|
| 1621 | if not didResolveOverload |
|---|
| 1622 | .throwError('Could not find an overload for "[.name]" with zero arguments.') |
|---|
| 1623 | else if defn inherits AbstractMethod |
|---|
| 1624 | params = defn.params |
|---|
| 1625 | if params.count <> 0 and not (params.count == 1 and params[0].type inherits VariType) |
|---|
| 1626 | .throwError('The method "[defn.name]" is expecting [params.count] argument[Utils.plural(params)], but no arguments are being supplied in this call.') |
|---|
| 1627 | else if .name <> 'getType' # ug, more special handling for getType |
|---|
| 1628 | box = .binarySuperNode.left.type to? Box |
|---|
| 1629 | if box |
|---|
| 1630 | # If there are multiple methods with the name, but this is not an overload, |
|---|
| 1631 | # then prefer the one returning a generic type. |
|---|
| 1632 | # This is for getEnumerator: |
|---|
| 1633 | # def getEnumerator as IEnumerator |
|---|
| 1634 | # def getEnumerator as IEnumerator<of T> |
|---|
| 1635 | members = box.allMembersForName(.name) |
|---|
| 1636 | if members.count > 1 |
|---|
| 1637 | for mbr in members |
|---|
| 1638 | if mbr.resultType inherits Box and (mbr.resultType to Box).isGeneric |
|---|
| 1639 | _definition = mbr |
|---|
| 1640 | _type = mbr.resultType |
|---|
| 1641 | break |
|---|
| 1642 | |
|---|
| 1643 | def toCobraSource as String is override |
|---|
| 1644 | return _name |
|---|
| 1645 | |
|---|
| 1646 | |
|---|
| 1647 | class OldExpr |
|---|
| 1648 | is partial |
|---|
| 1649 | inherits Expr |
|---|
| 1650 | """ |
|---|
| 1651 | The `old` expression used in contracts such as: |
|---|
| 1652 | ensure .count == old .count + 1 |
|---|
| 1653 | """ |
|---|
| 1654 | |
|---|
| 1655 | cue init(token as IToken, expr as Expr) |
|---|
| 1656 | base.init(token) |
|---|
| 1657 | _expr = expr |
|---|
| 1658 | |
|---|
| 1659 | get allExprs as Expr* |
|---|
| 1660 | for expr in base.allExprs, yield expr |
|---|
| 1661 | for expr in .expr.allExprs, yield expr |
|---|
| 1662 | |
|---|
| 1663 | get expr from var as Expr |
|---|
| 1664 | |
|---|
| 1665 | get name as String |
|---|
| 1666 | return 'old' |
|---|
| 1667 | |
|---|
| 1668 | get willChangeVar as bool is override |
|---|
| 1669 | if _expr.willChangeVar, return true |
|---|
| 1670 | return false |
|---|
| 1671 | |
|---|
| 1672 | def addSubFields is override |
|---|
| 1673 | base.addSubFields |
|---|
| 1674 | .addField('expr', _expr) |
|---|
| 1675 | |
|---|
| 1676 | def toCobraSource as String is override |
|---|
| 1677 | return 'old ' + .expr.toCobraSource |
|---|
| 1678 | |
|---|
| 1679 | def _bindImp is override |
|---|
| 1680 | base._bindImp |
|---|
| 1681 | .curCodeMember.addOldExpr(this) # will set the sharpVarName |
|---|
| 1682 | _expr.bindImp |
|---|
| 1683 | _type = _expr.type |
|---|
| 1684 | assert _type |
|---|
| 1685 | |
|---|
| 1686 | |
|---|
| 1687 | class PostCallExpr |
|---|
| 1688 | is partial |
|---|
| 1689 | inherits Expr |
|---|
| 1690 | """ |
|---|
| 1691 | Covers cases like: |
|---|
| 1692 | Car() |
|---|
| 1693 | someDelegate() |
|---|
| 1694 | obj[i]('x') |
|---|
| 1695 | String[](10) |
|---|
| 1696 | Foo.Bar.Baz[](0) |
|---|
| 1697 | |
|---|
| 1698 | For "bar.foo('x')" that's a binary dot expression with a CallExpr on the right hand side. |
|---|
| 1699 | |
|---|
| 1700 | Some transformations are required. For example, the parser creates a PostCallExpr for these: |
|---|
| 1701 | EnumType(EnumMember1, EnumMember2) -- should be: EnumCallExpr |
|---|
| 1702 | _method(arg1, arg2) -- should be: DotExpr containing a CallExpr |
|---|
| 1703 | """ |
|---|
| 1704 | |
|---|
| 1705 | shared |
|---|
| 1706 | |
|---|
| 1707 | def isTargetAcceptable(expr as Expr) as bool |
|---|
| 1708 | """ |
|---|
| 1709 | Returns true if the given expression can be called. |
|---|
| 1710 | That currently includes TypeExpr, IndexExpr and IdentifierExpr. |
|---|
| 1711 | Do not pass expressions that fail this test to the PostCallExpr init. |
|---|
| 1712 | """ |
|---|
| 1713 | return expr inherits TypeExpr or expr inherits IndexExpr or expr inherits IdentifierExpr |
|---|
| 1714 | |
|---|
| 1715 | var _expr as Expr |
|---|
| 1716 | var _args as List<of Expr> |
|---|
| 1717 | var _hasKeywordArg as bool # such as Foo(1, a=2) |
|---|
| 1718 | var _isForAttribute as bool # see Attributes.cobra |
|---|
| 1719 | var _helperMethod as Method? |
|---|
| 1720 | |
|---|
| 1721 | cue init(token as IToken, expr as Expr, args as List<of Expr>) |
|---|
| 1722 | require .isTargetAcceptable(expr) |
|---|
| 1723 | base.init(token) |
|---|
| 1724 | _expr = expr |
|---|
| 1725 | _args = args |
|---|
| 1726 | |
|---|
| 1727 | def addSubFields is override |
|---|
| 1728 | base.addSubFields |
|---|
| 1729 | .addField('expr', _expr) |
|---|
| 1730 | .addField('args', _args) |
|---|
| 1731 | .addField('hasKeywordArg', .hasKeywordArg) |
|---|
| 1732 | .addField('isForAttribute', .isForAttribute) |
|---|
| 1733 | |
|---|
| 1734 | get allExprs as Expr* |
|---|
| 1735 | for expr in base.allExprs, yield expr |
|---|
| 1736 | for expr in .expr.allExprs, yield expr |
|---|
| 1737 | for arg in .args, for expr in arg.allExprs, yield expr |
|---|
| 1738 | |
|---|
| 1739 | get canBeStatement as bool is override |
|---|
| 1740 | return true |
|---|
| 1741 | |
|---|
| 1742 | get expr from var |
|---|
| 1743 | |
|---|
| 1744 | get args from var |
|---|
| 1745 | has Subnodes |
|---|
| 1746 | |
|---|
| 1747 | get hasKeywordArg from var |
|---|
| 1748 | |
|---|
| 1749 | pro isForAttribute from var |
|---|
| 1750 | |
|---|
| 1751 | get name as String |
|---|
| 1752 | e = .expr |
|---|
| 1753 | if e inherits IdentifierExpr, return e.name |
|---|
| 1754 | assert .didBindImp |
|---|
| 1755 | if e inherits TypeExpr, return e.realType.name |
|---|
| 1756 | if e inherits IndexExpr, return e.toCobraSource |
|---|
| 1757 | throw FallThroughException(e) |
|---|
| 1758 | |
|---|
| 1759 | get willChangeVar as bool is override |
|---|
| 1760 | if _expr.willChangeVar, return true |
|---|
| 1761 | for arg in _args, if arg.willChangeVar, return true |
|---|
| 1762 | return false |
|---|
| 1763 | |
|---|
| 1764 | def replaceChild(find as INode, replace as INode) as bool |
|---|
| 1765 | # Because _bindImp invokes .bindImp directly on the .right of an AssignExpr argument, |
|---|
| 1766 | # the base class thinks this PostCallExpr is the super node and fails to find a child to replace. |
|---|
| 1767 | # The real super node is the AssignExpr whose .bindImp was skipped because it's not |
|---|
| 1768 | # a true AssignExpr--it's just syntax for init'ing properties during instance creation. |
|---|
| 1769 | r = base.replaceChild(find, replace) |
|---|
| 1770 | if not r |
|---|
| 1771 | for arg in .args |
|---|
| 1772 | if arg inherits AssignExpr |
|---|
| 1773 | r = arg.replaceChild(find, replace) |
|---|
| 1774 | if r, break |
|---|
| 1775 | return r |
|---|
| 1776 | |
|---|
| 1777 | def _bindImp is override |
|---|
| 1778 | base._bindImp |
|---|
| 1779 | expr = .expr |
|---|
| 1780 | |
|---|
| 1781 | if expr inherits IdentifierExpr |
|---|
| 1782 | if expr.name.startsWith('_') and not .compiler.symbolForName(expr.name, false) inherits BoxVar |
|---|
| 1783 | # method invocation |
|---|
| 1784 | # _foo(x) --> ._foo(x) |
|---|
| 1785 | newCall = CallExpr(.token, .name, .args, true) |
|---|
| 1786 | dotted = DotExpr(.token, 'DOT', ThisLit(.token, isImplicit=true), newCall, isImplicit=true).bindImp |
|---|
| 1787 | _type = dotted.type to IType |
|---|
| 1788 | _transformTo(dotted) |
|---|
| 1789 | return |
|---|
| 1790 | |
|---|
| 1791 | expr = expr.bindImp |
|---|
| 1792 | |
|---|
| 1793 | if expr.definition inherits EnumDecl |
|---|
| 1794 | enumCall = EnumCallExpr(.token, expr.toCobraSource, .args, expr.definition to EnumDecl).bindImp # CC: axe cast |
|---|
| 1795 | _type = enumCall.type to IType |
|---|
| 1796 | _transformTo(enumCall) |
|---|
| 1797 | return |
|---|
| 1798 | |
|---|
| 1799 | for arg in .args |
|---|
| 1800 | try |
|---|
| 1801 | if arg inherits AssignExpr |
|---|
| 1802 | _hasKeywordArg = true |
|---|
| 1803 | if not arg.left inherits IdentifierExpr |
|---|
| 1804 | .recordError('General purpose assignments are not allowed as arguments. Assignment syntax can only be used for keyword arguments.') |
|---|
| 1805 | else if not expr.receiverType.isDynamicOrPassThrough |
|---|
| 1806 | propertyName = (arg.left to IdentifierExpr).name |
|---|
| 1807 | if expr.memberForName(propertyName) is nil |
|---|
| 1808 | .recordCannotFindMemberError(expr, propertyName) |
|---|
| 1809 | arg.right.bindImp # 'x=y' has special treatment in arguments |
|---|
| 1810 | else |
|---|
| 1811 | if _hasKeywordArg |
|---|
| 1812 | .throwError('Cannot have a non-keyword argument ("[arg.toCobraSource]") after a keyword argument. All positional arguments must come before all keyword arguments.') |
|---|
| 1813 | arg.bindImp |
|---|
| 1814 | catch ne as NodeException |
|---|
| 1815 | .compiler.recordError(ne) |
|---|
| 1816 | |
|---|
| 1817 | if expr inherits TypeExpr |
|---|
| 1818 | # instantiation |
|---|
| 1819 | assert expr.containedType |
|---|
| 1820 | _type = expr.containedType |
|---|
| 1821 | else if expr inherits IndexExpr |
|---|
| 1822 | pass |
|---|
| 1823 | else if expr inherits IdentifierExpr |
|---|
| 1824 | assert expr.type # or it should have throw an exception when binding |
|---|
| 1825 | if expr.type.isDynamicOrPassThrough |
|---|
| 1826 | pass |
|---|
| 1827 | else if expr.definition inherits IType |
|---|
| 1828 | pass |
|---|
| 1829 | else if expr.type.nonNil.isDescendantOf(.compiler.delegateType) |
|---|
| 1830 | pass |
|---|
| 1831 | else if expr.type.nonNil.isDescendantOf(.compiler.typeType) |
|---|
| 1832 | pass |
|---|
| 1833 | else |
|---|
| 1834 | .throwError('Cannot call "[expr.toCobraSource]" of type "[expr.type.name]".') |
|---|
| 1835 | else |
|---|
| 1836 | # one example where this happened: x to SomeType() |
|---|
| 1837 | # which yielded: PostCallExpr(expr=ToExpr(...)) |
|---|
| 1838 | # TODO: can probably make this an error now |
|---|
| 1839 | assert false, _expr |
|---|
| 1840 | |
|---|
| 1841 | if _type is nil |
|---|
| 1842 | assert expr.type is not nil |
|---|
| 1843 | exprType = expr.type.nonNil |
|---|
| 1844 | if exprType inherits MethodSig |
|---|
| 1845 | _type = exprType.returnType |
|---|
| 1846 | else if exprType.isDescendantOf(.compiler.delegateType) |
|---|
| 1847 | member = exprType.memberForName('invoke') |
|---|
| 1848 | if member inherits Method |
|---|
| 1849 | _type = member.resultType |
|---|
| 1850 | else |
|---|
| 1851 | .throwError('Cannot find a single "invoke" method for "_eventType.name".') |
|---|
| 1852 | else if expr.receiverType and expr.receiverType.isSystemTypeClass |
|---|
| 1853 | _type = .compiler.dynamicType |
|---|
| 1854 | else if exprType inherits Box |
|---|
| 1855 | _type = expr.receiverType # for example, an IdentifierExpr of 'SomeClass' has a .receiverType of that class |
|---|
| 1856 | _handleClassInitializer |
|---|
| 1857 | else if exprType.isDynamic |
|---|
| 1858 | _type = .compiler.nilableDynamicType |
|---|
| 1859 | else if expr.definition inherits NameSpace |
|---|
| 1860 | .throwError('Cannot instantiate the namespace "[(expr.definition to NameSpace).fullName]".') |
|---|
| 1861 | else |
|---|
| 1862 | assert false, expr |
|---|
| 1863 | |
|---|
| 1864 | if .hasKeywordArg and not .isForAttribute and not .hasError |
|---|
| 1865 | _makeHelperMethod |
|---|
| 1866 | |
|---|
| 1867 | def _makeHelperMethod |
|---|
| 1868 | """ |
|---|
| 1869 | Add a private helper method to the current box to support extended initializer. |
|---|
| 1870 | |
|---|
| 1871 | Foo(expr0, expr1, bar=expr2) --> |
|---|
| 1872 | call: |
|---|
| 1873 | _ch_ext_init_1207(expr0, expr1, expr2) |
|---|
| 1874 | def: |
|---|
| 1875 | def _ch_ext_init_1207(arg0 as int, arg1 as int, /#bar=#/arg2 as int) |
|---|
| 1876 | obj = Foo(arg0, arg1) |
|---|
| 1877 | obj.bar = arg2 |
|---|
| 1878 | return obj |
|---|
| 1879 | """ |
|---|
| 1880 | box, token = .compiler.curBox, box.token.copy |
|---|
| 1881 | paramsForDecl = List<of Param>() |
|---|
| 1882 | argsForInitCall = List<of Expr>() # args to pass to `Foo(arg0, arg1)` |
|---|
| 1883 | propsToSet = List<of AssignExpr>() # props to set as `obj.bar = arg2` etc. |
|---|
| 1884 | firstPropArg = -1 |
|---|
| 1885 | for i, arg in .args.numbered |
|---|
| 1886 | if arg inherits AssignExpr |
|---|
| 1887 | propsToSet.add(arg) |
|---|
| 1888 | if firstPropArg == -1, firstPropArg = i |
|---|
| 1889 | propertyName = (arg.left to IdentifierExpr).name |
|---|
| 1890 | argType = _expr.memberForName(propertyName).resultType |
|---|
| 1891 | arg = arg.right |
|---|
| 1892 | else |
|---|
| 1893 | argsForInitCall.add(if(arg inherits NilLiteral, arg, IdentifierExpr(token.copy('ID', 'arg[i]')))) |
|---|
| 1894 | argType = arg.type to ! |
|---|
| 1895 | paramsForDecl.add(Param(box.token.copy('ID', 'arg[i]'), argType)) |
|---|
| 1896 | name = '_ch_ext_init_[.serialNum]' # ch = class helper, ext = extended, init = initializer |
|---|
| 1897 | if .compiler.codeMemberStack.count |
|---|
| 1898 | curMember = .compiler.curCodeMember |
|---|
| 1899 | genericParams = if(curMember inherits Method, List<of IType>((curMember to Method).genericParams), List<of IType>()) |
|---|
| 1900 | else |
|---|
| 1901 | genericParams = List<of IType>() |
|---|
| 1902 | m = Method(Token.empty, token.copy('ID', name), box, name, genericParams, paramsForDecl, _type, nil, ['shared', 'private'], AttributeList(), '', isCompilerGenerated=true) |
|---|
| 1903 | m.locals.add(LocalVar(token.copy('ID', 'obj'), .type)) |
|---|
| 1904 | |
|---|
| 1905 | objId = IdentifierExpr(token.copy('ID', 'obj'), 'obj') |
|---|
| 1906 | callExpr = PostCallExpr(token.copy('ID', .type.name), IdentifierExpr(token.copy('ID', .type.name), .type), argsForInitCall) |
|---|
| 1907 | assign = AssignExpr(token.copy('ASSIGN', '='), 'ASSIGN', objId, callExpr) |
|---|
| 1908 | m.addStmt(assign) |
|---|
| 1909 | |
|---|
| 1910 | i = firstPropArg |
|---|
| 1911 | for propSetExpr in propsToSet |
|---|
| 1912 | propName = (propSetExpr.left to IdentifierExpr).name |
|---|
| 1913 | memberExpr = DotExpr(token.copy('DOT', '.'), 'DOT', IdentifierExpr(token.copy('ID', 'obj')), MemberExpr(token.copy('ID', propName))) |
|---|
| 1914 | assign = AssignExpr(token.copy('ASSIGN', '='), 'ASSIGN', memberExpr, IdentifierExpr(token.copy('ID', 'arg[i]'))) |
|---|
| 1915 | m.addStmt(assign) |
|---|
| 1916 | i += 1 |
|---|
| 1917 | |
|---|
| 1918 | retStmt = ReturnStmt(token.copy('RETURN', 'return'), IdentifierExpr(token.copy('ID', 'obj'), 'obj')) |
|---|
| 1919 | m.addStmt(retStmt) |
|---|
| 1920 | |
|---|
| 1921 | if .compiler.isBindingInt, m.bindInt # happens for: var foo = Bar(baz=1) |
|---|
| 1922 | else, m.bindAll |
|---|
| 1923 | box.addDecl(m) |
|---|
| 1924 | _helperMethod = m |
|---|
| 1925 | |
|---|
| 1926 | def toCobraSource as String is override |
|---|
| 1927 | sb = StringBuilder() |
|---|
| 1928 | sb.append(_expr.toCobraSource) |
|---|
| 1929 | sb.append('(') |
|---|
| 1930 | sep = '' |
|---|
| 1931 | for arg in .args |
|---|
| 1932 | sb.append(sep) |
|---|
| 1933 | sb.append(arg.toCobraSource) |
|---|
| 1934 | sep = ', ' |
|---|
| 1935 | sb.append(')') |
|---|
| 1936 | return sb.toString |
|---|
| 1937 | |
|---|
| 1938 | def _handleClassInitializer |
|---|
| 1939 | initCall as Initializer? |
|---|
| 1940 | if .args.count > 0 |
|---|
| 1941 | type = .compiler.symbolForName(.name, false, true) to IType? |
|---|
| 1942 | if type is not nil |
|---|
| 1943 | possibleCalls = type.memberForName('cue.init') |
|---|
| 1944 | if possibleCalls is nil |
|---|
| 1945 | pass # TODO: determine if this is an error or if support for base class init functions need to be added |
|---|
| 1946 | else if possibleCalls inherits MemberOverload |
|---|
| 1947 | initCall = possibleCalls.computeBestOverload(.args, nil, false) to Initializer? |
|---|
| 1948 | else if possibleCalls inherits Initializer |
|---|
| 1949 | initCall = possibleCalls |
|---|
| 1950 | #else |
|---|
| 1951 | # TODO: determine if this is an error |
|---|
| 1952 | #print initCall |
|---|
| 1953 | if initCall is not nil and .args.count == initCall.params.count |
|---|
| 1954 | for i, arg in .args.numbered |
|---|
| 1955 | if arg.type is not nil and arg.type.isDynamic |
|---|
| 1956 | # set context type so that backend can type cast it correctly |
|---|
| 1957 | arg.contextType = initCall.params[i].type |
|---|
| 1958 | #else |
|---|
| 1959 | # TODO: warning or error? |
|---|
| 1960 | |
|---|
| 1961 | |
|---|
| 1962 | class RefExpr inherits Expr is partial |
|---|
| 1963 | """ |
|---|
| 1964 | A `ref` expression is used to refer to a method without invoking it: |
|---|
| 1965 | ref obj.foo |
|---|
| 1966 | ref _foo |
|---|
| 1967 | """ |
|---|
| 1968 | |
|---|
| 1969 | cue init(token as IToken, expr as Expr) |
|---|
| 1970 | base.init(token) |
|---|
| 1971 | # TODO: can the types of expressions be limited? |
|---|
| 1972 | _expr = expr |
|---|
| 1973 | |
|---|
| 1974 | def addSubFields |
|---|
| 1975 | base.addSubFields |
|---|
| 1976 | .addField('expr', _expr) |
|---|
| 1977 | |
|---|
| 1978 | get allExprs as Expr* |
|---|
| 1979 | for expr in base.allExprs, yield expr |
|---|
| 1980 | for expr in .expr.allExprs, yield expr |
|---|
| 1981 | |
|---|
| 1982 | get expr from var as Expr |
|---|
| 1983 | |
|---|
| 1984 | get willChangeVar as bool is override |
|---|
| 1985 | if _expr.willChangeVar, return true |
|---|
| 1986 | return false |
|---|
| 1987 | |
|---|
| 1988 | def _bindImp is override |
|---|
| 1989 | base._bindImp |
|---|
| 1990 | .compiler.refExprLevel += 1 |
|---|
| 1991 | try |
|---|
| 1992 | _expr.bindImp |
|---|
| 1993 | catch ne as NodeException |
|---|
| 1994 | # TODO: is this really needed? if so, should this be handled universally rather than just at this site? |
|---|
| 1995 | if ne inherits NodeMultiException |
|---|
| 1996 | if ne.exceptions.count == 1 |
|---|
| 1997 | ne = ne.exceptions[0] |
|---|
| 1998 | throw ne |
|---|
| 1999 | throw |
|---|
| 2000 | finally |
|---|
| 2001 | .compiler.refExprLevel -= 1 |
|---|
| 2002 | if _expr inherits DotExpr |
|---|
| 2003 | right = _expr.right |
|---|
| 2004 | if right inherits MemberExpr |
|---|
| 2005 | if right.definition is nil |
|---|
| 2006 | # happens from error recovery such as: obj = BadClassName() ... ref obj.foo |
|---|
| 2007 | pass |
|---|
| 2008 | else if right.definition inherits AbstractMethod or right.definition inherits MemberOverload |
|---|
| 2009 | right.isReference = true |
|---|
| 2010 | else |
|---|
| 2011 | .throwError('Only methods can be referenced, not [right.definition.englishName].') |
|---|
| 2012 | else if right inherits CallExpr |
|---|
| 2013 | .throwError('Cannot call a method that is preceded by `ref`.') |
|---|
| 2014 | else |
|---|
| 2015 | throw FallThroughException([this, _expr, right]) |
|---|
| 2016 | else if _expr inherits IdentifierExpr |
|---|
| 2017 | if _expr.definition inherits AbstractMethod |
|---|
| 2018 | pass |
|---|
| 2019 | else |
|---|
| 2020 | .throwError('Only methods can be referenced, not [Utils.pluralize(_expr.definition.englishName)].') |
|---|
| 2021 | else |
|---|
| 2022 | .throwError('Unexpected reference. Refer to methods after `ref` or remove `ref`.') |
|---|
| 2023 | _type = .compiler.passThroughType # TODO: Set a real type such as .compiler.delegateType |
|---|
| 2024 | # TODO: need to do something more sophisticated like overriding: def canBeAssignedTo(type as IType) as bool |
|---|
| 2025 | |
|---|
| 2026 | def toCobraSource as String is override |
|---|
| 2027 | return 'ref ' + _expr.toCobraSource |
|---|
| 2028 | |
|---|
| 2029 | |
|---|
| 2030 | class SharpExpr |
|---|
| 2031 | is partial |
|---|
| 2032 | inherits Expr |
|---|
| 2033 | |
|---|
| 2034 | var _expr as StringLit? |
|---|
| 2035 | var _sharpSource as String? |
|---|
| 2036 | |
|---|
| 2037 | cue init(token as IToken, sharpSource as String) |
|---|
| 2038 | base.init(token) |
|---|
| 2039 | _sharpSource = sharpSource |
|---|
| 2040 | |
|---|
| 2041 | cue init(token as IToken, expr as Expr) |
|---|
| 2042 | base.init(token) |
|---|
| 2043 | if expr inherits StringLit # TODO:? make this an arg type |
|---|
| 2044 | _expr = expr |
|---|
| 2045 | else |
|---|
| 2046 | assert false, r'sharp expression must be a String Literal (No substitutions) expr=[expr]' |
|---|
| 2047 | |
|---|
| 2048 | get allExprs as Expr* |
|---|
| 2049 | for expr in base.allExprs, yield expr |
|---|
| 2050 | if .expr, for expr in .expr.allExprs, yield expr |
|---|
| 2051 | |
|---|
| 2052 | get canBeStatement as bool is override |
|---|
| 2053 | return true |
|---|
| 2054 | |
|---|
| 2055 | get sharpSource from var |
|---|
| 2056 | |
|---|
| 2057 | get expr from var |
|---|
| 2058 | |
|---|
| 2059 | def toCobraSource as String is override |
|---|
| 2060 | if _sharpSource |
|---|
| 2061 | quote = if(.token.text.startsWith('sharp"'), '"', "'") |
|---|
| 2062 | return "sharp[quote][_sharpSource][quote]" |
|---|
| 2063 | else |
|---|
| 2064 | return "sharp'[_expr.toCobraSource]'" |
|---|
| 2065 | |
|---|
| 2066 | def _bindImp is override |
|---|
| 2067 | base._bindImp |
|---|
| 2068 | _type = .compiler.passThroughType |
|---|
| 2069 | if _expr |
|---|
| 2070 | _expr.bindImp |
|---|
| 2071 | |
|---|
| 2072 | |
|---|
| 2073 | class SliceExpr |
|---|
| 2074 | is partial |
|---|
| 2075 | inherits Expr |
|---|
| 2076 | """ |
|---|
| 2077 | Just like Python slices. |
|---|
| 2078 | """ |
|---|
| 2079 | |
|---|
| 2080 | var _target as Expr |
|---|
| 2081 | var _start as Expr? |
|---|
| 2082 | var _stop as Expr? |
|---|
| 2083 | var _step as Expr? |
|---|
| 2084 | |
|---|
| 2085 | cue init(token as IToken, target as Expr, start as Expr?, stopp as Expr?, stepp as Expr?) |
|---|
| 2086 | base.init(token) |
|---|
| 2087 | _target = target |
|---|
| 2088 | _start = start |
|---|
| 2089 | _stop = stopp |
|---|
| 2090 | _step = stepp |
|---|
| 2091 | |
|---|
| 2092 | def addSubFields |
|---|
| 2093 | base.addSubFields |
|---|
| 2094 | .addField('target', _target) |
|---|
| 2095 | .addField('start', _start) |
|---|
| 2096 | .addField('stop', _stop) |
|---|
| 2097 | .addField('step', _step) |
|---|
| 2098 | |
|---|
| 2099 | get allExprs as Expr* |
|---|
| 2100 | for expr in base.allExprs, yield expr |
|---|
| 2101 | for expr in .target.allExprs, yield expr |
|---|
| 2102 | if .start, for expr in .start.allExprs, yield expr |
|---|
| 2103 | if .stop, for expr in .stop.allExprs, yield expr |
|---|
| 2104 | if .step, for expr in .step.allExprs, yield expr |
|---|
| 2105 | |
|---|
| 2106 | get willChangeVar as bool is override |
|---|
| 2107 | if _target.willChangeVar, return true |
|---|
| 2108 | if _start and _start.willChangeVar, return true |
|---|
| 2109 | if _stop and _stop.willChangeVar, return true |
|---|
| 2110 | if _step and _step.willChangeVar, return true |
|---|
| 2111 | return false |
|---|
| 2112 | |
|---|
| 2113 | get target from var |
|---|
| 2114 | |
|---|
| 2115 | get start from var |
|---|
| 2116 | |
|---|
| 2117 | get stop from var |
|---|
| 2118 | |
|---|
| 2119 | get step from var |
|---|
| 2120 | |
|---|
| 2121 | def _bindImp |
|---|
| 2122 | base._bindImp |
|---|
| 2123 | intType = .compiler.intType |
|---|
| 2124 | try |
|---|
| 2125 | _target.bindImp |
|---|
| 2126 | catch ne as NodeException |
|---|
| 2127 | .compiler.recordError(ne) |
|---|
| 2128 | success |
|---|
| 2129 | if not _target.type.isSequenceLike and not _target.type inherits PassThroughType |
|---|
| 2130 | .throwError('Cannot slice values of type "[_target.type.name]". You can slice strings, arrays, IList and IList<of>.') |
|---|
| 2131 | if _start |
|---|
| 2132 | try |
|---|
| 2133 | _start.bindImp |
|---|
| 2134 | catch ne as NodeException |
|---|
| 2135 | .compiler.recordError(ne) |
|---|
| 2136 | success |
|---|
| 2137 | if _start.type.isDynamic |
|---|
| 2138 | _start.contextType = intType |
|---|
| 2139 | else if not _start.isKindOf(intType) |
|---|
| 2140 | _start.recordError('The start index of the slice is type "[_start.type.name]", but should be "int".') |
|---|
| 2141 | if _stop |
|---|
| 2142 | try |
|---|
| 2143 | _stop.bindImp |
|---|
| 2144 | catch ne as NodeException |
|---|
| 2145 | .compiler.recordError(ne) |
|---|
| 2146 | success |
|---|
| 2147 | if _stop.type.isDynamic |
|---|
| 2148 | _stop.contextType = intType |
|---|
| 2149 | else if not _stop.isKindOf(intType) |
|---|
| 2150 | _stop.recordError('The stop index of the slice is type "[_stop.type.name]", but should be "int".') |
|---|
| 2151 | if _step |
|---|
| 2152 | try |
|---|
| 2153 | _step.bindImp |
|---|
| 2154 | catch ne as NodeException |
|---|
| 2155 | .compiler.recordError(ne) |
|---|
| 2156 | success |
|---|
| 2157 | if _step.type.isDynamic |
|---|
| 2158 | _step.contextType = intType |
|---|
| 2159 | else if not _step.isKindOf(intType) |
|---|
| 2160 | _step.recordError('The step of the slice is type "[_step.type.name]", but should be "int".') |
|---|
| 2161 | if _target.hasError |
|---|
| 2162 | _type = .compiler.passThroughType |
|---|
| 2163 | else |
|---|
| 2164 | _type = _target.type |
|---|
| 2165 | |
|---|
| 2166 | def toCobraSource as String is override |
|---|
| 2167 | sb = StringBuilder() |
|---|
| 2168 | sb.append(_target.toCobraSource) |
|---|
| 2169 | sb.append(r'[') |
|---|
| 2170 | if _start |
|---|
| 2171 | sb.append(_start.toCobraSource) |
|---|
| 2172 | sb.append(':') |
|---|
| 2173 | if _stop |
|---|
| 2174 | sb.append(_stop.toCobraSource) |
|---|
| 2175 | if _step |
|---|
| 2176 | sb.append(':') |
|---|
| 2177 | sb.append(_step.toCobraSource) |
|---|
| 2178 | sb.append(']') |
|---|
| 2179 | return sb.toString |
|---|
| 2180 | |
|---|
| 2181 | |
|---|
| 2182 | class TruthExpr |
|---|
| 2183 | is partial |
|---|
| 2184 | inherits Expr |
|---|
| 2185 | """ |
|---|
| 2186 | A truth expr wraps an expression such that it can be used where a bool is expected in .NET/C#. |
|---|
| 2187 | For example, if passed an integer typed expression, the truth expression will wrap it with a |
|---|
| 2188 | comparison 0!=expr. |
|---|
| 2189 | Statically typed bools are passed straight through. |
|---|
| 2190 | The `dynamic` and `dynamic?` types use a run-time service. |
|---|
| 2191 | Other nilable types are checked for nil. |
|---|
| 2192 | """ |
|---|
| 2193 | |
|---|
| 2194 | enum Treatment |
|---|
| 2195 | AsIs |
|---|
| 2196 | InvokeRuntime |
|---|
| 2197 | CompareToNull |
|---|
| 2198 | CompareToZero |
|---|
| 2199 | CompareToZeroChar |
|---|
| 2200 | |
|---|
| 2201 | var _expr as Expr |
|---|
| 2202 | var _origExpr as Expr |
|---|
| 2203 | var _notExpr as UnaryOpExpr? |
|---|
| 2204 | var _treatment as Treatment |
|---|
| 2205 | |
|---|
| 2206 | cue init(expr as Expr) |
|---|
| 2207 | .init(expr, nil) |
|---|
| 2208 | |
|---|
| 2209 | cue init(expr as Expr, notExpr as UnaryOpExpr?) |
|---|
| 2210 | """ |
|---|
| 2211 | Pass the notExpr if the truth expression is the target of the not operator. |
|---|
| 2212 | This affects warnings generated by TruthExpr. |
|---|
| 2213 | """ |
|---|
| 2214 | base.init(expr.token) |
|---|
| 2215 | _origExpr = _expr = expr |
|---|
| 2216 | _notExpr = notExpr |
|---|
| 2217 | |
|---|
| 2218 | def addMinFields is override |
|---|
| 2219 | base.addMinFields |
|---|
| 2220 | .addField('Treatment', _treatment) |
|---|
| 2221 | |
|---|
| 2222 | def addSubFields is override |
|---|
| 2223 | base.addSubFields |
|---|
| 2224 | .addField('expr', _expr) |
|---|
| 2225 | .addField('Treatment', _treatment) |
|---|
| 2226 | |
|---|
| 2227 | get allExprs as Expr* |
|---|
| 2228 | for expr in base.allExprs, yield expr |
|---|
| 2229 | for expr in .expr.allExprs, yield expr |
|---|
| 2230 | |
|---|
| 2231 | get expr from var |
|---|
| 2232 | |
|---|
| 2233 | get willChangeVar as bool is override |
|---|
| 2234 | if _expr.willChangeVar, return true |
|---|
| 2235 | if _origExpr.willChangeVar, return true |
|---|
| 2236 | # Don't check _notExpr which owns *this* expr: |
|---|
| 2237 | # if _notExpr and _notExpr.willChangeVar, return true |
|---|
| 2238 | return false |
|---|
| 2239 | |
|---|
| 2240 | get notExpr from var |
|---|
| 2241 | |
|---|
| 2242 | def _bindImp |
|---|
| 2243 | base._bindImp |
|---|
| 2244 | _expr.bindImp |
|---|
| 2245 | type = _expr.type |
|---|
| 2246 | if type is .compiler.boolType |
|---|
| 2247 | _treatment = Treatment.AsIs |
|---|
| 2248 | else if type inherits AbstractNumberType |
|---|
| 2249 | _treatment = Treatment.CompareToZero |
|---|
| 2250 | else if _expr inherits NilLiteral |
|---|
| 2251 | .compiler.warning(this, 'The value nil will always evaluate to false.') |
|---|
| 2252 | _expr = BoolLit(_expr.token, false) |
|---|
| 2253 | _expr.bindImp |
|---|
| 2254 | else if type inherits NilableType |
|---|
| 2255 | if type.nonNil inherits DynamicType or type.nonNil inherits PrimitiveType |
|---|
| 2256 | _treatment = Treatment.InvokeRuntime |
|---|
| 2257 | else |
|---|
| 2258 | _treatment = Treatment.CompareToNull |
|---|
| 2259 | else if type inherits DynamicType |
|---|
| 2260 | _treatment = Treatment.InvokeRuntime |
|---|
| 2261 | else if _expr.isKindOf(.compiler.passThroughType) |
|---|
| 2262 | _treatment = Treatment.InvokeRuntime |
|---|
| 2263 | else if type.isReference |
|---|
| 2264 | _treatment = Treatment.CompareToNull |
|---|
| 2265 | else if type inherits CharType |
|---|
| 2266 | _treatment = Treatment.CompareToZeroChar |
|---|
| 2267 | else if type inherits VoidType |
|---|
| 2268 | .throwError('Cannot determine truth because the method does not return a value.') |
|---|
| 2269 | else if type inherits Struct |
|---|
| 2270 | .throwError('Cannot determine truth from an arbitrary struct.') |
|---|
| 2271 | else |
|---|
| 2272 | throw FallThroughException(this) |
|---|
| 2273 | if type.isReference and not type inherits NilableType and not type inherits NilType and not type inherits PassThroughType and not type inherits DynamicType |
|---|
| 2274 | hint = 'You can remove the expression' |
|---|
| 2275 | if _notExpr |
|---|
| 2276 | if _expr.isKindOf(.compiler.stringType) |
|---|
| 2277 | hint += ' or check for an empty string.' |
|---|
| 2278 | else if type inherits ArrayType |
|---|
| 2279 | hint += ' or check for an empty array.' |
|---|
| 2280 | else if _expr.isKindOfCollection |
|---|
| 2281 | hint += ' or check for an empty collection.' |
|---|
| 2282 | else |
|---|
| 2283 | hint += '.' |
|---|
| 2284 | .compiler.warning(_notExpr, 'The expression "[_notExpr.toCobraSource]" (of type "[type.name]") will never evaluate to false because the expression is not nilable. [hint]') |
|---|
| 2285 | else |
|---|
| 2286 | if _expr.isKindOf(.compiler.stringType) |
|---|
| 2287 | hint += ' or check for non-empty strings with ".length".' |
|---|
| 2288 | else if type inherits ArrayType |
|---|
| 2289 | hint += ' or check for non-empty arrays with ".length".' |
|---|
| 2290 | else if _expr.isKindOfCollection |
|---|
| 2291 | hint += ' or check for non-empty collections with ".count".' |
|---|
| 2292 | else |
|---|
| 2293 | hint += '.' |
|---|
| 2294 | .compiler.warning(this, 'The expression "[_expr.toCobraSource]" (of type "[type.name]") will always evaluate to true because it is not nilable. [hint]') |
|---|
| 2295 | _type = .compiler.boolType |
|---|
| 2296 | |
|---|
| 2297 | def toCobraSource as String is override |
|---|
| 2298 | return _origExpr.toCobraSource |
|---|
| 2299 | |
|---|
| 2300 | |
|---|
| 2301 | class TypeExpr |
|---|
| 2302 | is partial |
|---|
| 2303 | inherits Expr |
|---|
| 2304 | implements IPotentialTypeExpr, ITypeProxy |
|---|
| 2305 | """ |
|---|
| 2306 | Unlike the other expressions that implement IPotentialType, a TypeExpr always represents a type. |
|---|
| 2307 | Hence it implements ITypeProxy as well. |
|---|
| 2308 | """ |
|---|
| 2309 | |
|---|
| 2310 | var _typeNode as ITypeProxy? |
|---|
| 2311 | var _containedType as IType? |
|---|
| 2312 | |
|---|
| 2313 | cue init(token as IToken, typeNode as ITypeProxy) |
|---|
| 2314 | base.init(token) |
|---|
| 2315 | _typeNode = typeNode |
|---|
| 2316 | |
|---|
| 2317 | cue init(typeNode as ITypeProxy) |
|---|
| 2318 | .init((typeNode to ISyntaxNode).token, typeNode) # TODO: hmmm this is kind of weird |
|---|
| 2319 | |
|---|
| 2320 | cue init(token as IToken, type as IType) |
|---|
| 2321 | base.init(token) |
|---|
| 2322 | _containedType = type |
|---|
| 2323 | _receiverType = type |
|---|
| 2324 | |
|---|
| 2325 | get definition is override |
|---|
| 2326 | return _containedType |
|---|
| 2327 | |
|---|
| 2328 | get _innerHasError as bool |
|---|
| 2329 | return base._innerHasError or (.typeNode and .typeNode.hasError) |
|---|
| 2330 | |
|---|
| 2331 | get containedType from var |
|---|
| 2332 | |
|---|
| 2333 | get potentialType as IType? |
|---|
| 2334 | return .realType |
|---|
| 2335 | |
|---|
| 2336 | get realType as IType |
|---|
| 2337 | assert .didBindImp |
|---|
| 2338 | assert _containedType |
|---|
| 2339 | return _containedType to ! |
|---|
| 2340 | |
|---|
| 2341 | get typeNode from var |
|---|
| 2342 | |
|---|
| 2343 | def addRefFields is override |
|---|
| 2344 | base.addRefFields |
|---|
| 2345 | .addField('containedType', _containedType) |
|---|
| 2346 | |
|---|
| 2347 | def addSubFields is override |
|---|
| 2348 | base.addSubFields |
|---|
| 2349 | .addField('typeNode', _typeNode) |
|---|
| 2350 | |
|---|
| 2351 | def toCobraSource as String is override |
|---|
| 2352 | assert _containedType |
|---|
| 2353 | return _containedType.name |
|---|
| 2354 | |
|---|
| 2355 | def _bindImp is override |
|---|
| 2356 | base._bindImp |
|---|
| 2357 | if not _containedType and _typeNode |
|---|
| 2358 | _containedType = _receiverType = _typeNode.realType |
|---|
| 2359 | _type = .compiler.typeType |
|---|
| 2360 | |
|---|
| 2361 | |
|---|
| 2362 | class AllOrAnyExpr |
|---|
| 2363 | is abstract, partial |
|---|
| 2364 | inherits Expr |
|---|
| 2365 | """ |
|---|
| 2366 | The base class for AllExpr and AnyExpr which have much in common. |
|---|
| 2367 | |
|---|
| 2368 | They are unary prefix operators taking something enumerable and returning a bool. |
|---|
| 2369 | """ |
|---|
| 2370 | |
|---|
| 2371 | var _expr as Expr |
|---|
| 2372 | |
|---|
| 2373 | cue init(token as IToken, expr as Expr) |
|---|
| 2374 | base.init(token) |
|---|
| 2375 | _expr = expr |
|---|
| 2376 | |
|---|
| 2377 | get allExprs as Expr* |
|---|
| 2378 | for expr in base.allExprs, yield expr |
|---|
| 2379 | for expr in .expr.allExprs, yield expr |
|---|
| 2380 | |
|---|
| 2381 | get opName as String is abstract |
|---|
| 2382 | |
|---|
| 2383 | get expr from var |
|---|
| 2384 | |
|---|
| 2385 | get willChangeVar as bool is override |
|---|
| 2386 | if base.willChangeVar, return true |
|---|
| 2387 | if .expr.willChangeVar, return true |
|---|
| 2388 | return false |
|---|
| 2389 | |
|---|
| 2390 | def addSubFields is override |
|---|
| 2391 | base.addSubFields |
|---|
| 2392 | .addField('expr', _expr) |
|---|
| 2393 | |
|---|
| 2394 | def toCobraSource as String is override |
|---|
| 2395 | return '[.opName] [.expr.toCobraSource]' |
|---|
| 2396 | |
|---|
| 2397 | def _bindImp |
|---|
| 2398 | base._bindImp |
|---|
| 2399 | _type = .compiler.boolType |
|---|
| 2400 | _expr.bindImp |
|---|
| 2401 | enumerable = .compiler.enumerableType |
|---|
| 2402 | if _expr.type.isDescendantOf(enumerable) |
|---|
| 2403 | pass |
|---|
| 2404 | else if _expr.type.isDynamic |
|---|
| 2405 | _expr.contextType = enumerable |
|---|
| 2406 | else |
|---|
| 2407 | .throwError('Expecting an enumerable expression after "[.opName]", but got an expression of type "[.expr.type.name]".') |
|---|
| 2408 | |
|---|
| 2409 | |
|---|
| 2410 | class AllExpr |
|---|
| 2411 | is partial |
|---|
| 2412 | inherits AllOrAnyExpr |
|---|
| 2413 | """ |
|---|
| 2414 | all <enumerable> --> true if all elements are true |
|---|
| 2415 | """ |
|---|
| 2416 | |
|---|
| 2417 | cue init(token as IToken, expr as Expr) |
|---|
| 2418 | base.init(token, expr) |
|---|
| 2419 | |
|---|
| 2420 | get opName as String is override |
|---|
| 2421 | return 'all' |
|---|
| 2422 | |
|---|
| 2423 | |
|---|
| 2424 | class AnyExpr |
|---|
| 2425 | is partial |
|---|
| 2426 | inherits AllOrAnyExpr |
|---|
| 2427 | """ |
|---|
| 2428 | any <enumerable> --> true if any element is true |
|---|
| 2429 | """ |
|---|
| 2430 | |
|---|
| 2431 | cue init(token as IToken, expr as Expr) |
|---|
| 2432 | base.init(token, expr) |
|---|
| 2433 | |
|---|
| 2434 | get opName as String is override |
|---|
| 2435 | return 'any' |
|---|
| 2436 | |
|---|
| 2437 | |
|---|
| 2438 | class UnaryOpExpr |
|---|
| 2439 | is partial |
|---|
| 2440 | inherits Expr |
|---|
| 2441 | |
|---|
| 2442 | var _op as String |
|---|
| 2443 | var _expr as Expr |
|---|
| 2444 | |
|---|
| 2445 | cue init(token as IToken, op as String, expr as Expr) |
|---|
| 2446 | require op in ['MINUS', 'PLUS', 'NOT', 'TILDE'] |
|---|
| 2447 | base.init(token) |
|---|
| 2448 | _op = op |
|---|
| 2449 | _expr = expr |
|---|
| 2450 | |
|---|
| 2451 | def addMinFields |
|---|
| 2452 | base.addMinFields |
|---|
| 2453 | .addField('op', _op) |
|---|
| 2454 | |
|---|
| 2455 | def addSubFields |
|---|
| 2456 | base.addSubFields |
|---|
| 2457 | .addField('expr', _expr) |
|---|
| 2458 | |
|---|
| 2459 | get allExprs as Expr* |
|---|
| 2460 | for expr in base.allExprs, yield expr |
|---|
| 2461 | for expr in .expr.allExprs, yield expr |
|---|
| 2462 | |
|---|
| 2463 | get willChangeVar as bool is override |
|---|
| 2464 | if _expr.willChangeVar, return true |
|---|
| 2465 | return false |
|---|
| 2466 | |
|---|
| 2467 | get op from var |
|---|
| 2468 | |
|---|
| 2469 | get expr from var |
|---|
| 2470 | |
|---|
| 2471 | def _bindImp is override |
|---|
| 2472 | base._bindImp |
|---|
| 2473 | op = .op |
|---|
| 2474 | expr = _expr |
|---|
| 2475 | expr.bindImp |
|---|
| 2476 | if _type is nil, _type = _expr.type |
|---|
| 2477 | branch op |
|---|
| 2478 | on 'MINUS', _type = expr.type |
|---|
| 2479 | on 'PLUS', _type = expr.type |
|---|
| 2480 | on 'TILDE', _type = expr.type |
|---|
| 2481 | on 'NOT' |
|---|
| 2482 | if expr.type is not .compiler.boolType |
|---|
| 2483 | _expr = TruthExpr(expr, this).bindAll to TruthExpr |
|---|
| 2484 | _type = .compiler.boolType |
|---|
| 2485 | else |
|---|
| 2486 | throw FallThroughException(op) |
|---|
| 2487 | |
|---|
| 2488 | if op in ['MINUS', 'PLUS'] # TODO: 'TILDE' for ints? |
|---|
| 2489 | if expr inherits AtomicLiteral |
|---|
| 2490 | if expr.isNumeric |
|---|
| 2491 | token = expr.token.copy |
|---|
| 2492 | branch op |
|---|
| 2493 | on 'PLUS' |
|---|
| 2494 | pass |
|---|
| 2495 | on 'MINUS' |
|---|
| 2496 | token.value = -(token.value to dynamic) |
|---|
| 2497 | token.text = if(token.text.startsWith('-'), token.text[1:], '-' + token.text) |
|---|
| 2498 | else |
|---|
| 2499 | throw FallThroughException(op) |
|---|
| 2500 | nodeType = expr.getType |
|---|
| 2501 | expr = nodeType(token) |
|---|
| 2502 | expr.type = _type |
|---|
| 2503 | expr.bindAll |
|---|
| 2504 | _transformTo(expr) |
|---|
| 2505 | |
|---|
| 2506 | def toCobraSource as String is override |
|---|
| 2507 | branch _op |
|---|
| 2508 | on 'MINUS', op = '-' |
|---|
| 2509 | on 'PLUS', op = '+' |
|---|
| 2510 | on 'TILDE', op = '~' |
|---|
| 2511 | on 'NOT', op = 'not ' |
|---|
| 2512 | else, throw FallThroughException(_op) |
|---|
| 2513 | return op + _expr.toCobraSource |
|---|
| 2514 | |
|---|
| 2515 | |
|---|
| 2516 | ## |
|---|
| 2517 | ## Literals |
|---|
| 2518 | ## |
|---|
| 2519 | |
|---|
| 2520 | class Literal |
|---|
| 2521 | is partial |
|---|
| 2522 | inherits Expr |
|---|
| 2523 | |
|---|
| 2524 | cue init(token as IToken) |
|---|
| 2525 | base.init(token) |
|---|
| 2526 | |
|---|
| 2527 | |
|---|
| 2528 | class AtomicLiteral |
|---|
| 2529 | is partial |
|---|
| 2530 | inherits Literal |
|---|
| 2531 | |
|---|
| 2532 | var _text as String |
|---|
| 2533 | |
|---|
| 2534 | cue init(token as IToken) |
|---|
| 2535 | base.init(token) |
|---|
| 2536 | _text = token.text |
|---|
| 2537 | |
|---|
| 2538 | def isEquivalentTo(expr as Expr) as bool |
|---|
| 2539 | r = base.isEquivalentTo(expr) |
|---|
| 2540 | if not r |
|---|
| 2541 | r = expr.typeOf == .typeOf and (expr to AtomicLiteral)._text == _text |
|---|
| 2542 | return r |
|---|
| 2543 | |
|---|
| 2544 | get isNumeric as bool |
|---|
| 2545 | """ |
|---|
| 2546 | Returns true if the literal is numeric such that + and - could apply to it. |
|---|
| 2547 | The default implementation returns false. |
|---|
| 2548 | """ |
|---|
| 2549 | return false |
|---|
| 2550 | |
|---|
| 2551 | def bindImp as dynamic is override |
|---|
| 2552 | base.bindImp |
|---|
| 2553 | .checkType |
|---|
| 2554 | return .bindImpResult |
|---|
| 2555 | |
|---|
| 2556 | def checkType |
|---|
| 2557 | assert _type, this |
|---|
| 2558 | |
|---|
| 2559 | def _bindImp is override |
|---|
| 2560 | base._bindImp |
|---|
| 2561 | |
|---|
| 2562 | def toCobraSource as String is override |
|---|
| 2563 | return _text |
|---|
| 2564 | |
|---|
| 2565 | |
|---|
| 2566 | class BoolLit |
|---|
| 2567 | is partial |
|---|
| 2568 | inherits AtomicLiteral |
|---|
| 2569 | |
|---|
| 2570 | var _value as bool |
|---|
| 2571 | |
|---|
| 2572 | cue init(token as IToken) |
|---|
| 2573 | require token.text in ['true', 'false'] |
|---|
| 2574 | base.init(token) |
|---|
| 2575 | _value = token.text=='true' |
|---|
| 2576 | |
|---|
| 2577 | cue init(token as IToken, value as bool) |
|---|
| 2578 | base.init(token) |
|---|
| 2579 | _value = value |
|---|
| 2580 | |
|---|
| 2581 | def _bindImp is override |
|---|
| 2582 | base._bindImp |
|---|
| 2583 | _type = .compiler.boolType |
|---|
| 2584 | |
|---|
| 2585 | |
|---|
| 2586 | class CharLit |
|---|
| 2587 | is partial |
|---|
| 2588 | inherits AtomicLiteral |
|---|
| 2589 | |
|---|
| 2590 | var _value as String # TODO: should probably be char |
|---|
| 2591 | |
|---|
| 2592 | cue init(token as IToken) |
|---|
| 2593 | require |
|---|
| 2594 | token.which in ['CHAR_LIT_SINGLE', 'CHAR_LIT_DOUBLE'] |
|---|
| 2595 | token.value inherits String |
|---|
| 2596 | body |
|---|
| 2597 | base.init(token) |
|---|
| 2598 | _value = token.value to String |
|---|
| 2599 | |
|---|
| 2600 | def _bindImp is override |
|---|
| 2601 | base._bindImp |
|---|
| 2602 | _type = .compiler.charType |
|---|
| 2603 | |
|---|
| 2604 | |
|---|
| 2605 | class DecimalLit |
|---|
| 2606 | is partial |
|---|
| 2607 | inherits AtomicLiteral |
|---|
| 2608 | |
|---|
| 2609 | var _value as decimal |
|---|
| 2610 | |
|---|
| 2611 | cue init(token as IToken) |
|---|
| 2612 | require token.value inherits decimal |
|---|
| 2613 | base.init(token) |
|---|
| 2614 | _value = token.value to decimal |
|---|
| 2615 | |
|---|
| 2616 | get isNumeric as bool is override |
|---|
| 2617 | return true |
|---|
| 2618 | |
|---|
| 2619 | def _bindImp is override |
|---|
| 2620 | base._bindImp |
|---|
| 2621 | _type = .compiler.decimalType |
|---|
| 2622 | |
|---|
| 2623 | |
|---|
| 2624 | class FractionalLit |
|---|
| 2625 | is partial |
|---|
| 2626 | inherits AtomicLiteral |
|---|
| 2627 | |
|---|
| 2628 | var _value as decimal |
|---|
| 2629 | |
|---|
| 2630 | cue init(token as IToken) |
|---|
| 2631 | require token.value inherits decimal |
|---|
| 2632 | base.init(token) |
|---|
| 2633 | _value = token.value to decimal |
|---|
| 2634 | |
|---|
| 2635 | get isNumeric as bool is override |
|---|
| 2636 | return true |
|---|
| 2637 | |
|---|
| 2638 | get value from var |
|---|
| 2639 | |
|---|
| 2640 | def _bindImp is override |
|---|
| 2641 | base._bindImp |
|---|
| 2642 | _type = .compiler.numberType |
|---|
| 2643 | |
|---|
| 2644 | |
|---|
| 2645 | class FloatLit |
|---|
| 2646 | is partial |
|---|
| 2647 | inherits AtomicLiteral |
|---|
| 2648 | |
|---|
| 2649 | var _value as float |
|---|
| 2650 | |
|---|
| 2651 | cue init(token as IToken) |
|---|
| 2652 | require token.value inherits float |
|---|
| 2653 | base.init(token) |
|---|
| 2654 | _value = token.value to float |
|---|
| 2655 | |
|---|
| 2656 | get isNumeric as bool is override |
|---|
| 2657 | return true |
|---|
| 2658 | |
|---|
| 2659 | def _bindImp is override |
|---|
| 2660 | base._bindImp |
|---|
| 2661 | if _type is nil |
|---|
| 2662 | _type = if(.token.info inherits int, .compiler.floatType(.token.info to int), .compiler.floatType) |
|---|
| 2663 | |
|---|
| 2664 | |
|---|
| 2665 | class IntegerLit |
|---|
| 2666 | is partial |
|---|
| 2667 | inherits AtomicLiteral |
|---|
| 2668 | |
|---|
| 2669 | var _value as int |
|---|
| 2670 | |
|---|
| 2671 | cue init(token as IToken) |
|---|
| 2672 | require token.value inherits int |
|---|
| 2673 | base.init(token) |
|---|
| 2674 | _value = token.value to int |
|---|
| 2675 | |
|---|
| 2676 | cue init(token as IToken, value as int) |
|---|
| 2677 | base.init(token) |
|---|
| 2678 | _value = value |
|---|
| 2679 | |
|---|
| 2680 | get isNumeric as bool is override |
|---|
| 2681 | return true |
|---|
| 2682 | |
|---|
| 2683 | get value from var |
|---|
| 2684 | |
|---|
| 2685 | def canBeAssignedTo(type as IType) as bool is override |
|---|
| 2686 | if type.nonNil.isDescendantOf(.compiler.anyIntType) |
|---|
| 2687 | # to-do: check that the literal fits in the range of the type |
|---|
| 2688 | return true |
|---|
| 2689 | else |
|---|
| 2690 | return base.canBeAssignedTo(type) |
|---|
| 2691 | |
|---|
| 2692 | def _bindImp is override |
|---|
| 2693 | base._bindImp |
|---|
| 2694 | if _type is nil |
|---|
| 2695 | if .token.info inherits int |
|---|
| 2696 | size = .token.info to int |
|---|
| 2697 | signed = size < 0 |
|---|
| 2698 | size = if(signed, -1, +1) * size |
|---|
| 2699 | _type = .compiler.intType(signed, size) |
|---|
| 2700 | else |
|---|
| 2701 | _type = .compiler.intType |
|---|
| 2702 | |
|---|
| 2703 | |
|---|
| 2704 | class NilLiteral |
|---|
| 2705 | is partial |
|---|
| 2706 | inherits AtomicLiteral |
|---|
| 2707 | |
|---|
| 2708 | cue init(token as IToken) |
|---|
| 2709 | base.init(token) |
|---|
| 2710 | |
|---|
| 2711 | def _bindImp is override |
|---|
| 2712 | base._bindImp |
|---|
| 2713 | _type = .compiler.nilType |
|---|
| 2714 | |
|---|
| 2715 | |
|---|
| 2716 | class StringLit inherits AtomicLiteral is partial |
|---|
| 2717 | |
|---|
| 2718 | var _string as String # String contents (with no surrounding quotes or escaping) |
|---|
| 2719 | |
|---|
| 2720 | cue init(token as IToken) |
|---|
| 2721 | require token.which.startsWith('STRING') |
|---|
| 2722 | base.init(token as IToken) |
|---|
| 2723 | _string = token.value to String |
|---|
| 2724 | |
|---|
| 2725 | get isObjectLiteral as bool is override |
|---|
| 2726 | return true |
|---|
| 2727 | |
|---|
| 2728 | get string from var |
|---|
| 2729 | |
|---|
| 2730 | def _bindInt is override |
|---|
| 2731 | if not _type |
|---|
| 2732 | _type = .compiler.stringType |
|---|
| 2733 | base._bindInt |
|---|
| 2734 | |
|---|
| 2735 | def _bindImp is override |
|---|
| 2736 | if not _type |
|---|
| 2737 | _type = .compiler.stringType |
|---|
| 2738 | base._bindImp |
|---|
| 2739 | |
|---|
| 2740 | def toCobraSource as String is override |
|---|
| 2741 | return .token.text |
|---|
| 2742 | |
|---|
| 2743 | |
|---|
| 2744 | class StringSubstLit |
|---|
| 2745 | is partial |
|---|
| 2746 | inherits Literal |
|---|
| 2747 | |
|---|
| 2748 | var _items as List<of Expr> |
|---|
| 2749 | |
|---|
| 2750 | cue init(items as List<of Expr>) |
|---|
| 2751 | require items.count |
|---|
| 2752 | base.init(items[0].token) |
|---|
| 2753 | if true |
|---|
| 2754 | _items = items |
|---|
| 2755 | else |
|---|
| 2756 | # TODO: the efficient, but not debugged case |
|---|
| 2757 | # CC: potential |
|---|
| 2758 | # _items = for item in items if not item inherits StringLit or item.string get item |
|---|
| 2759 | _items = List<of Expr>() |
|---|
| 2760 | for item in _items |
|---|
| 2761 | if item inherits StringLit and not (item to StringLit).string.length |
|---|
| 2762 | continue |
|---|
| 2763 | _items.add(item) |
|---|
| 2764 | assert _items.count |
|---|
| 2765 | |
|---|
| 2766 | def addSubFields is override |
|---|
| 2767 | base.addSubFields |
|---|
| 2768 | .addField('items', _items) |
|---|
| 2769 | |
|---|
| 2770 | get allExprs as Expr* |
|---|
| 2771 | for expr in base.allExprs, yield expr |
|---|
| 2772 | for item in .items, for expr in item.allExprs, yield expr |
|---|
| 2773 | |
|---|
| 2774 | get items from var |
|---|
| 2775 | has Subnodes |
|---|
| 2776 | |
|---|
| 2777 | def toCobraSource as String is override |
|---|
| 2778 | sb = StringBuilder() |
|---|
| 2779 | for item in _items |
|---|
| 2780 | if item inherits StringLit |
|---|
| 2781 | sb.append(item.token.text) |
|---|
| 2782 | else |
|---|
| 2783 | sb.append(item.toCobraSource) |
|---|
| 2784 | return sb.toString |
|---|
| 2785 | |
|---|
| 2786 | def _bindImp is override |
|---|
| 2787 | base._bindImp |
|---|
| 2788 | for item in _items |
|---|
| 2789 | try |
|---|
| 2790 | item.bindImp |
|---|
| 2791 | catch ne as NodeException |
|---|
| 2792 | .compiler.recordError(ne) |
|---|
| 2793 | if not _type |
|---|
| 2794 | _type = .compiler.stringType |
|---|
| 2795 | |
|---|
| 2796 | |
|---|
| 2797 | class FormattedExpr |
|---|
| 2798 | inherits Expr |
|---|
| 2799 | """ |
|---|
| 2800 | This is used exclusively for string substitutions that have formatting: |
|---|
| 2801 | '[i:N]' |
|---|
| 2802 | """ |
|---|
| 2803 | |
|---|
| 2804 | var _expr as Expr |
|---|
| 2805 | var _format as String |
|---|
| 2806 | |
|---|
| 2807 | cue init(expr as Expr, format as String) |
|---|
| 2808 | base.init(expr.token) |
|---|
| 2809 | _expr = expr |
|---|
| 2810 | _format = format |
|---|
| 2811 | |
|---|
| 2812 | get allExprs as Expr* |
|---|
| 2813 | for expr in base.allExprs, yield expr |
|---|
| 2814 | for expr in .expr.allExprs, yield expr |
|---|
| 2815 | |
|---|
| 2816 | get expr from var |
|---|
| 2817 | |
|---|
| 2818 | get format from var |
|---|
| 2819 | |
|---|
| 2820 | def toCobraSource as String is override |
|---|
| 2821 | return '[_expr.toCobraSource]:[_format]' |
|---|
| 2822 | |
|---|
| 2823 | def _bindImp is override |
|---|
| 2824 | base._bindImp |
|---|
| 2825 | _expr.bindImp |
|---|
| 2826 | _type = .compiler.stringType |
|---|
| 2827 | |
|---|
| 2828 | |
|---|
| 2829 | class ThisOrBaseLit inherits AtomicLiteral is abstract, partial |
|---|
| 2830 | """ |
|---|
| 2831 | The "current box" used to be passed to .init rather than referenced in _bindImp, but that |
|---|
| 2832 | didn't work well with partial classes. Also, it's not necessary to creating a ThisOrBaseLit. |
|---|
| 2833 | """ |
|---|
| 2834 | |
|---|
| 2835 | cue init(token as IToken) |
|---|
| 2836 | base.init(token) |
|---|
| 2837 | |
|---|
| 2838 | def _bindImp |
|---|
| 2839 | base._bindImp |
|---|
| 2840 | _type = .compiler.curBox |
|---|
| 2841 | if _type inherits Extension |
|---|
| 2842 | _type = _type.extendedBox |
|---|
| 2843 | |
|---|
| 2844 | |
|---|
| 2845 | class BaseLit |
|---|
| 2846 | is partial |
|---|
| 2847 | inherits ThisOrBaseLit |
|---|
| 2848 | |
|---|
| 2849 | cue init(token as IToken) |
|---|
| 2850 | base.init(token) |
|---|
| 2851 | |
|---|
| 2852 | def checkType is override |
|---|
| 2853 | pass |
|---|
| 2854 | |
|---|
| 2855 | def memberForName(name as String) as IMember? is override |
|---|
| 2856 | assert .didBindImp |
|---|
| 2857 | assert _type |
|---|
| 2858 | t = .receiverType |
|---|
| 2859 | if t.superType |
|---|
| 2860 | t = t.superType |
|---|
| 2861 | return t.memberForName(name) |
|---|
| 2862 | |
|---|
| 2863 | def toCobraSource as String is override |
|---|
| 2864 | return 'base' |
|---|
| 2865 | |
|---|
| 2866 | |
|---|
| 2867 | class ThisLit |
|---|
| 2868 | is partial |
|---|
| 2869 | inherits ThisOrBaseLit |
|---|
| 2870 | |
|---|
| 2871 | # TODO: somewhere it has to be error checked that you're not assigning to |
|---|
| 2872 | # "this" (unless C# allows that which I doubt) |
|---|
| 2873 | |
|---|
| 2874 | cue init(token as IToken) |
|---|
| 2875 | base.init(token) |
|---|
| 2876 | |
|---|
| 2877 | get isExplicit as bool |
|---|
| 2878 | """ |
|---|
| 2879 | Returns true if the `this` literal was explicitly present as in `this.foo` vs. implicitly present as in `.foo`. |
|---|
| 2880 | """ |
|---|
| 2881 | return .token.which == 'THIS' |
|---|
| 2882 | |
|---|
| 2883 | def toCobraSource as String is override |
|---|
| 2884 | return 'this' |
|---|
| 2885 | |
|---|
| 2886 | |
|---|
| 2887 | class VarLit |
|---|
| 2888 | is partial |
|---|
| 2889 | inherits AtomicLiteral |
|---|
| 2890 | |
|---|
| 2891 | var _propertyMember as ProperDexerXetter? |
|---|
| 2892 | var _name as String |
|---|
| 2893 | var _var as IVar? |
|---|
| 2894 | |
|---|
| 2895 | cue init(token as IToken, propertyMember as ProperDexerXetter) |
|---|
| 2896 | base.init(token) |
|---|
| 2897 | _propertyMember = propertyMember |
|---|
| 2898 | _name = '_'+propertyMember.parent.name |
|---|
| 2899 | |
|---|
| 2900 | def _bindImp is override |
|---|
| 2901 | possible = _propertyMember.parent.parentBox.symbolForName(_name, true) |
|---|
| 2902 | if possible is nil |
|---|
| 2903 | .throwError('Cannot find a class variable named "[_name]".') |
|---|
| 2904 | else if possible inherits IVar |
|---|
| 2905 | _var = possible |
|---|
| 2906 | else |
|---|
| 2907 | assert false, possible |
|---|
| 2908 | _type = _var.type |
|---|
| 2909 | _propertyMember = nil # don't need this reference anymore |
|---|
| 2910 | base._bindImp |
|---|
| 2911 | |
|---|
| 2912 | |
|---|
| 2913 | class CompositeLiteral inherits Literal |
|---|
| 2914 | |
|---|
| 2915 | cue init(token as IToken) |
|---|
| 2916 | base.init(token) |
|---|
| 2917 | |
|---|
| 2918 | get isObjectLiteral as bool is override |
|---|
| 2919 | return true |
|---|
| 2920 | |
|---|
| 2921 | |
|---|
| 2922 | class SequenceLit |
|---|
| 2923 | is abstract, partial |
|---|
| 2924 | inherits CompositeLiteral |
|---|
| 2925 | |
|---|
| 2926 | var _exprs as List<of Expr> |
|---|
| 2927 | |
|---|
| 2928 | cue init(token as IToken, exprs as List<of Expr>) |
|---|
| 2929 | base.init(token) |
|---|
| 2930 | _exprs = exprs |
|---|
| 2931 | |
|---|
| 2932 | def addSubFields is override |
|---|
| 2933 | base.addSubFields |
|---|
| 2934 | .addField('exprs', _exprs) |
|---|
| 2935 | |
|---|
| 2936 | get allExprs as Expr* |
|---|
| 2937 | for expr in base.allExprs, yield expr |
|---|
| 2938 | for expr in .exprs, for expr2 in expr.allExprs, yield expr2 |
|---|
| 2939 | |
|---|
| 2940 | get exprs from var |
|---|
| 2941 | has Subnodes |
|---|
| 2942 | |
|---|
| 2943 | get willChangeVar as bool is override |
|---|
| 2944 | for expr in _exprs, if expr.willChangeVar, return true |
|---|
| 2945 | return false |
|---|
| 2946 | |
|---|
| 2947 | def _bindImp is override |
|---|
| 2948 | base._bindImp |
|---|
| 2949 | exceptions = List<of NodeException>() |
|---|
| 2950 | for expr in _exprs |
|---|
| 2951 | try |
|---|
| 2952 | expr.bindImp |
|---|
| 2953 | catch ne as NodeException |
|---|
| 2954 | exceptions.add(ne) |
|---|
| 2955 | if exceptions.count |
|---|
| 2956 | throw NodeMultiException(exceptions) |
|---|
| 2957 | if _type is nil |
|---|
| 2958 | exprs = _exprs |
|---|
| 2959 | if exprs.count == 0 |
|---|
| 2960 | type = .compiler.defaultType |
|---|
| 2961 | else |
|---|
| 2962 | type = exprs[0].type to ! |
|---|
| 2963 | for i in 1 : exprs.count |
|---|
| 2964 | exprs[i].bindImp |
|---|
| 2965 | type = exprs[i].type.greatestCommonDenominatorWith(type) |
|---|
| 2966 | if type.isSystemObjectClass # make dynamic if heterogenous and all non Object |
|---|
| 2967 | allObj = all for expr in exprs get expr.type.isSystemObjectClass |
|---|
| 2968 | if not allObj, type = .compiler.dynamicType |
|---|
| 2969 | if type inherits NilType, type = .compiler.defaultType |
|---|
| 2970 | _type = _makeTypeWith(type) |
|---|
| 2971 | |
|---|
| 2972 | def _makeTypeWith(type as IType) as IType is abstract |
|---|
| 2973 | |
|---|
| 2974 | get brackets as List<of String> is abstract |
|---|
| 2975 | |
|---|
| 2976 | def toCobraSource as String is override |
|---|
| 2977 | brackets = .brackets |
|---|
| 2978 | sb = StringBuilder(brackets[0]) |
|---|
| 2979 | sep = '' |
|---|
| 2980 | for expr in _exprs |
|---|
| 2981 | sb.append(sep) |
|---|
| 2982 | sb.append(expr.toCobraSource) |
|---|
| 2983 | sep = ', ' |
|---|
| 2984 | sb.append(brackets[1]) |
|---|
| 2985 | return sb.toString |
|---|
| 2986 | |
|---|
| 2987 | |
|---|
| 2988 | class ListLit |
|---|
| 2989 | is partial |
|---|
| 2990 | inherits SequenceLit |
|---|
| 2991 | |
|---|
| 2992 | # CC: should just inherit this because no initializers are defined |
|---|
| 2993 | cue init(token as IToken, exprs as List<of Expr>) |
|---|
| 2994 | base.init(token, exprs) |
|---|
| 2995 | |
|---|
| 2996 | def _makeTypeWith(type as IType) as IType is override |
|---|
| 2997 | return .compiler.listOfType.constructedTypeFor([type]) |
|---|
| 2998 | |
|---|
| 2999 | get brackets as List<of String> is override |
|---|
| 3000 | return [r'[', ']'] |
|---|
| 3001 | |
|---|
| 3002 | |
|---|
| 3003 | class ArrayLit inherits SequenceLit is partial |
|---|
| 3004 | |
|---|
| 3005 | # CC: should just inherit this because no initializers are defined |
|---|
| 3006 | cue init(token as IToken, exprs as List<of Expr>) |
|---|
| 3007 | base.init(token, exprs) |
|---|
| 3008 | |
|---|
| 3009 | def _makeTypeWith(type as IType) as IType is override |
|---|
| 3010 | return .typeProvider.arrayType(type) |
|---|
| 3011 | |
|---|
| 3012 | get brackets as List<of String> is override |
|---|
| 3013 | return [r'@[', ']'] |
|---|
| 3014 | |
|---|
| 3015 | |
|---|
| 3016 | class SetLit inherits SequenceLit is partial |
|---|
| 3017 | """ |
|---|
| 3018 | Sets aren't exactly sequences although they appear that way in source code which itself is a sequence of characters. |
|---|
| 3019 | Like lists, sets have one generic type param. |
|---|
| 3020 | |
|---|
| 3021 | Hence it's convenient to inherit SequenceLit, the base class for ListLit and ArrayLit as well. |
|---|
| 3022 | |
|---|
| 3023 | Examples: |
|---|
| 3024 | {1, 2, 3} |
|---|
| 3025 | {'Cobra', 'Python'} |
|---|
| 3026 | {1, Object(), 'Car'} |
|---|
| 3027 | {,} |
|---|
| 3028 | """ |
|---|
| 3029 | |
|---|
| 3030 | cue init(token as IToken, exprs as List<of Expr>) |
|---|
| 3031 | base.init(token, exprs) |
|---|
| 3032 | |
|---|
| 3033 | def _makeTypeWith(type as IType) as IType is override |
|---|
| 3034 | return .compiler.setOfType.constructedTypeFor([type]) |
|---|
| 3035 | |
|---|
| 3036 | get brackets as List<of String> is override |
|---|
| 3037 | return [r'{', '}'] |
|---|
| 3038 | |
|---|
| 3039 | def _bindImp |
|---|
| 3040 | base._bindImp |
|---|
| 3041 | if not .hasError |
|---|
| 3042 | exprs = .exprs |
|---|
| 3043 | c = exprs.count |
|---|
| 3044 | done = false |
|---|
| 3045 | for i in c-1 |
|---|
| 3046 | for j in i+1 : c |
|---|
| 3047 | if exprs[i].isEquivalentTo(exprs[j]) |
|---|
| 3048 | .compiler.warning(exprs[i], 'Duplicate member in set.') |
|---|
| 3049 | done = true |
|---|
| 3050 | break |
|---|
| 3051 | if done, break |
|---|
| 3052 | |
|---|
| 3053 | |
|---|
| 3054 | class DictLit inherits CompositeLiteral is partial |
|---|
| 3055 | |
|---|
| 3056 | var _entries as List<of List<of Expr>> |
|---|
| 3057 | |
|---|
| 3058 | cue init(token as IToken, entries as List<of List<of Expr>>) |
|---|
| 3059 | base.init(token) |
|---|
| 3060 | if CobraCore.willCheckAssert |
|---|
| 3061 | for entry in entries |
|---|
| 3062 | assert entry.count == 2 |
|---|
| 3063 | _entries = entries |
|---|
| 3064 | |
|---|
| 3065 | def addSubFields is override |
|---|
| 3066 | base.addSubFields |
|---|
| 3067 | .addField('entries', _entries) |
|---|
| 3068 | |
|---|
| 3069 | get allExprs as Expr* |
|---|
| 3070 | for expr in base.allExprs, yield expr |
|---|
| 3071 | for entry in .entries, for element in entry, for expr in element.allExprs, yield expr |
|---|
| 3072 | |
|---|
| 3073 | get willChangeVar as bool is override |
|---|
| 3074 | for entry in _entries |
|---|
| 3075 | for expr in entry |
|---|
| 3076 | if expr.willChangeVar |
|---|
| 3077 | return true |
|---|
| 3078 | return false |
|---|
| 3079 | |
|---|
| 3080 | get entries from var |
|---|
| 3081 | |
|---|
| 3082 | def replaceChild(find as INode, replace as INode) as bool |
|---|
| 3083 | didReplace = base.replaceChild(find, replace) |
|---|
| 3084 | if replace inherits Expr |
|---|
| 3085 | for entry in _entries |
|---|
| 3086 | if entry[0] is find |
|---|
| 3087 | entry[0] = replace |
|---|
| 3088 | didReplace = true |
|---|
| 3089 | if entry[1] is find |
|---|
| 3090 | entry[1] = replace |
|---|
| 3091 | didReplace = true |
|---|
| 3092 | return didReplace |
|---|
| 3093 | |
|---|
| 3094 | def _bindImp is override |
|---|
| 3095 | base._bindImp |
|---|
| 3096 | hadError = false |
|---|
| 3097 | for entry in _entries |
|---|
| 3098 | try |
|---|
| 3099 | entry[0].bindImp |
|---|
| 3100 | catch ne as NodeException |
|---|
| 3101 | .compiler.recordError(ne) |
|---|
| 3102 | hadError = true |
|---|
| 3103 | try |
|---|
| 3104 | entry[1].bindImp |
|---|
| 3105 | catch ne as NodeException |
|---|
| 3106 | .compiler.recordError(ne) |
|---|
| 3107 | hadError = true |
|---|
| 3108 | if hadError |
|---|
| 3109 | return |
|---|
| 3110 | if .type is nil |
|---|
| 3111 | entries = _entries |
|---|
| 3112 | if _entries.count == 0 |
|---|
| 3113 | keyType = valueType = .compiler.defaultType |
|---|
| 3114 | else |
|---|
| 3115 | keyType = entries[0][0].type to ! |
|---|
| 3116 | valueType = entries[0][1].type to ! |
|---|
| 3117 | i = 1 |
|---|
| 3118 | keysAllSysObject = valuesAllSysObject = true |
|---|
| 3119 | while i < entries.count |
|---|
| 3120 | keyType = entries[i][0].type.greatestCommonDenominatorWith(keyType) |
|---|
| 3121 | if not entries[i][0].type.isSystemObjectClass, keysAllSysObject = false |
|---|
| 3122 | valueType = entries[i][1].type.greatestCommonDenominatorWith(valueType) |
|---|
| 3123 | if not entries[i][1].type.isSystemObjectClass, valuesAllSysObject = false |
|---|
| 3124 | i += 1 |
|---|
| 3125 | # make types dynamic if heterogenous and all non Object |
|---|
| 3126 | if keyType.isSystemObjectClass and not keysAllSysObject |
|---|
| 3127 | keyType = .compiler.dynamicType |
|---|
| 3128 | if valueType.isSystemObjectClass and not valuesAllSysObject |
|---|
| 3129 | valueType = .compiler.dynamicType |
|---|
| 3130 | if keyType inherits NilType, keyType = .compiler.defaultType |
|---|
| 3131 | if valueType inherits NilType, valueType = .compiler.defaultType |
|---|
| 3132 | _type = .compiler.dictionaryOfType.constructedTypeFor([keyType, valueType]) |
|---|
| 3133 | |
|---|
| 3134 | |
|---|
| 3135 | class ToNilableOrNotExpr |
|---|
| 3136 | is abstract, partial |
|---|
| 3137 | inherits Expr |
|---|
| 3138 | """ |
|---|
| 3139 | The abstract base class for ToNilableExpr and ToNonNilableExpr which have much in common. |
|---|
| 3140 | """ |
|---|
| 3141 | |
|---|
| 3142 | var _rightTok as IToken |
|---|
| 3143 | var _expr as Expr |
|---|
| 3144 | |
|---|
| 3145 | cue init(opToken as IToken, rightTok as IToken, expr as Expr) |
|---|
| 3146 | base.init(opToken) |
|---|
| 3147 | _rightTok = rightTok |
|---|
| 3148 | _expr = expr |
|---|
| 3149 | |
|---|
| 3150 | get allExprs as Expr* |
|---|
| 3151 | for expr in base.allExprs, yield expr |
|---|
| 3152 | for expr in .expr.allExprs, yield expr |
|---|
| 3153 | |
|---|
| 3154 | get rightTok from var |
|---|
| 3155 | |
|---|
| 3156 | get expr from var |
|---|
| 3157 | |
|---|
| 3158 | def _bindImp |
|---|
| 3159 | base._bindImp |
|---|
| 3160 | _expr.bindImp |
|---|
| 3161 | |
|---|
| 3162 | get willChangeVar as bool is override |
|---|
| 3163 | if _expr.willChangeVar, return true |
|---|
| 3164 | return false |
|---|
| 3165 | |
|---|
| 3166 | def toCobraSource as String is override |
|---|
| 3167 | sb = StringBuilder() |
|---|
| 3168 | sb.append(_expr.toCobraSource) |
|---|
| 3169 | sb.append(' to ') |
|---|
| 3170 | sb.append(_rightTok.text) |
|---|
| 3171 | return sb.toString |
|---|
| 3172 | |
|---|
| 3173 | |
|---|
| 3174 | class ToNilableExpr |
|---|
| 3175 | is partial |
|---|
| 3176 | inherits ToNilableOrNotExpr |
|---|
| 3177 | """ |
|---|
| 3178 | Casts an expression to the nilable version of its type. |
|---|
| 3179 | |
|---|
| 3180 | When coding in Cobra: |
|---|
| 3181 | |
|---|
| 3182 | You would do this primarily when the expression is on the right hand side of an assignment and |
|---|
| 3183 | you want the left hand var to by implicitly typed as nilable so you can assign nil to it later. |
|---|
| 3184 | |
|---|
| 3185 | ex: name = obj.name to ? |
|---|
| 3186 | ex: age = customer.age to ? |
|---|
| 3187 | """ |
|---|
| 3188 | |
|---|
| 3189 | cue init(opToken as IToken, rightTok as IToken, expr as Expr) |
|---|
| 3190 | base.init(opToken, rightTok, expr) |
|---|
| 3191 | |
|---|
| 3192 | def _bindImp |
|---|
| 3193 | base._bindImp |
|---|
| 3194 | if not _expr.hasError |
|---|
| 3195 | assert _expr.type |
|---|
| 3196 | if _expr.type inherits NilableType |
|---|
| 3197 | _type = _expr.type |
|---|
| 3198 | .compiler.warning(this, 'The given expression is already nilable so "to ?" is redundant. You can remove it.') # TODO: needs test case |
|---|
| 3199 | else |
|---|
| 3200 | _type = .typeProvider.nilableType(_expr.type to !) |
|---|
| 3201 | |
|---|
| 3202 | |
|---|
| 3203 | class ToNonNilableExpr |
|---|
| 3204 | is partial |
|---|
| 3205 | inherits ToNilableOrNotExpr |
|---|
| 3206 | """ |
|---|
| 3207 | Casts an expression to the non-nilable version of its type. |
|---|
| 3208 | |
|---|
| 3209 | When coding in Cobra: |
|---|
| 3210 | |
|---|
| 3211 | You would do this when trying to use a nilable expression where a non-nil type is expected and |
|---|
| 3212 | you know that at run-time the expression won't actually by nil (presumably due to the logic of |
|---|
| 3213 | your code). |
|---|
| 3214 | |
|---|
| 3215 | Or you might do this to affect type inference. |
|---|
| 3216 | |
|---|
| 3217 | ex: name = obj.name to ! |
|---|
| 3218 | ex: obj.foo(age to !) |
|---|
| 3219 | """ |
|---|
| 3220 | |
|---|
| 3221 | cue init(opToken as IToken, rightTok as IToken, expr as Expr) |
|---|
| 3222 | base.init(opToken, rightTok, expr) |
|---|
| 3223 | |
|---|
| 3224 | def _bindImp |
|---|
| 3225 | base._bindImp |
|---|
| 3226 | if (et = _expr.type) inherits NilableType |
|---|
| 3227 | _type = et.nonNil |
|---|
| 3228 | else |
|---|
| 3229 | .compiler.warning(this, 'The given expression is already non-nilable so "to !" is redundant. You can remove it.') # TODO: needs test case |
|---|
| 3230 | _type = _expr.type |
|---|
| 3231 | |
|---|
| 3232 | |
|---|
| 3233 | class ChainedCompareExpr inherits Expr is partial |
|---|
| 3234 | |
|---|
| 3235 | var _items as List<of Expr> |
|---|
| 3236 | var _operations as List<of String> |
|---|
| 3237 | |
|---|
| 3238 | cue init(opToken as IToken, items as List<of Expr>, operations as List<of String>) |
|---|
| 3239 | require |
|---|
| 3240 | # should not happen, there is an error in the compiler if this occurs |
|---|
| 3241 | items.count == operations.count + 1 |
|---|
| 3242 | items.count > 2 |
|---|
| 3243 | body |
|---|
| 3244 | base.init(opToken) |
|---|
| 3245 | _items, _operations = items, operations |
|---|
| 3246 | |
|---|
| 3247 | get allExprs as Expr* |
|---|
| 3248 | for expr in base.allExprs, yield expr |
|---|
| 3249 | for item in .items, for expr in item.allExprs, yield expr |
|---|
| 3250 | |
|---|
| 3251 | get items from var |
|---|
| 3252 | |
|---|
| 3253 | def _bindImp is override |
|---|
| 3254 | base._bindImp |
|---|
| 3255 | itemIndex = 1 |
|---|
| 3256 | for operation in _operations |
|---|
| 3257 | _items[itemIndex - 1].bindImp |
|---|
| 3258 | _items[itemIndex].bindImp |
|---|
| 3259 | _willCompare(_items[itemIndex - 1], operation, _items[itemIndex]) |
|---|
| 3260 | itemIndex += 1 |
|---|
| 3261 | _type = .compiler.boolType |
|---|
| 3262 | |
|---|
| 3263 | def _willCompare(left as Expr, op as String, right as Expr) as bool |
|---|
| 3264 | if left.type is nil or right.type is nil |
|---|
| 3265 | assert .hasError |
|---|
| 3266 | return false |
|---|
| 3267 | leftType = left.type to ! |
|---|
| 3268 | rightType = right.type to ! |
|---|
| 3269 | innerMsg as String? |
|---|
| 3270 | # cannot invoke .isAssignableTo directly on the types because if one is nilable and the other is not, you can get false, even though that doesn't apply for "is". So use isEquatableTo and isComparableTo |
|---|
| 3271 | if op in ['EQ', 'NE', 'IS', 'ISNOT'] |
|---|
| 3272 | if not leftType.isEquatableTo(rightType) |
|---|
| 3273 | innerMsg = if(op.startsWith('IS'), 'can never be identical', 'cannot be equated') |
|---|
| 3274 | else if op in ['IS', 'ISNOT'] |
|---|
| 3275 | # Essentially "is" and "is not" are for reference types. Give a warning when used for value types. |
|---|
| 3276 | if not leftType.isDynamicOrPassThrough and not rightType.isDynamicOrPassThrough |
|---|
| 3277 | opName = if(op=='IS', 'is', 'is not') |
|---|
| 3278 | altName = if(op=='IS', '==', '<>') |
|---|
| 3279 | if leftType inherits NilableType and rightType inherits NilType |
|---|
| 3280 | pass |
|---|
| 3281 | else if not leftType.isReference and not rightType.isReference |
|---|
| 3282 | .compiler.warning(this, 'Both the left and right sides of "[opName]" are value types ("[leftType.name]" and "[rightType.name]"), but "[opName]" applies to reference types. Use "[altName]" instead.') |
|---|
| 3283 | # interestingly, I don't think the following ever happen in practice... |
|---|
| 3284 | else if not leftType.isReference and not rightType.isNilableAndDescendantOf(leftType) |
|---|
| 3285 | .compiler.warning(this, 'The left side of "[opName]" is a value type ("[leftType.name]") while the right side is an incompatible reference type ("[rightType.name]").') |
|---|
| 3286 | else if not rightType.isReference and not leftType.isNilableAndDescendantOf(rightType) |
|---|
| 3287 | .compiler.warning(this, 'The right side of "[opName]" is a value type ("[rightType.name]") while the left side is an incompatible reference type ("[leftType.name]").') |
|---|
| 3288 | else if left.isObjectLiteral or right.isObjectLiteral |
|---|
| 3289 | .compiler.warning(this, 'Do not use the identity operator "[opName]" with an object literal. Use an equality operator such as "==" or "<>".') |
|---|
| 3290 | else |
|---|
| 3291 | if not leftType.isComparableTo(rightType) |
|---|
| 3292 | innerMsg = 'cannot be compared' |
|---|
| 3293 | if innerMsg |
|---|
| 3294 | typeNames = if(left.type.name==right.type.name, 'type ("[left.type.name]")', 'types ("[left.type.name]" and "[right.type.name]")') |
|---|
| 3295 | msg = 'The left and right sides of the "[.token.text]" expression [innerMsg] because of their [typeNames].' |
|---|
| 3296 | if op == 'IS' and right.type.isDescendantOf(.compiler.typeType) # C# |
|---|
| 3297 | msg += ' Maybe you should try "inherits" instead of "is".' |
|---|
| 3298 | .throwError(msg) |
|---|
| 3299 | return false |
|---|
| 3300 | return true |
|---|
| 3301 | |
|---|
| 3302 | def toCobraSource as String is override |
|---|
| 3303 | sb = StringBuilder(_items[0].toCobraSource) |
|---|
| 3304 | sep = ' ' |
|---|
| 3305 | for index in _operations.count |
|---|
| 3306 | sb.append(sep) |
|---|
| 3307 | branch _operations[index] |
|---|
| 3308 | on 'EQ', sb.append('==') |
|---|
| 3309 | on 'NE', sb.append('<>') |
|---|
| 3310 | on 'GT', sb.append('>') |
|---|
| 3311 | on 'LT', sb.append('<') |
|---|
| 3312 | on 'GE', sb.append('>=') |
|---|
| 3313 | on 'LE', sb.append('<=') |
|---|
| 3314 | on 'IS', sb.append('is') |
|---|
| 3315 | on 'ISNOT', sb.append('is not') |
|---|
| 3316 | sb.append(sep) |
|---|
| 3317 | sb.append(_items[index + 1].toCobraSource) |
|---|
| 3318 | return sb.toString |
|---|