| 1 | class Stmt |
|---|
| 2 | is abstract, partial |
|---|
| 3 | inherits SyntaxNode |
|---|
| 4 | |
|---|
| 5 | var _parent as INode? |
|---|
| 6 | |
|---|
| 7 | cue init(token as IToken) |
|---|
| 8 | base.init(token) |
|---|
| 9 | |
|---|
| 10 | pro parent from var |
|---|
| 11 | """ |
|---|
| 12 | The node that the statement belongs to. This is being set on an as-needed basis. |
|---|
| 13 | Typical needs are for error checking, but could also include other purposes such as code generation. |
|---|
| 14 | The common place to set a node's `parent` is in the `_bindImp` of the parent |
|---|
| 15 | """ |
|---|
| 16 | |
|---|
| 17 | get lastToken as IToken |
|---|
| 18 | return .token |
|---|
| 19 | |
|---|
| 20 | def afterParserRecognizesStatement |
|---|
| 21 | pass |
|---|
| 22 | |
|---|
| 23 | def afterStatementBindImp |
|---|
| 24 | """ |
|---|
| 25 | Invoked to let expressions know when they are used as statements. This default |
|---|
| 26 | implementation does nothing. Invoked by OneBlockCodeMember and BlockStmt. |
|---|
| 27 | """ |
|---|
| 28 | pass |
|---|
| 29 | |
|---|
| 30 | def noAssignmentAllowed(expr as Expr) |
|---|
| 31 | """ |
|---|
| 32 | Utility method for IfStmt and WhileStmt which do not allow assignment in |
|---|
| 33 | their expressions. |
|---|
| 34 | """ |
|---|
| 35 | if expr inherits BinaryOpExpr |
|---|
| 36 | if expr.op=='ASSIGN' |
|---|
| 37 | .throwError('Cannot make an assignment in a flow control expression. Change to == or make the assignment just above.') |
|---|
| 38 | else if expr.op=='BANG_EQUALS' |
|---|
| 39 | .throwError('Cannot use an augmented assignment in a flow control expression. If you meant "does not equal" then use "<>". "a != b" means "a = a ! b" where "!" is an operator concerning nil.') |
|---|
| 40 | else if expr.op.endsWith('_EQUALS') |
|---|
| 41 | .throwError('Cannot use an augmented assignment in a flow control expression. Change to == or make the assignment just above.') |
|---|
| 42 | |
|---|
| 43 | def bindVar(ve as NameExpr) as IVar |
|---|
| 44 | """ |
|---|
| 45 | Computes the _var from the _varExpr which is either IdentifierExpr or AsExpr. |
|---|
| 46 | May call `_error` and `inferredType` as needed. |
|---|
| 47 | Typical use: _var = .bindVar(_varExpr) |
|---|
| 48 | """ |
|---|
| 49 | # TODO: would prefer to do this as a mixin with two class vars: |
|---|
| 50 | # var _varExpr as NameExpr |
|---|
| 51 | # var _var as IVar? |
|---|
| 52 | varr as IVar? |
|---|
| 53 | if ve inherits IdentifierExpr |
|---|
| 54 | # find or infer |
|---|
| 55 | definition as INamedNode? |
|---|
| 56 | canBeUndottedMember = ve.name.canBeUndottedMemberName |
|---|
| 57 | usingExistingLocal = false |
|---|
| 58 | if canBeUndottedMember |
|---|
| 59 | assert .compiler.boxStack.count |
|---|
| 60 | definition = .compiler.symbolForName(ve.name, false) to passthrough |
|---|
| 61 | if definition is nil |
|---|
| 62 | ve.throwUnknownIdError |
|---|
| 63 | else |
|---|
| 64 | definition = .compiler.findLocal(ve.name) |
|---|
| 65 | # infer a local var |
|---|
| 66 | it = .inferredType |
|---|
| 67 | if it is nil |
|---|
| 68 | .throwError('Cannot infer the type for "[ve.name]" from the "for" loop.') |
|---|
| 69 | if definition is nil |
|---|
| 70 | definition = LocalVar(ve.token, it).bindAll to INamedNode # CC: axe cast after "as this" |
|---|
| 71 | .compiler.curCodeMember.addLocal(definition to LocalVar) |
|---|
| 72 | else |
|---|
| 73 | # using an existing local. that's fine as long as the types are the name |
|---|
| 74 | usingExistingLocal = true |
|---|
| 75 | if definition inherits IVar |
|---|
| 76 | varr = definition |
|---|
| 77 | if usingExistingLocal |
|---|
| 78 | ve.bindImp # can't do this at the beginning of this method as it interferes with type inference. but here, it's an existing var so no "Unknown identifier" error will occur |
|---|
| 79 | assert definition.type |
|---|
| 80 | if definition.type is .compiler.passThroughType |
|---|
| 81 | varr.type = definition.type |
|---|
| 82 | else if not it.isAssignableTo(definition.type to !) |
|---|
| 83 | .throwError('Cannot redeclare "[ve.name]" as "[it.name]" because it was declared as "[definition.type.name]" earlier.') # TODO: would be nice to give the location of the other declaration |
|---|
| 84 | else |
|---|
| 85 | .throwError('The definition of "[ve.name]" is not a variable which is what the "for" loop requires.') |
|---|
| 86 | ve.bindImp |
|---|
| 87 | else if ve inherits AsExpr |
|---|
| 88 | ve.bindImp |
|---|
| 89 | assert ve.definition |
|---|
| 90 | varr = ve.definition |
|---|
| 91 | else |
|---|
| 92 | throw FallThroughException(ve) |
|---|
| 93 | return varr to ! |
|---|
| 94 | |
|---|
| 95 | def inferredType as IType? |
|---|
| 96 | """ |
|---|
| 97 | Used by .bindVar so that the subclasses can implement their inferred type logic. |
|---|
| 98 | """ |
|---|
| 99 | return nil |
|---|
| 100 | |
|---|
| 101 | var _canSetLine as bool |
|---|
| 102 | |
|---|
| 103 | def bindImp as dynamic is override |
|---|
| 104 | # CC: and ensure result inherits Stmt |
|---|
| 105 | base.bindImp |
|---|
| 106 | assert .didBindImp, this |
|---|
| 107 | .postBindImp |
|---|
| 108 | return .bindImpResult |
|---|
| 109 | |
|---|
| 110 | def postBindImp |
|---|
| 111 | .help |
|---|
| 112 | |
|---|
| 113 | def help |
|---|
| 114 | if not .hasError and .isHelpRequested |
|---|
| 115 | topic = .token.text + ' statement' |
|---|
| 116 | c = .compiler |
|---|
| 117 | hg = HelpGenerator(topic, this, c.curBoxMember, c.curBox) |
|---|
| 118 | hg.searchTerms.add(topic) |
|---|
| 119 | hg.generate |
|---|
| 120 | .compiler.warning(this, '@help at "[hg.path]".') |
|---|
| 121 | |
|---|
| 122 | def _bindImp |
|---|
| 123 | base._bindImp |
|---|
| 124 | _canSetLine = .compiler.willTrackLocals |
|---|
| 125 | # note that the code member stack can be empty because of class vars (ex: var _foo = true) |
|---|
| 126 | # even the box stack can be empty because of assembly; has ... |
|---|
| 127 | |
|---|
| 128 | |
|---|
| 129 | class AssertStmt |
|---|
| 130 | is partial |
|---|
| 131 | inherits Stmt |
|---|
| 132 | |
|---|
| 133 | var _expr as Expr |
|---|
| 134 | var _info as Expr? |
|---|
| 135 | |
|---|
| 136 | cue init(token as IToken, expr as Expr, info as Expr?) |
|---|
| 137 | base.init(token) |
|---|
| 138 | _expr = TruthExpr(expr) |
|---|
| 139 | _info = info |
|---|
| 140 | |
|---|
| 141 | def addSubFields |
|---|
| 142 | base.addSubFields |
|---|
| 143 | .addField('expr', _expr) |
|---|
| 144 | .addField('info', _info) |
|---|
| 145 | |
|---|
| 146 | get expr from var |
|---|
| 147 | |
|---|
| 148 | get info from var |
|---|
| 149 | |
|---|
| 150 | def _innerClone |
|---|
| 151 | base._innerClone |
|---|
| 152 | _expr = _expr.clone |
|---|
| 153 | if _info, _info = _info.clone |
|---|
| 154 | |
|---|
| 155 | def _bindImp |
|---|
| 156 | base._bindImp |
|---|
| 157 | _expr.bindImp |
|---|
| 158 | e = _expr |
|---|
| 159 | if e.willChangeVar |
|---|
| 160 | if e inherits TruthExpr |
|---|
| 161 | e = e.expr |
|---|
| 162 | if e inherits AbstractAssignExpr |
|---|
| 163 | # TODO: should also cover any assignment expressions anywhere inside the expression |
|---|
| 164 | .throwError('Assert condition is an assignment. Since asserts may be suppressed these conditions should not have side effects. Perhaps you meant to do a comparison "==" instead of assignment "=".') |
|---|
| 165 | else |
|---|
| 166 | .throwError('Condition has a side effect. Since asserts may be suppressed at compile-time or run-time, their conditions cannot have side effects.') |
|---|
| 167 | # Augment any warning about non nilable expr evaluate to true |
|---|
| 168 | .compiler.augmentWarning(this, 'always evaluate to true because it is not nilable', _ |
|---|
| 169 | 'You can remove the expression', _ |
|---|
| 170 | 'This assertion is always true. You can remove the assertion or correct the condition') |
|---|
| 171 | if _info, _info.bindImp |
|---|
| 172 | |
|---|
| 173 | |
|---|
| 174 | class CompileTimeTraceStmt inherits Stmt |
|---|
| 175 | |
|---|
| 176 | # to-do: retire after @help is more mature |
|---|
| 177 | |
|---|
| 178 | var _expr as Expr |
|---|
| 179 | |
|---|
| 180 | cue init(token as IToken, expr as Expr) |
|---|
| 181 | base.init(token) |
|---|
| 182 | _expr = expr |
|---|
| 183 | |
|---|
| 184 | def addSubFields |
|---|
| 185 | base.addSubFields |
|---|
| 186 | .addField('expr', _expr) |
|---|
| 187 | |
|---|
| 188 | get expr from var |
|---|
| 189 | |
|---|
| 190 | def _innerClone |
|---|
| 191 | base._innerClone |
|---|
| 192 | _expr = _expr.clone |
|---|
| 193 | |
|---|
| 194 | def _bindImp |
|---|
| 195 | base._bindImp |
|---|
| 196 | expr = _expr |
|---|
| 197 | # this output will likely need to be done as warning output so it works with IDEs |
|---|
| 198 | prefix = ' *' |
|---|
| 199 | print |
|---|
| 200 | print '* Compile-time trace at [.token.toTechString]' |
|---|
| 201 | print '[prefix] before bind imp on expression: [expr.toCobraSource] = [expr]' |
|---|
| 202 | expr.bindImp |
|---|
| 203 | print '[prefix] after bind imp on expression: [expr.toCobraSource] = [expr]' |
|---|
| 204 | dexpr = expr to dynamic |
|---|
| 205 | try |
|---|
| 206 | defi = dexpr.definition |
|---|
| 207 | if defi, print '[prefix] definition = [defi]' |
|---|
| 208 | catch DynamicOperationException |
|---|
| 209 | print '[prefix] no definition' |
|---|
| 210 | try |
|---|
| 211 | type = dexpr.type |
|---|
| 212 | if type, print '[prefix] type = [type]' |
|---|
| 213 | catch DynamicOperationException |
|---|
| 214 | print '[prefix] no type' |
|---|
| 215 | |
|---|
| 216 | |
|---|
| 217 | class BranchStmt inherits Stmt is partial |
|---|
| 218 | |
|---|
| 219 | cue init(token as IToken, expr as Expr, onParts as List<of BranchOnPart>, elsePart as BlockStmt?) |
|---|
| 220 | base.init(token) |
|---|
| 221 | _expr = expr |
|---|
| 222 | _onParts = onParts |
|---|
| 223 | _elsePart = elsePart |
|---|
| 224 | |
|---|
| 225 | def addSubFields |
|---|
| 226 | base.addSubFields |
|---|
| 227 | .addField('expr', .expr) |
|---|
| 228 | .addField('onParts', .onParts) |
|---|
| 229 | .addField('elsePart', .elsePart) |
|---|
| 230 | |
|---|
| 231 | get expr from var as Expr |
|---|
| 232 | get onParts from var as List<of BranchOnPart> |
|---|
| 233 | get elsePart from var as BlockStmt? |
|---|
| 234 | |
|---|
| 235 | def _innerClone |
|---|
| 236 | base._innerClone |
|---|
| 237 | _expr = _expr.clone |
|---|
| 238 | _onParts = _onParts.toList |
|---|
| 239 | for i in .onParts.count, .onParts[i] = .onParts[i].clone |
|---|
| 240 | _elsePart = _elsePart.clone |
|---|
| 241 | |
|---|
| 242 | def _bindImp |
|---|
| 243 | base._bindImp |
|---|
| 244 | _expr.bindImp |
|---|
| 245 | uniqueOnParts = Set<of String>() |
|---|
| 246 | for onPart in _onParts |
|---|
| 247 | onPart.bindImp |
|---|
| 248 | for expr in onPart.exprs |
|---|
| 249 | source = expr.toCobraSource |
|---|
| 250 | if source in uniqueOnParts, expr.recordError('Branch case "[source]" was already used.') |
|---|
| 251 | uniqueOnParts.add(source) |
|---|
| 252 | if _elsePart, _elsePart.bindImp |
|---|
| 253 | |
|---|
| 254 | |
|---|
| 255 | class BranchOnPart inherits Node |
|---|
| 256 | |
|---|
| 257 | var _exprs as List<of Expr> |
|---|
| 258 | var _block as BlockStmt |
|---|
| 259 | |
|---|
| 260 | cue init(exprs as List<of Expr>, block as BlockStmt) |
|---|
| 261 | base.init |
|---|
| 262 | _exprs = exprs |
|---|
| 263 | _block = block |
|---|
| 264 | |
|---|
| 265 | def addSubFields |
|---|
| 266 | base.addSubFields |
|---|
| 267 | .addField('exprs', _exprs) |
|---|
| 268 | .addField('block', _block) |
|---|
| 269 | |
|---|
| 270 | get block from var |
|---|
| 271 | |
|---|
| 272 | get exprs from var |
|---|
| 273 | has Subnodes |
|---|
| 274 | |
|---|
| 275 | def _innerClone |
|---|
| 276 | base._innerClone |
|---|
| 277 | _exprs = _exprs.toList2 |
|---|
| 278 | for i in _exprs.count, _exprs[i] = _exprs[i].clone |
|---|
| 279 | _block = _block.clone |
|---|
| 280 | |
|---|
| 281 | def _bindImp |
|---|
| 282 | base._bindImp |
|---|
| 283 | for expr in _exprs, expr.bindImp |
|---|
| 284 | _block.bindImp |
|---|
| 285 | |
|---|
| 286 | |
|---|
| 287 | class BlockStmt |
|---|
| 288 | is partial |
|---|
| 289 | inherits Stmt |
|---|
| 290 | """ |
|---|
| 291 | A BlockStmt holds a series of statements which are the target of complex statement such |
|---|
| 292 | if, while, etc. BlockStmts are *not* the target of a method or property. |
|---|
| 293 | """ |
|---|
| 294 | |
|---|
| 295 | var _stmts as List<of Stmt> |
|---|
| 296 | var _ifInheritsVar as IVar? |
|---|
| 297 | var _ifInheritsType as IType? |
|---|
| 298 | var _curStmtIndex as int # to implement .replaceChild |
|---|
| 299 | |
|---|
| 300 | cue init(token as IToken, stmts as List<of Stmt>) |
|---|
| 301 | base.init(token) |
|---|
| 302 | _stmts = stmts |
|---|
| 303 | |
|---|
| 304 | get lastToken as IToken |
|---|
| 305 | return if(_stmts.count, _stmts[_stmts.count-1].lastToken, base.lastToken) |
|---|
| 306 | |
|---|
| 307 | def addSubFields |
|---|
| 308 | base.addSubFields |
|---|
| 309 | .addField('stmts', _stmts) |
|---|
| 310 | |
|---|
| 311 | def setIfInherits(varr as IVar, type as IType) |
|---|
| 312 | _ifInheritsVar = varr |
|---|
| 313 | _ifInheritsType = type |
|---|
| 314 | |
|---|
| 315 | get stmts from var |
|---|
| 316 | has Subnodes |
|---|
| 317 | |
|---|
| 318 | def replaceChild(find as INode, replace as INode) as bool |
|---|
| 319 | # using _curStmtIndex is faster and blocks can get potentially large |
|---|
| 320 | if _curStmtIndex < _stmts.count and _stmts[_curStmtIndex] is find |
|---|
| 321 | _stmts[_curStmtIndex] = replace to Stmt |
|---|
| 322 | return true |
|---|
| 323 | else |
|---|
| 324 | return base.replaceChild(find, replace) |
|---|
| 325 | |
|---|
| 326 | def _innerClone |
|---|
| 327 | base._innerClone |
|---|
| 328 | _stmts = _stmts.toList2 |
|---|
| 329 | for i in _stmts.count, _stmts[i] = _stmts[i].clone |
|---|
| 330 | # not expecting a clone after binding imp: |
|---|
| 331 | assert _ifInheritsVar is nil |
|---|
| 332 | assert _ifInheritsType is nil |
|---|
| 333 | |
|---|
| 334 | def _bindImp |
|---|
| 335 | base._bindImp |
|---|
| 336 | _curStmtIndex = 0 |
|---|
| 337 | for stmt in _stmts.toArray |
|---|
| 338 | stmt.parent = this |
|---|
| 339 | try |
|---|
| 340 | stmt.bindImp |
|---|
| 341 | stmt.afterStatementBindImp # to let expressions know when they are used as statements |
|---|
| 342 | catch ne as NodeException |
|---|
| 343 | .compiler.recordError(ne) |
|---|
| 344 | _curStmtIndex += 1 |
|---|
| 345 | |
|---|
| 346 | |
|---|
| 347 | class BreakStmt |
|---|
| 348 | is partial |
|---|
| 349 | inherits Stmt |
|---|
| 350 | |
|---|
| 351 | cue init(tok as IToken) |
|---|
| 352 | base.init(tok) |
|---|
| 353 | |
|---|
| 354 | |
|---|
| 355 | class ContinueStmt |
|---|
| 356 | is partial |
|---|
| 357 | inherits Stmt |
|---|
| 358 | |
|---|
| 359 | cue init(tok as IToken) |
|---|
| 360 | base.init(tok) |
|---|
| 361 | |
|---|
| 362 | |
|---|
| 363 | class ExpectStmt inherits Stmt is partial |
|---|
| 364 | |
|---|
| 365 | var _exceptionTypeNode as ITypeProxy |
|---|
| 366 | var _exceptionType as IType? |
|---|
| 367 | var _block as BlockStmt |
|---|
| 368 | var _varNumber as int |
|---|
| 369 | |
|---|
| 370 | get block from var |
|---|
| 371 | |
|---|
| 372 | cue init(token as IToken, exceptionTypeNode as ITypeProxy, block as BlockStmt) |
|---|
| 373 | base.init(token) |
|---|
| 374 | _exceptionTypeNode = exceptionTypeNode |
|---|
| 375 | _block = block |
|---|
| 376 | |
|---|
| 377 | get lastToken as IToken is override |
|---|
| 378 | return _block.lastToken |
|---|
| 379 | |
|---|
| 380 | def addSubFields |
|---|
| 381 | base.addSubFields |
|---|
| 382 | .addField('exceptionTypeNode', _exceptionTypeNode) |
|---|
| 383 | .addField('exceptionType', _exceptionType) |
|---|
| 384 | .addField('block', _block) |
|---|
| 385 | |
|---|
| 386 | def _innerClone |
|---|
| 387 | base._innerClone |
|---|
| 388 | _block = _block.clone |
|---|
| 389 | |
|---|
| 390 | def _bindImp |
|---|
| 391 | base._bindImp |
|---|
| 392 | if _exceptionType is nil |
|---|
| 393 | _exceptionType = (_exceptionTypeNode.bindAll to ITypeProxy).realType # CC: axe cast |
|---|
| 394 | assert _exceptionType, this |
|---|
| 395 | excClass = .compiler.exceptionType |
|---|
| 396 | if not _exceptionType.isDescendantOf(excClass) |
|---|
| 397 | .throwError('Can only use Exception and its descendants for "expect". "[_exceptionType.name]" does not inherit the Exception class.') |
|---|
| 398 | .compiler.curBox.makeNextPrivateSerialNumber # the code gen typically uses two serial nums |
|---|
| 399 | _varNumber = .compiler.curBox.makeNextPrivateSerialNumber |
|---|
| 400 | _block.bindImp |
|---|
| 401 | |
|---|
| 402 | |
|---|
| 403 | class ForStmt inherits Stmt is partial |
|---|
| 404 | """ |
|---|
| 405 | Abstract base class for ForNumericStmt and ForEnumerablebase. |
|---|
| 406 | """ |
|---|
| 407 | |
|---|
| 408 | cue init(token as IToken, varr as NameExpr, block as BlockStmt) |
|---|
| 409 | base.init(token) |
|---|
| 410 | _varExpr = varr |
|---|
| 411 | _block = block |
|---|
| 412 | |
|---|
| 413 | get varExpr from var as NameExpr |
|---|
| 414 | |
|---|
| 415 | get var from var as IVar? |
|---|
| 416 | |
|---|
| 417 | get block from var as BlockStmt |
|---|
| 418 | |
|---|
| 419 | get lastToken as IToken is override |
|---|
| 420 | return _block.lastToken |
|---|
| 421 | |
|---|
| 422 | def addSubFields |
|---|
| 423 | base.addSubFields |
|---|
| 424 | .addField('varExpr', .varExpr) |
|---|
| 425 | .addField('var', .var) |
|---|
| 426 | .addField('block', .block) |
|---|
| 427 | |
|---|
| 428 | def _innerClone |
|---|
| 429 | base._innerClone |
|---|
| 430 | _varExpr = _varExpr.clone |
|---|
| 431 | _block = _block.clone |
|---|
| 432 | |
|---|
| 433 | def _bindImp |
|---|
| 434 | base._bindImp |
|---|
| 435 | _varExpr.bindImp |
|---|
| 436 | if _varExpr.definition |
|---|
| 437 | if _varExpr.definition inherits IVar |
|---|
| 438 | _var = _varExpr.definition |
|---|
| 439 | else |
|---|
| 440 | .throwError('Expecting a variable not a [_varExpr.definition.getType.name].') # TODO: what's the best way to report what was found? |
|---|
| 441 | else |
|---|
| 442 | assert _varExpr.hasError |
|---|
| 443 | #if not var.isTracked TODO disabled this to get tests\200-classes\804-test.cobra working. |
|---|
| 444 | if false |
|---|
| 445 | varr = .var |
|---|
| 446 | existingVar = .compiler.curCodeMember.findLocal(varr.name) |
|---|
| 447 | if existingVar |
|---|
| 448 | # that's fine as long as the types are the name |
|---|
| 449 | if varr.type is .compiler.passThroughType |
|---|
| 450 | varr.type = existingVar.type |
|---|
| 451 | else if existingVar.type is not varr.type |
|---|
| 452 | .throwError('Cannot redeclare "[varr.name]" as "[varr.type]" because it was declared as "[existingVar.type]" earlier.') # TODO: would be nice to give the location of the other declaration |
|---|
| 453 | |
|---|
| 454 | # TODO: check for existing variables |
|---|
| 455 | # if 1 |
|---|
| 456 | # ns = .compiler.nameSpaceStack.peek |
|---|
| 457 | # existingVar = ns.findLocal(varr.name) |
|---|
| 458 | # if existingVar |
|---|
| 459 | # # that's fine as long as the types are the name |
|---|
| 460 | # if varr.type is .compiler.passThroughType |
|---|
| 461 | # varr.type = existingVar.type |
|---|
| 462 | # else if existingVar.type is not varr.type |
|---|
| 463 | # .error('Cannot redeclare "[varr.name]" as "[varr.type]" because it was declared as "[existingVar.type]" earlier.') |
|---|
| 464 | # else |
|---|
| 465 | # .compiler.nameStack.peek.pushName(varr) # TODO should be pushSymbol() or pushVar() |
|---|
| 466 | # varr.bindAll # CallExpr() wants its definition to have bound int, so here we call bindAll instead of bindImp |
|---|
| 467 | _block.bindImp |
|---|
| 468 | # TODO axe this |
|---|
| 469 | #if not varr.isTracked |
|---|
| 470 | # .compiler.nameStack.peek.pop # TODO should be popSymbol() or popVar() |
|---|
| 471 | |
|---|
| 472 | |
|---|
| 473 | class OldForNumericStmt |
|---|
| 474 | is partial |
|---|
| 475 | inherits ForStmt |
|---|
| 476 | |
|---|
| 477 | var _start as Expr |
|---|
| 478 | var _stop as Expr |
|---|
| 479 | var _dir as int |
|---|
| 480 | var _step as Expr? |
|---|
| 481 | |
|---|
| 482 | cue init(token as IToken, varr as NameExpr, start as Expr, stopp as Expr, dir as int, stepp as Expr?, block as BlockStmt) |
|---|
| 483 | require |
|---|
| 484 | dir==-1 or dir==+1 |
|---|
| 485 | body |
|---|
| 486 | base.init(token, varr, block) |
|---|
| 487 | _start = start |
|---|
| 488 | _stop = stopp |
|---|
| 489 | _dir = dir |
|---|
| 490 | _step = stepp |
|---|
| 491 | |
|---|
| 492 | def addSubFields |
|---|
| 493 | base.addSubFields |
|---|
| 494 | .addField('start', _start) |
|---|
| 495 | .addField('stop', _stop) |
|---|
| 496 | .addField('dir', _dir) |
|---|
| 497 | .addField('step', _step) |
|---|
| 498 | .addField('block', _block) |
|---|
| 499 | |
|---|
| 500 | def _innerClone |
|---|
| 501 | base._innerClone |
|---|
| 502 | _start = _start.clone |
|---|
| 503 | _stop = _stop.clone |
|---|
| 504 | if _step, _step = _step.clone |
|---|
| 505 | |
|---|
| 506 | def _bindImp |
|---|
| 507 | _start.bindImp |
|---|
| 508 | _stop.bindImp |
|---|
| 509 | if _step |
|---|
| 510 | _step.bindImp |
|---|
| 511 | _var = .bindVar(_varExpr) |
|---|
| 512 | base._bindImp |
|---|
| 513 | _block.bindImp |
|---|
| 514 | |
|---|
| 515 | def inferredType as IType? is override |
|---|
| 516 | return _start.type.greatestCommonDenominatorWith(_stop.type to !) |
|---|
| 517 | |
|---|
| 518 | |
|---|
| 519 | class ForNumericStmt inherits ForStmt is partial |
|---|
| 520 | |
|---|
| 521 | cue init(token as IToken, varr as NameExpr, start as Expr, stopp as Expr, dir as int, stepp as Expr?, block as BlockStmt) |
|---|
| 522 | require |
|---|
| 523 | dir in [-1, 0, +1] # 0 is for current syntax. -1 and +1 are for old "for x = 0 .. n ++ 2" syntax |
|---|
| 524 | body |
|---|
| 525 | base.init(token, varr, block) |
|---|
| 526 | _start = start |
|---|
| 527 | _stop = stopp |
|---|
| 528 | _dir = dir |
|---|
| 529 | _step = stepp |
|---|
| 530 | |
|---|
| 531 | def addSubFields |
|---|
| 532 | base.addSubFields |
|---|
| 533 | .addField('start', _start) |
|---|
| 534 | .addField('stop', _stop) |
|---|
| 535 | .addField('dir', _dir) |
|---|
| 536 | .addField('step', _step) |
|---|
| 537 | .addField('block', _block) |
|---|
| 538 | |
|---|
| 539 | get start from var as Expr |
|---|
| 540 | |
|---|
| 541 | get stop from var as Expr |
|---|
| 542 | |
|---|
| 543 | get step from var as Expr? |
|---|
| 544 | |
|---|
| 545 | get dir from var as int |
|---|
| 546 | |
|---|
| 547 | def _innerClone |
|---|
| 548 | base._innerClone |
|---|
| 549 | _start = _start.clone |
|---|
| 550 | _stop = _stop.clone |
|---|
| 551 | if _step, _step = _step.clone |
|---|
| 552 | |
|---|
| 553 | def _bindImp |
|---|
| 554 | _start.bindImp |
|---|
| 555 | _stop.bindImp |
|---|
| 556 | if _step, _step.bindImp |
|---|
| 557 | _var = .bindVar(_varExpr) |
|---|
| 558 | base._bindImp |
|---|
| 559 | _block.bindImp |
|---|
| 560 | if not _start.hasError and not _stop.hasError |
|---|
| 561 | startType, stopType = _start.type to !, _stop.type to ! |
|---|
| 562 | if _checkCompatibleTypes(startType, stopType) |
|---|
| 563 | if _step and not _step.hasError |
|---|
| 564 | stepType = _step.type to ! |
|---|
| 565 | if not _checkCompatibleTypes(startType, stepType) |
|---|
| 566 | .throwError('For loop start condition type "[startType.name]" is not compatible with step condition type "[stepType.name]".[_divHelper(_start)][_divHelper(_step)]') |
|---|
| 567 | else if not _checkCompatibleTypes(stopType, stepType) |
|---|
| 568 | .throwError('For loop stop condition type "[stopType.name]" is not compatible with step condition type "[stepType.name]".[_divHelper(_stop)][_divHelper(_step)]') |
|---|
| 569 | else |
|---|
| 570 | .throwError('For loop start condition type "[startType.name]" is not compatible with stop condition type "[stopType.name]".[_divHelper(_start)][_divHelper(_stop)]') |
|---|
| 571 | # optimization: |
|---|
| 572 | if _dir == 0 # the value for: for x in start : stop : step |
|---|
| 573 | if _step is nil |
|---|
| 574 | _dir = +1 |
|---|
| 575 | _step = IntegerLit(.token.copy('INTEGER_LIT', '+1'), +1) |
|---|
| 576 | _step.bindImp |
|---|
| 577 | else if _step inherits IntegerLit |
|---|
| 578 | _dir = if(_step.value < 0, -1, +1) |
|---|
| 579 | |
|---|
| 580 | def inferredType as IType? is override |
|---|
| 581 | return _start.type.greatestCommonDenominatorWith(_stop.type to !) |
|---|
| 582 | |
|---|
| 583 | def _checkCompatibleTypes(type1 as IType, type2 as IType) as bool |
|---|
| 584 | if type1 == type2 |
|---|
| 585 | return true |
|---|
| 586 | if type1.isDynamic or type2.isDynamic |
|---|
| 587 | return true |
|---|
| 588 | if type1.isDescendantOf(.compiler.anyIntType) and type2.isDescendantOf(.compiler.anyIntType) |
|---|
| 589 | return true |
|---|
| 590 | return false |
|---|
| 591 | |
|---|
| 592 | def _divHelper(expr as Expr?) as String |
|---|
| 593 | if expr inherits BinaryMathExpr and expr.token.which == 'SLASH' |
|---|
| 594 | return ' Note that the "/" operator produces a decimal or floating point number. If you want integer division, use the "//" operator.' |
|---|
| 595 | return '' |
|---|
| 596 | |
|---|
| 597 | |
|---|
| 598 | class ForEnumerableStmt inherits ForStmt is partial |
|---|
| 599 | |
|---|
| 600 | cue init(token as IToken, varr as NameExpr, what as Expr, block as BlockStmt) |
|---|
| 601 | base.init(token, varr, block) |
|---|
| 602 | _what = what |
|---|
| 603 | |
|---|
| 604 | cue init(token as IToken, varr as NameExpr, args as List<of NameExpr>, what as Expr, block as BlockStmt) |
|---|
| 605 | # multiArg assignment in for stmt |
|---|
| 606 | .init(token, varr, what, block) |
|---|
| 607 | _multiArgs = args |
|---|
| 608 | |
|---|
| 609 | def addSubFields |
|---|
| 610 | base.addSubFields |
|---|
| 611 | .addField('what', _what) |
|---|
| 612 | .addField('varNumber', _varNumber) |
|---|
| 613 | .addField('multiArgs', _multiArgs) |
|---|
| 614 | |
|---|
| 615 | get what from var as Expr |
|---|
| 616 | |
|---|
| 617 | get varNumber from var as int |
|---|
| 618 | |
|---|
| 619 | get multiArgs from var as List<of NameExpr>? |
|---|
| 620 | |
|---|
| 621 | def _innerClone |
|---|
| 622 | base._innerClone |
|---|
| 623 | _what = _what.clone |
|---|
| 624 | if .multiArgs |
|---|
| 625 | _multiArgs = _multiArgs.toList |
|---|
| 626 | for i in .multiArgs.count, .multiArgs[i] = .multiArgs[i].clone |
|---|
| 627 | |
|---|
| 628 | def _bindImp |
|---|
| 629 | _what.bindImp |
|---|
| 630 | if _what.type.isDynamic |
|---|
| 631 | _what.contextType = .compiler.enumerableType |
|---|
| 632 | if _what.type inherits AnyIntType |
|---|
| 633 | # 'for x in 10' is a numeric for loop |
|---|
| 634 | _transformTo(ForNumericStmt(.token, _varExpr, IntegerLit(.token.copy('INTEGER_LIT', '0'), 0), _what, 1, nil, _block).bindAll) |
|---|
| 635 | base._bindImp # just to pass the assertion that base._bindImp was invoked |
|---|
| 636 | else |
|---|
| 637 | _var = .bindVar(_varExpr) |
|---|
| 638 | if _multiArgs, _unpackMultiArgStmts |
|---|
| 639 | base._bindImp |
|---|
| 640 | _varNumber = .compiler.curBox.makeNextPrivateSerialNumber |
|---|
| 641 | _block.bindImp |
|---|
| 642 | |
|---|
| 643 | def _unpackMultiArgStmts |
|---|
| 644 | """ |
|---|
| 645 | Handle expansion of statements for multiArg variables in for statements: |
|---|
| 646 | for k, v in <dict> # becomes |
|---|
| 647 | for varExpr_uniq in <dict> |
|---|
| 648 | k = varExpr_uniq.key |
|---|
| 649 | v = varExpr_uniq.value |
|---|
| 650 | and |
|---|
| 651 | for a, b, c in <IEnumerable (of triplets in this case)> becomes |
|---|
| 652 | for varExpr_uniq in <IEnumerable> |
|---|
| 653 | a = varExpr_uniq[0] |
|---|
| 654 | b = varExpr_uniq[1] |
|---|
| 655 | c = varExpr_uniq[2] |
|---|
| 656 | with rest of block following |
|---|
| 657 | """ |
|---|
| 658 | # TODO: make sure varName starts with _lh_ |
|---|
| 659 | stmts = List<of Stmt>() |
|---|
| 660 | varName = _varExpr.name # "[_varExpr.name]_[.serialNum]" |
|---|
| 661 | token = _token.copy |
|---|
| 662 | count = 0 |
|---|
| 663 | isStreamOfKeyValue = .what.type.isDictionaryLike |
|---|
| 664 | if not isStreamOfKeyValue and _var.type inherits Box |
|---|
| 665 | isStreamOfKeyValue = (_var.type to Box).isIndirectConstructionOf(.compiler.libraryType('System.Collections.Generic.KeyValuePair<of,>') to Box) |
|---|
| 666 | if isStreamOfKeyValue |
|---|
| 667 | # This covers: |
|---|
| 668 | # * dictionaries such as: [I]Dictionary<of TKey, TValue> |
|---|
| 669 | # * streams such as: KeyValuePair<of TKey, TValue>* |
|---|
| 670 | # * any user type that implements IEnumerable<of KeyValuePair<of TKey, TValue>> |
|---|
| 671 | |
|---|
| 672 | # assume enumerator will return KeyValue so can't have other than 2 multiargs |
|---|
| 673 | if _multiArgs.count <> 2 |
|---|
| 674 | .throwError('Cannot have other than two multiarg variables in a for statement as assignment targets from a dictionary or key-value stream.') |
|---|
| 675 | kvRHS = ['key', 'value'] |
|---|
| 676 | for id in _multiArgs |
|---|
| 677 | forVar = IdentifierExpr(token.copy('ID', varName), varName) |
|---|
| 678 | rhsToken = token.copy('ID', kvRHS[count]) |
|---|
| 679 | keyOrVal = MemberExpr(rhsToken) |
|---|
| 680 | kvExpr = BinaryOpExpr.make(token.copy('DOT', '.'), 'DOT', forVar, keyOrVal) # forVar.{key,value} |
|---|
| 681 | assignTok = token.copy('ASSIGN', '=') |
|---|
| 682 | # print id.toCobraSource, '=', kvExpr.toCobraSource |
|---|
| 683 | stmts.add(AssignExpr(assignTok, assignTok.which, id, kvExpr)) # id = forVar.{key,value} |
|---|
| 684 | count += 1 |
|---|
| 685 | else # enumeration, each item of which is matching count sequence |
|---|
| 686 | # TODO: ? Add check varName.count <> multArgs.count throw RTException |
|---|
| 687 | for id in _multiArgs |
|---|
| 688 | intToken = token.copy('INTEGER_LIT', '[count]') |
|---|
| 689 | intToken.value = count |
|---|
| 690 | countIntLit = IntegerLit(intToken) |
|---|
| 691 | exprs = List<of Expr>() |
|---|
| 692 | exprs.add(countIntLit) |
|---|
| 693 | forVar = IdentifierExpr(token.copy('ID', varName), varName) |
|---|
| 694 | idxExpr = IndexExpr(token.copy('LBRACKET', r'['), forVar, exprs) # forVar[count] |
|---|
| 695 | assignTok = token.copy('ASSIGN', '=') |
|---|
| 696 | stmts.add(AssignExpr(assignTok, assignTok.which, id, idxExpr)) |
|---|
| 697 | count += 1 |
|---|
| 698 | _block.stmts.insertRange(0, stmts) |
|---|
| 699 | |
|---|
| 700 | def inferredType as IType? is override |
|---|
| 701 | assert _what.type |
|---|
| 702 | return _what.type.innerType |
|---|
| 703 | |
|---|
| 704 | |
|---|
| 705 | class IfStmt |
|---|
| 706 | is partial |
|---|
| 707 | inherits Stmt |
|---|
| 708 | |
|---|
| 709 | var _cond as Expr |
|---|
| 710 | var _trueStmts as BlockStmt |
|---|
| 711 | var _falseStmts as BlockStmt? |
|---|
| 712 | var _doNotPopIfInheritsStack as bool |
|---|
| 713 | var _ifInheritsVar as IVar? |
|---|
| 714 | |
|---|
| 715 | cue init(token as IToken, cond as Expr, trueStmts as BlockStmt, falseStmts as BlockStmt?) |
|---|
| 716 | base.init(token) |
|---|
| 717 | _cond = cond |
|---|
| 718 | _trueStmts = trueStmts |
|---|
| 719 | _falseStmts = falseStmts |
|---|
| 720 | |
|---|
| 721 | get cond from var |
|---|
| 722 | |
|---|
| 723 | get ifInheritsVar from var |
|---|
| 724 | |
|---|
| 725 | get trueStmts from var |
|---|
| 726 | |
|---|
| 727 | get falseStmts from var |
|---|
| 728 | |
|---|
| 729 | get lastToken as IToken is override |
|---|
| 730 | if _falseStmts |
|---|
| 731 | return _falseStmts.lastToken |
|---|
| 732 | else |
|---|
| 733 | return _trueStmts.lastToken |
|---|
| 734 | |
|---|
| 735 | def addSubFields |
|---|
| 736 | base.addSubFields |
|---|
| 737 | .addField('cond', _cond) |
|---|
| 738 | .addField('trueStmts', _trueStmts) |
|---|
| 739 | .addField('falseStmts', _falseStmts) |
|---|
| 740 | .addField('ifInheritsVar', _ifInheritsVar) |
|---|
| 741 | |
|---|
| 742 | def _innerClone |
|---|
| 743 | assert not _ifInheritsVar # should not have bound imp yet |
|---|
| 744 | base._innerClone |
|---|
| 745 | _cond = _cond.clone |
|---|
| 746 | _trueStmts = _trueStmts.clone |
|---|
| 747 | if _falseStmts, _falseStmts = _falseStmts.clone |
|---|
| 748 | |
|---|
| 749 | def _bindImp |
|---|
| 750 | base._bindImp |
|---|
| 751 | try |
|---|
| 752 | _cond.bindImp |
|---|
| 753 | catch ne as NodeException |
|---|
| 754 | .compiler.recordError(ne) |
|---|
| 755 | success |
|---|
| 756 | _doNotPopIfInheritsStack = false |
|---|
| 757 | .noAssignmentAllowed(_cond) |
|---|
| 758 | if _cond.type is not .compiler.boolType |
|---|
| 759 | _cond = TruthExpr(_cond).bindImp to TruthExpr # CC: axe cast when supporting 'as this' |
|---|
| 760 | cond = _cond |
|---|
| 761 | ifInherits = false |
|---|
| 762 | # TODO: handle the "x inherits Y" being part of a sequence of "and"ed exprs |
|---|
| 763 | # CC: combine some of the if statements below when "if inherits" can handle complex expressions |
|---|
| 764 | if cond inherits InheritsExpr |
|---|
| 765 | left = cond.left |
|---|
| 766 | if left inherits IdentifierExpr |
|---|
| 767 | leftVar = left.namedDefinition |
|---|
| 768 | ifInherits = _ivarIfInherits(cond, leftVar) |
|---|
| 769 | else if left inherits AssignExpr |
|---|
| 770 | if left.left inherits IdentifierExpr |
|---|
| 771 | leftVar = left.left.definition |
|---|
| 772 | ifInherits = _ivarIfInherits(cond, leftVar) |
|---|
| 773 | if not ifInherits |
|---|
| 774 | # check for an if-not-nil |
|---|
| 775 | if cond inherits TruthExpr |
|---|
| 776 | if cond.expr inherits IdentifierExpr |
|---|
| 777 | leftVar = cond.expr.definition |
|---|
| 778 | leftType = leftVar.typeForIdentifier |
|---|
| 779 | if leftType inherits NilableType or (leftType.isReference and leftType is not .compiler.objectType) |
|---|
| 780 | # if x ... |
|---|
| 781 | # Note: The check for .objectType just above is due to the fact that a type of Object can point to boxed values such as ints and bools which Cobra will treat properly at runtime. |
|---|
| 782 | notNil = true |
|---|
| 783 | # TODO: the checks about variables that are never nil (/always non-nil) need to happen at the *expression* level so they kick in for assert, require, etc. |
|---|
| 784 | if not ifInherits and not notNil |
|---|
| 785 | if cond inherits CompareExpr |
|---|
| 786 | if cond.op in ['ISNOT', 'NE'] and cond.left inherits IdentifierExpr and cond.right inherits NilLiteral |
|---|
| 787 | # if x is not nil ... |
|---|
| 788 | # if x <> nil ... |
|---|
| 789 | # TODO: also handle "if nil is not x" |
|---|
| 790 | # TODO? "if not x is nil" "if nil is not x" |
|---|
| 791 | notNil = true |
|---|
| 792 | leftVar = cond.left.definition |
|---|
| 793 | if notNil |
|---|
| 794 | if leftVar inherits IVar |
|---|
| 795 | if leftVar.type inherits WrappedType |
|---|
| 796 | leftVar.ifInheritsStack.push((leftVar.type to WrappedType).theWrappedType) |
|---|
| 797 | ifInherits = true |
|---|
| 798 | else |
|---|
| 799 | throw FallThroughException(leftVar) |
|---|
| 800 | if ifInherits |
|---|
| 801 | if leftVar inherits IVar |
|---|
| 802 | _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) |
|---|
| 803 | else |
|---|
| 804 | throw FallThroughException(leftVar) |
|---|
| 805 | if ifInherits |
|---|
| 806 | if leftVar inherits IVar |
|---|
| 807 | _ifInheritsVar = leftVar |
|---|
| 808 | stackCount = leftVar.ifInheritsStack.count |
|---|
| 809 | else |
|---|
| 810 | assert false, leftVar |
|---|
| 811 | _trueStmts.parent = this |
|---|
| 812 | _trueStmts.bindImp |
|---|
| 813 | if ifInherits |
|---|
| 814 | if not _doNotPopIfInheritsStack |
|---|
| 815 | (leftVar to IVar).ifInheritsStack.pop |
|---|
| 816 | assert (leftVar to IVar).ifInheritsStack.count == stackCount - 1 |
|---|
| 817 | |
|---|
| 818 | if _falseStmts |
|---|
| 819 | _falseStmts.parent = this |
|---|
| 820 | _falseStmts.bindImp |
|---|
| 821 | |
|---|
| 822 | def _ivarIfInherits(cond as InheritsExpr, leftVar as INamedNode?) as bool |
|---|
| 823 | ifInherits = false |
|---|
| 824 | if leftVar inherits IVar # if-inherits smarts only work on variables |
|---|
| 825 | # if x inherits Y ... |
|---|
| 826 | if (right = cond.right) inherits IPotentialTypeExpr |
|---|
| 827 | assert right.potentialType |
|---|
| 828 | leftVar.ifInheritsStack.push(right.potentialType to !) |
|---|
| 829 | else |
|---|
| 830 | throw FallThroughException(right) |
|---|
| 831 | ifInherits = true |
|---|
| 832 | _trueStmts.setIfInherits(leftVar, leftVar.ifInheritsStack.peek) |
|---|
| 833 | return ifInherits |
|---|
| 834 | |
|---|
| 835 | def doNotPopIfInheritsStack |
|---|
| 836 | _doNotPopIfInheritsStack = true |
|---|
| 837 | |
|---|
| 838 | |
|---|
| 839 | class ListenOrIgnoreStmt |
|---|
| 840 | is abstract, partial |
|---|
| 841 | inherits Stmt |
|---|
| 842 | |
|---|
| 843 | var _event as Expr? |
|---|
| 844 | var _target as Expr? |
|---|
| 845 | |
|---|
| 846 | cue init(token as IToken, eventRef as Expr, target as Expr) |
|---|
| 847 | base.init(token) |
|---|
| 848 | _event = eventRef |
|---|
| 849 | _target = target |
|---|
| 850 | |
|---|
| 851 | def addSubFields |
|---|
| 852 | base.addSubFields |
|---|
| 853 | .addField('event', _event) |
|---|
| 854 | .addField('target', _target) |
|---|
| 855 | |
|---|
| 856 | get event from var |
|---|
| 857 | |
|---|
| 858 | get target from var |
|---|
| 859 | |
|---|
| 860 | def _innerClone |
|---|
| 861 | base._innerClone |
|---|
| 862 | if _event, _event = _event.clone |
|---|
| 863 | if _target, _target = _target.clone |
|---|
| 864 | |
|---|
| 865 | def _bindImp |
|---|
| 866 | base._bindImp |
|---|
| 867 | |
|---|
| 868 | _event.bindImp |
|---|
| 869 | if not _event.definition inherits BoxEvent |
|---|
| 870 | if _event.definition |
|---|
| 871 | .throwError('The first parameter must be an event, not a [_event.definition.englishName].') |
|---|
| 872 | else |
|---|
| 873 | .throwError('The first parameter must be an event.') |
|---|
| 874 | # TODO: could suggest events with similar names |
|---|
| 875 | |
|---|
| 876 | .compiler.refExprLevel += 1 |
|---|
| 877 | # ^ to avoid errors such as ".foo requirements arguments" |
|---|
| 878 | # the more accurate and informative error about requiring "ref" is detected further below |
|---|
| 879 | try |
|---|
| 880 | _target.bindImp |
|---|
| 881 | finally |
|---|
| 882 | .compiler.refExprLevel -= 1 |
|---|
| 883 | if not _target inherits RefExpr and not _target inherits AnonymousMethodExpr and not _target.type.nonNil.isDescendantOf(.compiler.delegateType) |
|---|
| 884 | if _target.definition inherits AbstractMethod |
|---|
| 885 | sugg = ' Put "ref" before the method such as "ref [_target.toCobraSource]".' |
|---|
| 886 | else |
|---|
| 887 | sugg = '' |
|---|
| 888 | .throwError('The second parameter must be a reference to a method.[sugg]') |
|---|
| 889 | else |
|---|
| 890 | # TODO: check that _target is a normal method reference |
|---|
| 891 | pass |
|---|
| 892 | |
|---|
| 893 | |
|---|
| 894 | class ListenStmt |
|---|
| 895 | is partial |
|---|
| 896 | inherits ListenOrIgnoreStmt |
|---|
| 897 | |
|---|
| 898 | cue init(token as IToken, eventRef as Expr, target as Expr) |
|---|
| 899 | base.init(token, eventRef, target) |
|---|
| 900 | |
|---|
| 901 | |
|---|
| 902 | class IgnoreStmt |
|---|
| 903 | is partial |
|---|
| 904 | inherits ListenOrIgnoreStmt |
|---|
| 905 | |
|---|
| 906 | cue init(token as IToken, eventRef as Expr, target as Expr) |
|---|
| 907 | base.init(token, eventRef, target) |
|---|
| 908 | |
|---|
| 909 | |
|---|
| 910 | class LockStmt inherits Stmt is partial |
|---|
| 911 | |
|---|
| 912 | cue init(token as IToken, expr as Expr, block as BlockStmt) |
|---|
| 913 | base.init(token) |
|---|
| 914 | _expr, _block = expr, block |
|---|
| 915 | |
|---|
| 916 | get expr from var as Expr |
|---|
| 917 | |
|---|
| 918 | get block from var as BlockStmt |
|---|
| 919 | |
|---|
| 920 | get lastToken as IToken is override |
|---|
| 921 | return _block.lastToken |
|---|
| 922 | |
|---|
| 923 | def addSubFields |
|---|
| 924 | base.addSubFields |
|---|
| 925 | .addField('expr', .expr) |
|---|
| 926 | .addField('block', .block) |
|---|
| 927 | |
|---|
| 928 | def _innerClone |
|---|
| 929 | base._innerClone |
|---|
| 930 | _expr = _expr.clone |
|---|
| 931 | _block = _block.clone |
|---|
| 932 | |
|---|
| 933 | def _bindImp |
|---|
| 934 | base._bindImp |
|---|
| 935 | # TODO: the expression must be a reference type |
|---|
| 936 | _expr.bindImp |
|---|
| 937 | _block.bindImp |
|---|
| 938 | |
|---|
| 939 | |
|---|
| 940 | class PassStmt |
|---|
| 941 | is partial |
|---|
| 942 | inherits Stmt |
|---|
| 943 | |
|---|
| 944 | cue init(token as IToken) |
|---|
| 945 | base.init(token) |
|---|
| 946 | |
|---|
| 947 | def _bindImp |
|---|
| 948 | base._bindImp |
|---|
| 949 | |
|---|
| 950 | |
|---|
| 951 | class AbstractPrintStmt inherits Stmt is abstract, partial |
|---|
| 952 | |
|---|
| 953 | cue init(token as IToken, destination as Expr?) |
|---|
| 954 | base.init(token) |
|---|
| 955 | _destination = destination |
|---|
| 956 | |
|---|
| 957 | def addSubFields |
|---|
| 958 | base.addSubFields |
|---|
| 959 | .addField('destination', .destination) |
|---|
| 960 | |
|---|
| 961 | get destination from var as Expr? |
|---|
| 962 | |
|---|
| 963 | def _innerClone |
|---|
| 964 | base._innerClone |
|---|
| 965 | if _destination, _destination = _destination.clone |
|---|
| 966 | |
|---|
| 967 | def _bindImp |
|---|
| 968 | base._bindImp |
|---|
| 969 | if .destination |
|---|
| 970 | .destination.bindImp |
|---|
| 971 | t = .destination.type |
|---|
| 972 | if not t.isDynamicOrPassThrough and not t.isDescendantOf(.compiler.libraryType('System.IO.TextWriter')) # TODO-BACKEND |
|---|
| 973 | .recordError('Invalid destination of type "[t.name]" for "print". Use a TextWriter or subclass thereof instead.') |
|---|
| 974 | |
|---|
| 975 | |
|---|
| 976 | class PrintStmt inherits AbstractPrintStmt is partial |
|---|
| 977 | |
|---|
| 978 | cue init(token as IToken, destination as Expr?, args as List<of Expr>, stopp as bool) |
|---|
| 979 | base.init(token, destination) |
|---|
| 980 | _args, _stop = args, stopp |
|---|
| 981 | |
|---|
| 982 | get args from var as List<of Expr> |
|---|
| 983 | has Subnodes |
|---|
| 984 | |
|---|
| 985 | get stop from var as bool |
|---|
| 986 | |
|---|
| 987 | def _innerClone |
|---|
| 988 | base._innerClone |
|---|
| 989 | _args = _args.toList2 |
|---|
| 990 | for i in _args.count, _args[i] = _args[i].clone |
|---|
| 991 | |
|---|
| 992 | def _bindImp |
|---|
| 993 | base._bindImp |
|---|
| 994 | for i, arg in .args.numbered |
|---|
| 995 | try |
|---|
| 996 | arg.bindImp |
|---|
| 997 | catch ne as NodeException |
|---|
| 998 | ne.prefixMessage('For "print" arg [i+1]: ') |
|---|
| 999 | .compiler.recordError(ne) |
|---|
| 1000 | if arg.type and arg.type inherits VoidType |
|---|
| 1001 | .recordError('For "print" arg [i+1]: Expression cannot be printed because it does not return a value.') |
|---|
| 1002 | |
|---|
| 1003 | def addSubFields |
|---|
| 1004 | base.addSubFields |
|---|
| 1005 | .addField('stop', .stop) |
|---|
| 1006 | .addField('args', .args) |
|---|
| 1007 | |
|---|
| 1008 | |
|---|
| 1009 | class PrintRedirectStmt inherits AbstractPrintStmt is partial |
|---|
| 1010 | |
|---|
| 1011 | var _block as BlockStmt |
|---|
| 1012 | |
|---|
| 1013 | cue init(token as IToken, destination as Expr, block as BlockStmt) |
|---|
| 1014 | base.init(token, destination) |
|---|
| 1015 | _block = block |
|---|
| 1016 | |
|---|
| 1017 | get block from var |
|---|
| 1018 | |
|---|
| 1019 | get lastToken as IToken is override |
|---|
| 1020 | return _block.lastToken |
|---|
| 1021 | |
|---|
| 1022 | def _innerClone |
|---|
| 1023 | base._innerClone |
|---|
| 1024 | _block = _block.clone |
|---|
| 1025 | |
|---|
| 1026 | def _bindImp |
|---|
| 1027 | base._bindImp |
|---|
| 1028 | _destination.bindImp |
|---|
| 1029 | _block.bindImp |
|---|
| 1030 | |
|---|
| 1031 | def addSubFields |
|---|
| 1032 | base.addSubFields |
|---|
| 1033 | .addField('block', _block) |
|---|
| 1034 | |
|---|
| 1035 | |
|---|
| 1036 | class RaiseStmt |
|---|
| 1037 | is partial |
|---|
| 1038 | inherits Stmt |
|---|
| 1039 | """ |
|---|
| 1040 | Raise an event. |
|---|
| 1041 | |
|---|
| 1042 | TODO: test raising an inherited event, either from source or from binary |
|---|
| 1043 | TODO: Test generic events. |
|---|
| 1044 | """ |
|---|
| 1045 | |
|---|
| 1046 | var _name = '' |
|---|
| 1047 | var _exprs as List<of Expr> |
|---|
| 1048 | |
|---|
| 1049 | # computed in _bindImp |
|---|
| 1050 | var _definition as BoxEvent? |
|---|
| 1051 | var _eventType as IType? |
|---|
| 1052 | var _params as IList<of Param>? |
|---|
| 1053 | var _args as List<of Expr>? |
|---|
| 1054 | |
|---|
| 1055 | cue init(token as IToken, exprs as List<of Expr>) |
|---|
| 1056 | require exprs.count > 0 |
|---|
| 1057 | base.init(token) |
|---|
| 1058 | _exprs = exprs |
|---|
| 1059 | |
|---|
| 1060 | def addSubFields |
|---|
| 1061 | base.addSubFields |
|---|
| 1062 | .addField('exprs', _exprs) |
|---|
| 1063 | |
|---|
| 1064 | get name from var |
|---|
| 1065 | |
|---|
| 1066 | get exprs from var |
|---|
| 1067 | |
|---|
| 1068 | def _innerClone |
|---|
| 1069 | base._innerClone |
|---|
| 1070 | _exprs = _exprs.toList2 |
|---|
| 1071 | for i in _exprs.count, _exprs[i] = _exprs[i].clone |
|---|
| 1072 | |
|---|
| 1073 | def _bindImp |
|---|
| 1074 | base._bindImp |
|---|
| 1075 | hasError = false |
|---|
| 1076 | for expr in _exprs |
|---|
| 1077 | try |
|---|
| 1078 | expr.bindImp |
|---|
| 1079 | catch ne as NodeException |
|---|
| 1080 | .compiler.recordError(ne) |
|---|
| 1081 | hasError = true |
|---|
| 1082 | if not hasError |
|---|
| 1083 | expr = _exprs[0] |
|---|
| 1084 | if expr inherits DotExpr |
|---|
| 1085 | right = expr.dotRight |
|---|
| 1086 | defi = right.definition |
|---|
| 1087 | if defi inherits BoxEvent |
|---|
| 1088 | if right inherits CallExpr |
|---|
| 1089 | .throwError('Cannot call events. The general form is "raise .<event>, \[<sender>], <arg1>, <arg2>, ...".') |
|---|
| 1090 | _definition = defi |
|---|
| 1091 | _name = defi.name |
|---|
| 1092 | _eventType = defi.handlerType |
|---|
| 1093 | else |
|---|
| 1094 | .throwError('Expecting an event to raise.') |
|---|
| 1095 | else if expr inherits IdentifierExpr # ex: raise _eventName |
|---|
| 1096 | _name = expr.name |
|---|
| 1097 | _eventType = expr.potentialType |
|---|
| 1098 | if not _eventType |
|---|
| 1099 | hint = '' |
|---|
| 1100 | if expr.type inherits MethodSig |
|---|
| 1101 | hint = ' Use "[_name](args)" to invoke the method reference (delegate).' |
|---|
| 1102 | .throwError('The target of "raise" is not an event.[hint]') |
|---|
| 1103 | if _eventType.isDescendantOf(.compiler.delegateType) |
|---|
| 1104 | pass |
|---|
| 1105 | else if _eventType.isDescendantOf(.compiler.exceptionType) |
|---|
| 1106 | .throwError('Use "throw" instead of "raise" for the exception "[_eventType.name]".') |
|---|
| 1107 | else |
|---|
| 1108 | .throwError('Invalid expression for raising events. Use "raise .someEvent, args" or "throw SomeException(args)".') |
|---|
| 1109 | else |
|---|
| 1110 | .throwError('Invalid expression for raising events. Use "raise .someEvent, args" or "throw SomeException(args)".') |
|---|
| 1111 | if _eventType inherits MethodSig |
|---|
| 1112 | _params = _eventType.params |
|---|
| 1113 | else |
|---|
| 1114 | member = _eventType.memberForName('invoke') |
|---|
| 1115 | if member inherits Method |
|---|
| 1116 | _params = member.params |
|---|
| 1117 | else |
|---|
| 1118 | .throwError('Cannot find a single "invoke" method for "_eventType.name".') |
|---|
| 1119 | # TODO: check type compatibility of exprs with params |
|---|
| 1120 | args = _exprs[1:] |
|---|
| 1121 | params = _params |
|---|
| 1122 | unThis = 'Unnecessary "this" which is already implied by raising an event. You can remove it.' |
|---|
| 1123 | if args.count == params.count |
|---|
| 1124 | if args.count > 0 and args[0] inherits ThisLit |
|---|
| 1125 | .compiler.warning(this, unThis) |
|---|
| 1126 | else if args.count == params.count - 1 |
|---|
| 1127 | # off by one. |
|---|
| 1128 | if args[0] inherits ThisLit |
|---|
| 1129 | .compiler.warning(this, unThis) |
|---|
| 1130 | # since we have 'this', try appending the args object |
|---|
| 1131 | args.add(_postCallForType(params[params.count-1].type)) |
|---|
| 1132 | else |
|---|
| 1133 | # since we don't have 'this', try prepending 'this' |
|---|
| 1134 | args.insert(0, ThisLit(.token).bindImp) |
|---|
| 1135 | else if args.count == 0 and params.count == 2 |
|---|
| 1136 | # missing both 'this' and event args |
|---|
| 1137 | args.add(ThisLit(.token).bindImp) |
|---|
| 1138 | args.add(_postCallForType(params[params.count-1].type)) |
|---|
| 1139 | else |
|---|
| 1140 | .throwError('Event expects [params.count] arguments, but the "raise" statement is providing [args.count].') |
|---|
| 1141 | _args = args |
|---|
| 1142 | |
|---|
| 1143 | def _postCallForType(type as IType) as PostCallExpr |
|---|
| 1144 | init = type.memberForName('cue.init') |
|---|
| 1145 | good = false |
|---|
| 1146 | if init inherits Initializer |
|---|
| 1147 | if init.params.count == 0 |
|---|
| 1148 | good = true |
|---|
| 1149 | else if init inherits MemberOverload |
|---|
| 1150 | for member in init.members |
|---|
| 1151 | if member.params.count == 0 |
|---|
| 1152 | good = true |
|---|
| 1153 | break |
|---|
| 1154 | if good |
|---|
| 1155 | token = .token.copy('ID', type.name) |
|---|
| 1156 | return PostCallExpr(token, TypeExpr(token, type), List<of Expr>()).bindImp |
|---|
| 1157 | else |
|---|
| 1158 | .throwError('Missing argument for "raise" of type "[type.name]".') |
|---|
| 1159 | throw FallThroughException() # suppress an error |
|---|
| 1160 | |
|---|
| 1161 | |
|---|
| 1162 | class ReturnStmt |
|---|
| 1163 | is partial |
|---|
| 1164 | inherits Stmt |
|---|
| 1165 | |
|---|
| 1166 | var _expr as Expr? |
|---|
| 1167 | |
|---|
| 1168 | cue init(token as IToken, expr as Expr?) |
|---|
| 1169 | base.init(token) |
|---|
| 1170 | _expr = expr |
|---|
| 1171 | |
|---|
| 1172 | def addSubFields |
|---|
| 1173 | base.addSubFields |
|---|
| 1174 | .addField('expr', _expr) |
|---|
| 1175 | |
|---|
| 1176 | get expr from var |
|---|
| 1177 | |
|---|
| 1178 | def _innerClone |
|---|
| 1179 | base._innerClone |
|---|
| 1180 | if _expr, _expr = _expr.clone |
|---|
| 1181 | |
|---|
| 1182 | def _bindImp |
|---|
| 1183 | base._bindImp |
|---|
| 1184 | expr = _expr |
|---|
| 1185 | curCodeMember = .compiler.codeMemberStack.peek |
|---|
| 1186 | if _expr |
|---|
| 1187 | expr = _expr.bindImp |
|---|
| 1188 | if not expr.canBeAssignedTo(curCodeMember.resultType) |
|---|
| 1189 | suffix = if(curCodeMember.resultType is .compiler.voidType, 'not declared to return anything.', 'declared to return a [curCodeMember.resultType.name].') |
|---|
| 1190 | .throwError('Cannot return [expr.type.name] because "[curCodeMember.name]" is [suffix]') |
|---|
| 1191 | expr.contextType = curCodeMember.resultType |
|---|
| 1192 | else |
|---|
| 1193 | # TODO: the check for .passThroughType below is required only because lambdas are using it for their return value instead of setting it to the correct type. so improving lambdas (AnonExpr) will allow the removal of that check |
|---|
| 1194 | if curCodeMember.resultType is not .compiler.voidType and curCodeMember.resultType is not .compiler.passThroughType |
|---|
| 1195 | .throwError('Return statement must return a [curCodeMember.resultType.name], or [curCodeMember.name] must have its return type removed.') |
|---|
| 1196 | curCodeMember.hasReturnStmt = true |
|---|
| 1197 | _backEndResultVarName = curCodeMember.backEndResultVarName |
|---|
| 1198 | |
|---|
| 1199 | |
|---|
| 1200 | class TraceStmt |
|---|
| 1201 | is abstract, partial |
|---|
| 1202 | inherits Stmt |
|---|
| 1203 | |
|---|
| 1204 | var _codePart as AbstractMethod |
|---|
| 1205 | |
|---|
| 1206 | cue init(token as IToken, codePart as AbstractMethod) |
|---|
| 1207 | base.init(token) |
|---|
| 1208 | _codePart = codePart |
|---|
| 1209 | |
|---|
| 1210 | get codePart from var |
|---|
| 1211 | |
|---|
| 1212 | def includeTraces as bool |
|---|
| 1213 | return .compiler.options.boolValue('include-traces') |
|---|
| 1214 | |
|---|
| 1215 | |
|---|
| 1216 | class TraceLocationStmt |
|---|
| 1217 | is partial |
|---|
| 1218 | inherits TraceStmt |
|---|
| 1219 | |
|---|
| 1220 | cue init(token as IToken, codePart as AbstractMethod) |
|---|
| 1221 | base.init(token, codePart) |
|---|
| 1222 | |
|---|
| 1223 | |
|---|
| 1224 | class TraceAllStmt |
|---|
| 1225 | is partial |
|---|
| 1226 | inherits TraceStmt |
|---|
| 1227 | |
|---|
| 1228 | cue init(token as IToken, codePart as AbstractMethod) |
|---|
| 1229 | base.init(token, codePart) |
|---|
| 1230 | |
|---|
| 1231 | |
|---|
| 1232 | class TraceExprsStmt |
|---|
| 1233 | is partial |
|---|
| 1234 | inherits TraceStmt |
|---|
| 1235 | |
|---|
| 1236 | var _exprs as List<of Expr> |
|---|
| 1237 | |
|---|
| 1238 | cue init(token as IToken, codePart as AbstractMethod, exprs as List<of Expr>) |
|---|
| 1239 | base.init(token, codePart) |
|---|
| 1240 | _exprs = exprs |
|---|
| 1241 | |
|---|
| 1242 | get exprs from var |
|---|
| 1243 | has Subnodes |
|---|
| 1244 | |
|---|
| 1245 | def _innerClone |
|---|
| 1246 | base._innerClone |
|---|
| 1247 | _exprs = _exprs.toList2 |
|---|
| 1248 | for i in _exprs.count, _exprs[i] = _exprs[i].clone |
|---|
| 1249 | |
|---|
| 1250 | def _bindImp |
|---|
| 1251 | base._bindImp |
|---|
| 1252 | for expr in _exprs, expr.bindImp |
|---|
| 1253 | |
|---|
| 1254 | |
|---|
| 1255 | class TryStmt |
|---|
| 1256 | is partial |
|---|
| 1257 | inherits Stmt |
|---|
| 1258 | |
|---|
| 1259 | var _tryBlock as BlockStmt |
|---|
| 1260 | var _catchBlocks as List<of CatchBlock> |
|---|
| 1261 | var _successBlock as BlockStmt? |
|---|
| 1262 | var _finallyBlock as BlockStmt? |
|---|
| 1263 | var _varNumber as int |
|---|
| 1264 | |
|---|
| 1265 | cue init(token as IToken, tryBlock as BlockStmt, catchBlocks as List<of CatchBlock>, successBlock as BlockStmt?, finallyBlock as BlockStmt?) |
|---|
| 1266 | base.init(token) |
|---|
| 1267 | _tryBlock = tryBlock |
|---|
| 1268 | _catchBlocks = catchBlocks |
|---|
| 1269 | _successBlock = successBlock |
|---|
| 1270 | _finallyBlock = finallyBlock |
|---|
| 1271 | |
|---|
| 1272 | get lastToken as IToken is override |
|---|
| 1273 | if _finallyBlock, return _finallyBlock.lastToken |
|---|
| 1274 | if _successBlock, return _successBlock.lastToken |
|---|
| 1275 | if _catchBlocks.count, return _catchBlocks[_catchBlocks.count-1].lastToken |
|---|
| 1276 | return _tryBlock.lastToken |
|---|
| 1277 | |
|---|
| 1278 | def addSubFields |
|---|
| 1279 | base.addSubFields |
|---|
| 1280 | .addField('tryBlock', _tryBlock) |
|---|
| 1281 | .addField('catchBlocks', _catchBlocks) |
|---|
| 1282 | .addField('succesBlock', _successBlock) |
|---|
| 1283 | .addField('finallyBlock', _finallyBlock) |
|---|
| 1284 | |
|---|
| 1285 | get tryBlock from var |
|---|
| 1286 | |
|---|
| 1287 | get catchBlocks from var |
|---|
| 1288 | has Subnodes |
|---|
| 1289 | |
|---|
| 1290 | get successBlock from var |
|---|
| 1291 | |
|---|
| 1292 | get finallyBlock from var |
|---|
| 1293 | |
|---|
| 1294 | def _innerClone |
|---|
| 1295 | base._innerClone |
|---|
| 1296 | _tryBlock = _tryBlock.clone |
|---|
| 1297 | _catchBlocks = _catchBlocks.toList |
|---|
| 1298 | for i in _catchBlocks.count, _catchBlocks[i] = _catchBlocks[i].clone |
|---|
| 1299 | if _successBlock, _successBlock = _successBlock.clone |
|---|
| 1300 | if _finallyBlock, _finallyBlock = _finallyBlock.clone |
|---|
| 1301 | |
|---|
| 1302 | def _bindImp |
|---|
| 1303 | base._bindImp |
|---|
| 1304 | _varNumber = .compiler.curBox.makeNextPrivateSerialNumber |
|---|
| 1305 | _tryBlock.bindImp |
|---|
| 1306 | for cb in _catchBlocks |
|---|
| 1307 | cb.bindImp |
|---|
| 1308 | if _successBlock |
|---|
| 1309 | _successBlock.bindImp |
|---|
| 1310 | if _finallyBlock |
|---|
| 1311 | _finallyBlock.bindImp |
|---|
| 1312 | # error check |
|---|
| 1313 | if _catchBlocks.count |
|---|
| 1314 | for stmt in _tryBlock.stmts |
|---|
| 1315 | if stmt inherits YieldStmt |
|---|
| 1316 | stmt.recordError('Cannot yield a value in the body of a try block with a catch clause.') |
|---|
| 1317 | # error check |
|---|
| 1318 | for cb in _catchBlocks |
|---|
| 1319 | for stmt in cb.block.stmts |
|---|
| 1320 | if stmt inherits YieldStmt |
|---|
| 1321 | stmt.recordError('Cannot yield a value in the body of a catch clause.') |
|---|
| 1322 | # error check |
|---|
| 1323 | if _finallyBlock |
|---|
| 1324 | for stmt in _finallyBlock.stmts |
|---|
| 1325 | if stmt inherits YieldStmt |
|---|
| 1326 | stmt.recordError('Cannot yield in the body of a finally clause.') |
|---|
| 1327 | |
|---|
| 1328 | |
|---|
| 1329 | class CatchBlock |
|---|
| 1330 | is partial |
|---|
| 1331 | inherits SyntaxNode |
|---|
| 1332 | |
|---|
| 1333 | var _var as AbstractLocalVar? |
|---|
| 1334 | var _typeNode as ITypeProxy? |
|---|
| 1335 | var _type as IType? |
|---|
| 1336 | var _block as BlockStmt |
|---|
| 1337 | var _varName as String? |
|---|
| 1338 | |
|---|
| 1339 | cue init(token as IToken, block as BlockStmt) |
|---|
| 1340 | """ |
|---|
| 1341 | This is for `success`, `finally` and typeless `catch` blocks. |
|---|
| 1342 | """ |
|---|
| 1343 | base.init(token) |
|---|
| 1344 | _block = block |
|---|
| 1345 | |
|---|
| 1346 | cue init(token as IToken, varr as AbstractLocalVar, block as BlockStmt) |
|---|
| 1347 | """ |
|---|
| 1348 | This is for `catch` blocks that specify a type and variable. |
|---|
| 1349 | """ |
|---|
| 1350 | base.init(token) |
|---|
| 1351 | _var = varr |
|---|
| 1352 | _varName = varr.name |
|---|
| 1353 | _block = block |
|---|
| 1354 | |
|---|
| 1355 | cue init(token as IToken, typeNode as ITypeProxy, block as BlockStmt) |
|---|
| 1356 | """ |
|---|
| 1357 | This is for `catch` blocks that specify a type, but no variable. |
|---|
| 1358 | """ |
|---|
| 1359 | base.init(token) |
|---|
| 1360 | _typeNode = typeNode |
|---|
| 1361 | _block = block |
|---|
| 1362 | |
|---|
| 1363 | get block from var |
|---|
| 1364 | |
|---|
| 1365 | get type from var |
|---|
| 1366 | |
|---|
| 1367 | get varName from var |
|---|
| 1368 | |
|---|
| 1369 | get lastToken as IToken |
|---|
| 1370 | return _block.lastToken |
|---|
| 1371 | |
|---|
| 1372 | def _innerClone |
|---|
| 1373 | base._innerClone |
|---|
| 1374 | assert _var is nil |
|---|
| 1375 | _block = _block.clone |
|---|
| 1376 | |
|---|
| 1377 | def _bindImp |
|---|
| 1378 | base._bindImp |
|---|
| 1379 | if _var |
|---|
| 1380 | _var.bindImp |
|---|
| 1381 | # TODO: should this be using .bindVar like others? |
|---|
| 1382 | # TODO: wtf is isTracked again? |
|---|
| 1383 | if not _var.isTracked |
|---|
| 1384 | varr = _var to LocalVar # CC: axe typecast |
|---|
| 1385 | codePart = .compiler.codeMemberStack.peek |
|---|
| 1386 | existingVar = codePart.findLocal(varr.name) |
|---|
| 1387 | if existingVar |
|---|
| 1388 | if existingVar.type is not varr.type |
|---|
| 1389 | .throwError('Cannot redeclare "[varr.name]" as "[varr.type.name]" because it was declared as "[existingVar.type.name]" earlier.') |
|---|
| 1390 | else |
|---|
| 1391 | codePart.addLocal(varr) |
|---|
| 1392 | if _type is nil |
|---|
| 1393 | _type = _var.type |
|---|
| 1394 | assert _type |
|---|
| 1395 | else if _typeNode |
|---|
| 1396 | _typeNode.bindImp |
|---|
| 1397 | _type = _typeNode.realType # CC: combine with above: _type = _typeNode.bindImp.realType |
|---|
| 1398 | _sharpHelperName = '_lh_catch_[.compiler.curBox.makeNextPrivateSerialNumber]' |
|---|
| 1399 | _block.bindImp |
|---|
| 1400 | |
|---|
| 1401 | |
|---|
| 1402 | class ThrowStmt |
|---|
| 1403 | is partial |
|---|
| 1404 | inherits Stmt |
|---|
| 1405 | |
|---|
| 1406 | var _expr as Expr? |
|---|
| 1407 | |
|---|
| 1408 | cue init(token as IToken, expr as Expr?) |
|---|
| 1409 | base.init(token) |
|---|
| 1410 | _expr = expr |
|---|
| 1411 | |
|---|
| 1412 | def addSubFields |
|---|
| 1413 | base.addSubFields |
|---|
| 1414 | .addField('expr', '_expr') |
|---|
| 1415 | |
|---|
| 1416 | get expr from var |
|---|
| 1417 | |
|---|
| 1418 | def _innerClone |
|---|
| 1419 | base._innerClone |
|---|
| 1420 | if _expr, _expr = _expr.clone |
|---|
| 1421 | |
|---|
| 1422 | def _bindImp |
|---|
| 1423 | base._bindImp |
|---|
| 1424 | if _expr |
|---|
| 1425 | _expr.bindImp |
|---|
| 1426 | if not _expr.hasError |
|---|
| 1427 | exceptionType = .compiler.exceptionType |
|---|
| 1428 | typeType = .compiler.typeType |
|---|
| 1429 | if _expr.canBeAssignedTo(exceptionType) |
|---|
| 1430 | pass |
|---|
| 1431 | else if _expr.canBeAssignedTo(typeType) |
|---|
| 1432 | augment = '' |
|---|
| 1433 | /# |
|---|
| 1434 | CC: |
|---|
| 1435 | if _expr responds to (get definition) |
|---|
| 1436 | defin = _expr.definition |
|---|
| 1437 | if defin responds to (def isDescendantOf(t as IType)) |
|---|
| 1438 | augment = 'Try instantiating it with parentheses such as "[_expr.toCobraSource]()".' |
|---|
| 1439 | #/ |
|---|
| 1440 | defin = _expr.definition |
|---|
| 1441 | if defin inherits IType and defin.isDescendantOf(exceptionType) |
|---|
| 1442 | augment = 'Try instantiating it with parentheses such as "[_expr.toCobraSource]()".' |
|---|
| 1443 | if augment == '' |
|---|
| 1444 | augment = 'Also, this type does not inherit from Exception.' |
|---|
| 1445 | .throwError('Cannot throw a type. [augment]') |
|---|
| 1446 | else |
|---|
| 1447 | if _expr.definition inherits BoxEvent |
|---|
| 1448 | reason = 'it is an event, not an exception. Either throw an exception or use "raise" on the event instead of "throw"' |
|---|
| 1449 | else |
|---|
| 1450 | reason = 'it does not inherit from Exception' |
|---|
| 1451 | .throwError('Cannot throw "[_expr.type.name]" because [reason].') |
|---|
| 1452 | .compiler.curCodeMember.hasThrowStmt = true |
|---|
| 1453 | |
|---|
| 1454 | |
|---|
| 1455 | class UsingStmt |
|---|
| 1456 | is partial |
|---|
| 1457 | inherits Stmt |
|---|
| 1458 | |
|---|
| 1459 | var _varExpr as NameExpr |
|---|
| 1460 | var _var as IVar? |
|---|
| 1461 | var _initExpr as Expr |
|---|
| 1462 | var _block as BlockStmt |
|---|
| 1463 | |
|---|
| 1464 | cue init(token as IToken, varExpr as NameExpr, initExpr as Expr, block as BlockStmt) |
|---|
| 1465 | base.init(token) |
|---|
| 1466 | _varExpr = varExpr |
|---|
| 1467 | _initExpr = initExpr |
|---|
| 1468 | _block = block |
|---|
| 1469 | |
|---|
| 1470 | get lastToken as IToken is override |
|---|
| 1471 | return _block.lastToken |
|---|
| 1472 | |
|---|
| 1473 | get initExpr from var |
|---|
| 1474 | |
|---|
| 1475 | get block from var |
|---|
| 1476 | |
|---|
| 1477 | def _innerClone |
|---|
| 1478 | assert _var is nil |
|---|
| 1479 | base._innerClone |
|---|
| 1480 | _varExpr = _varExpr.clone |
|---|
| 1481 | _initExpr = _initExpr.clone |
|---|
| 1482 | _block = _block.clone |
|---|
| 1483 | |
|---|
| 1484 | def _bindImp |
|---|
| 1485 | base._bindImp |
|---|
| 1486 | _initExpr.bindImp |
|---|
| 1487 | _var = .bindVar(_varExpr) |
|---|
| 1488 | # TODO: add an error check that the var type and expr type are compatible |
|---|
| 1489 | _block.bindImp |
|---|
| 1490 | |
|---|
| 1491 | def inferredType as IType? is override |
|---|
| 1492 | assert _initExpr.type |
|---|
| 1493 | return _initExpr.type |
|---|
| 1494 | |
|---|
| 1495 | |
|---|
| 1496 | class AbstractWhileStmt inherits Stmt is partial |
|---|
| 1497 | |
|---|
| 1498 | cue init(token as IToken, expr as Expr, block as BlockStmt) |
|---|
| 1499 | base.init(token) |
|---|
| 1500 | _expr, _block = expr, block |
|---|
| 1501 | |
|---|
| 1502 | get lastToken as IToken is override |
|---|
| 1503 | return _block.lastToken |
|---|
| 1504 | |
|---|
| 1505 | get expr from var as Expr |
|---|
| 1506 | |
|---|
| 1507 | get block from var as BlockStmt |
|---|
| 1508 | |
|---|
| 1509 | def addSubFields |
|---|
| 1510 | base.addSubFields |
|---|
| 1511 | .addField('expr', _expr) |
|---|
| 1512 | .addField('block', _block) |
|---|
| 1513 | |
|---|
| 1514 | def _innerClone |
|---|
| 1515 | base._innerClone |
|---|
| 1516 | _expr = _expr.clone |
|---|
| 1517 | _block = _block.clone |
|---|
| 1518 | |
|---|
| 1519 | def _bindExpr |
|---|
| 1520 | try |
|---|
| 1521 | _expr.bindImp |
|---|
| 1522 | catch ne as NodeException |
|---|
| 1523 | .compiler.recordError(ne) |
|---|
| 1524 | success |
|---|
| 1525 | .noAssignmentAllowed(_expr) |
|---|
| 1526 | if _expr.type is not .compiler.boolType |
|---|
| 1527 | _expr = TruthExpr(_expr).bindImp to TruthExpr # CC: axe when I have "as this" |
|---|
| 1528 | |
|---|
| 1529 | |
|---|
| 1530 | class WhileStmt inherits AbstractWhileStmt is partial |
|---|
| 1531 | |
|---|
| 1532 | cue init(token as IToken, expr as Expr, block as BlockStmt) |
|---|
| 1533 | base.init(token, expr, block) |
|---|
| 1534 | |
|---|
| 1535 | def _bindImp |
|---|
| 1536 | base._bindImp |
|---|
| 1537 | _bindExpr |
|---|
| 1538 | _block.bindImp |
|---|
| 1539 | |
|---|
| 1540 | |
|---|
| 1541 | class PostWhileStmt inherits AbstractWhileStmt is partial |
|---|
| 1542 | |
|---|
| 1543 | cue init(token as IToken, expr as Expr, block as BlockStmt) |
|---|
| 1544 | base.init(token, expr, block) |
|---|
| 1545 | |
|---|
| 1546 | def _bindImp |
|---|
| 1547 | _block.bindImp |
|---|
| 1548 | _bindExpr |
|---|
| 1549 | base._bindImp |
|---|
| 1550 | |
|---|
| 1551 | |
|---|
| 1552 | class YieldStmt |
|---|
| 1553 | is abstract, partial |
|---|
| 1554 | inherits Stmt |
|---|
| 1555 | |
|---|
| 1556 | # CC: axe when initializers are inherited |
|---|
| 1557 | cue init(token as IToken) |
|---|
| 1558 | base.init(token) |
|---|
| 1559 | |
|---|
| 1560 | def _bindImp |
|---|
| 1561 | base._bindImp |
|---|
| 1562 | if .compiler.curCodeMember inherits Initializer |
|---|
| 1563 | .throwError('Cannot use yield statements in initializers.') |
|---|
| 1564 | # CC: should there be a check for property and indexer setters? |
|---|
| 1565 | .compiler.curCodeMember.hasYieldStmt = true |
|---|
| 1566 | |
|---|
| 1567 | |
|---|
| 1568 | class YieldBreakStmt |
|---|
| 1569 | is partial |
|---|
| 1570 | inherits YieldStmt |
|---|
| 1571 | |
|---|
| 1572 | # CC: axe when initializers are inherited |
|---|
| 1573 | cue init(token as IToken) |
|---|
| 1574 | base.init(token) |
|---|
| 1575 | |
|---|
| 1576 | def _bindImp |
|---|
| 1577 | base._bindImp |
|---|
| 1578 | |
|---|
| 1579 | |
|---|
| 1580 | class YieldReturnStmt |
|---|
| 1581 | is partial |
|---|
| 1582 | inherits YieldStmt |
|---|
| 1583 | |
|---|
| 1584 | var _expr as Expr? |
|---|
| 1585 | |
|---|
| 1586 | cue init(token as IToken, expr as Expr?) |
|---|
| 1587 | base.init(token) |
|---|
| 1588 | _expr = expr |
|---|
| 1589 | |
|---|
| 1590 | get expr from var |
|---|
| 1591 | |
|---|
| 1592 | def addSubFields |
|---|
| 1593 | base.addSubFields |
|---|
| 1594 | .addField('expr', _expr) |
|---|
| 1595 | |
|---|
| 1596 | def _innerClone |
|---|
| 1597 | base._innerClone |
|---|
| 1598 | if _expr, _expr = _expr.clone |
|---|
| 1599 | |
|---|
| 1600 | def _bindImp |
|---|
| 1601 | base._bindImp |
|---|
| 1602 | expr = _expr |
|---|
| 1603 | curCodeMember = .compiler.codeMemberStack.peek |
|---|
| 1604 | if expr |
|---|
| 1605 | _expr.bindImp |
|---|
| 1606 | resultType = curCodeMember.resultType |
|---|
| 1607 | if not resultType inherits StreamType and resultType <> .compiler.enumerableType and resultType <> .compiler.enumeratorType |
|---|
| 1608 | bad = true |
|---|
| 1609 | if resultType inherits Box |
|---|
| 1610 | ienumerableOf = .compiler.enumerableOfType |
|---|
| 1611 | ienumeratorOf = .compiler.enumeratorOfType |
|---|
| 1612 | bad = resultType is not ienumerableOf and resultType is not ienumeratorOf and not resultType.isDirectConstructionOf(ienumerableOf) and not resultType.isDirectConstructionOf(ienumeratorOf) |
|---|
| 1613 | if bad, .throwError('Cannot yield unless the return type is an iterator type. Try "[expr.type.name]*".') |
|---|
| 1614 | if resultType inherits StreamType |
|---|
| 1615 | elementType = resultType.innerType to ! |
|---|
| 1616 | else if resultType inherits Box and (resultType to Box).isGeneric |
|---|
| 1617 | elementType = (resultType to Box).genericParams[0] |
|---|
| 1618 | else |
|---|
| 1619 | elementType = .compiler.objectType |
|---|
| 1620 | if not expr.canBeAssignedTo(elementType) |
|---|
| 1621 | suffix = if(elementType is .compiler.voidType, 'not declared to return anything.', 'declared to return a [elementType.name].') |
|---|
| 1622 | .throwError('Cannot return [expr.type.name] because "[curCodeMember.name]" is [suffix]') |
|---|
| 1623 | _expr.contextType = elementType |
|---|
| 1624 | else |
|---|
| 1625 | # TODO: can there just be "yield return"? |
|---|
| 1626 | pass |
|---|
| 1627 | curCodeMember.hasYieldStmt = true |
|---|
| 1628 | |
|---|
| 1629 | class MultiTargetAssignStatement inherits Stmt is partial |
|---|
| 1630 | """ |
|---|
| 1631 | Handle statements of form |
|---|
| 1632 | a, b, ... = <list-generating-expr> |
|---|
| 1633 | or |
|---|
| 1634 | a, b, ... = <expr>, <expr>, ... |
|---|
| 1635 | |
|---|
| 1636 | Gets turned into various sequences of assignExpr. |
|---|
| 1637 | tmp = <List-Generating-Expr> |
|---|
| 1638 | a = tmp[0] |
|---|
| 1639 | b = tmp[1] |
|---|
| 1640 | ... |
|---|
| 1641 | a = <expr> |
|---|
| 1642 | b = <expr> |
|---|
| 1643 | ... |
|---|
| 1644 | TODO: support a, b[,...] = <Enumerable> |
|---|
| 1645 | TODO: support a, b[,...] = <KeyValuePair|DictionaryEntry> |
|---|
| 1646 | """ |
|---|
| 1647 | |
|---|
| 1648 | var _targets as List<of Expr> # Lvalues |
|---|
| 1649 | # exactly one of the following three should be non-nil |
|---|
| 1650 | var _rightValues as List<of Expr>? # list of source expressions |
|---|
| 1651 | var _source as Expr? # evaluate to IList/Array/String or otherwise indexable by int |
|---|
| 1652 | var _rvals as List<of Expr>? # list of source expressions |
|---|
| 1653 | |
|---|
| 1654 | var _block as BlockStmt? # the rewritten/expanded code |
|---|
| 1655 | |
|---|
| 1656 | cue init(opToken as IToken, args as List<of Expr>, source as Expr?, rvals as List<of Expr>?) |
|---|
| 1657 | base.init(opToken) |
|---|
| 1658 | assert opToken.which == 'ASSIGN' |
|---|
| 1659 | assert (source or rvals) and not (source and rvals) # either but not both |
|---|
| 1660 | _targets = args |
|---|
| 1661 | _source = source |
|---|
| 1662 | _rvals = rvals |
|---|
| 1663 | |
|---|
| 1664 | def addSubFields |
|---|
| 1665 | base.addSubFields |
|---|
| 1666 | .addField('targets', _targets) |
|---|
| 1667 | .addField('source', _source) |
|---|
| 1668 | .addField('rvals', _rvals) |
|---|
| 1669 | .addField('block', _block) |
|---|
| 1670 | |
|---|
| 1671 | get targets from var |
|---|
| 1672 | |
|---|
| 1673 | get source from var |
|---|
| 1674 | |
|---|
| 1675 | get rvals from var |
|---|
| 1676 | |
|---|
| 1677 | get block from var |
|---|
| 1678 | |
|---|
| 1679 | def _innerClone |
|---|
| 1680 | base._innerClone |
|---|
| 1681 | _targets = _targets.toList2 |
|---|
| 1682 | for i in _targets.count, _targets[i] = _targets[i].clone |
|---|
| 1683 | if _rightValues |
|---|
| 1684 | _rightValues = _rightValues.toList |
|---|
| 1685 | for i in _rightValues.count, _rightValues[i] = _rightValues[i].clone |
|---|
| 1686 | if _source, _source = _source.clone |
|---|
| 1687 | if _rvals |
|---|
| 1688 | _rvals = _rvals.toList |
|---|
| 1689 | for i in _rvals.count, _rvals[i] = _rvals[i].clone |
|---|
| 1690 | if _block, _block.clone |
|---|
| 1691 | |
|---|
| 1692 | def _bindImp |
|---|
| 1693 | assert _source or _rvals |
|---|
| 1694 | base._bindImp |
|---|
| 1695 | for target in _targets |
|---|
| 1696 | if not _isLValue(target) |
|---|
| 1697 | .throwError('"[target.toCobraSource]" is not an assignable lvalue (identifier, var, property or indexer).') |
|---|
| 1698 | if _rvals |
|---|
| 1699 | if _targets.count <> _rvals.count |
|---|
| 1700 | .throwError('The number of targets ([_targets.count]) must be the same as the number of expressions ([_rvals.count]) assigned to them') |
|---|
| 1701 | #If rvals are all safe to assign (no rerefs) we can do the simplest thing and turn |
|---|
| 1702 | # them into a sequence of individual assignments. i.e a,b = 1,2 or a,b = c,d cases |
|---|
| 1703 | # If not, we need to turn the rvals list into a literal list, pinning the current values, |
|---|
| 1704 | # and do the expansion on the list as a single item i.e a,b = b,a -> a,b = [b,a] |
|---|
| 1705 | if _isSafeToSimpleAssign() |
|---|
| 1706 | _makeBlockForSimpleAssign |
|---|
| 1707 | else |
|---|
| 1708 | _makeBlockForSimpleAssign # to get assignment exprs for error checking and code gen |
|---|
| 1709 | # to signal to back-end to use RightValues approach: |
|---|
| 1710 | _rightValues = _rvals |
|---|
| 1711 | _rvals = nil |
|---|
| 1712 | if _source |
|---|
| 1713 | #TODO: chk _source supports an int indexer somehow... |
|---|
| 1714 | #if not .can_be_indexed_by_int_indexer(_source) |
|---|
| 1715 | # .throwError(r"rhs expression [_source.toCobraSource] must support being indexed by an integer offset (e.g rhs[0]") |
|---|
| 1716 | _makeBlockForSource |
|---|
| 1717 | |
|---|
| 1718 | if _block, _block.bindImp |
|---|
| 1719 | |
|---|
| 1720 | def _isLValue(id as Expr) as bool |
|---|
| 1721 | # TODO: push to Expr.isLValue. probably require .didBindImp |
|---|
| 1722 | # TODO: for DotExpr and IndexExpr check if there is a setter |
|---|
| 1723 | # TODO: for MemberExpr check that its a setter property or visible var (not a method) |
|---|
| 1724 | return id inherits IdentifierExpr or id inherits DotExpr or id inherits IndexExpr |
|---|
| 1725 | |
|---|
| 1726 | def _isSafeToSimpleAssign as bool |
|---|
| 1727 | # conservatively we just presume its safe if the rvals list is all Literals |
|---|
| 1728 | if all for e in _rvals get e inherits Literal |
|---|
| 1729 | return true |
|---|
| 1730 | # or if identifiers in rvals are not also in target |
|---|
| 1731 | if _disjointIdentifierLists() |
|---|
| 1732 | return true |
|---|
| 1733 | # There are possibly others but above expected to be most common usage |
|---|
| 1734 | # TODO: expand this for more wide ranging simple assignment e.g MemberExprs non matching |
|---|
| 1735 | return false |
|---|
| 1736 | |
|---|
| 1737 | def _disjointIdentifierLists as bool |
|---|
| 1738 | # Specifically all of targets list are IdentifierExprs and rvals are Literals, DotExprs, |
|---|
| 1739 | # MemberExprs or Indentifiers and any Identifiers are not also in target list |
|---|
| 1740 | tnames = Set<of String>() |
|---|
| 1741 | for t in _targets |
|---|
| 1742 | if t inherits IdentifierExpr or t inherits AsExpr |
|---|
| 1743 | tnames.add((t to NameExpr).name) |
|---|
| 1744 | else |
|---|
| 1745 | return false |
|---|
| 1746 | if all for e in _rvals get e inherits Literal or e inherits DotExpr or e inherits MemberExpr or _ |
|---|
| 1747 | e inherits AbstractToExpr or _ |
|---|
| 1748 | (e inherits IdentifierExpr and (e to IdentifierExpr).name not in tnames) |
|---|
| 1749 | return true |
|---|
| 1750 | return false |
|---|
| 1751 | |
|---|
| 1752 | def _makeBlockForSimpleAssign |
|---|
| 1753 | """ |
|---|
| 1754 | Construct expanded assignment block for a multi target assignment from a list of expressions. |
|---|
| 1755 | Assignment block contains |
|---|
| 1756 | id0 = rvals[0] # id0 is contents of targets[0] |
|---|
| 1757 | id1 = rvals[1] # id1 is contents of targets[1] |
|---|
| 1758 | (... repeated for number of items in targets specifying ids (lvalues)) |
|---|
| 1759 | """ |
|---|
| 1760 | assert _targets.count == _rvals.count |
|---|
| 1761 | stmts = List<of Stmt>() |
|---|
| 1762 | assignTok = .lastToken # the one we stored in .init |
|---|
| 1763 | for i, id in _targets.numbered |
|---|
| 1764 | expr = _rvals[i] |
|---|
| 1765 | stmts.add(AssignExpr(assignTok, assignTok.which, id, expr)) |
|---|
| 1766 | _block = BlockStmt(.lastToken.copy('INDENT', ''), stmts) |
|---|
| 1767 | |
|---|
| 1768 | def _makeBlockForSource |
|---|
| 1769 | """ |
|---|
| 1770 | Construct expanded assignment block for a multi target assignment from a single source expression. |
|---|
| 1771 | if fewer targets than exprs in _source - extra exprs (silently) ignored |
|---|
| 1772 | TODO: give error when rvalues are too many |
|---|
| 1773 | if more targets than exprs in _source - will emit runtime exception |
|---|
| 1774 | above differ fm python - both give errors |
|---|
| 1775 | Unchecked assumptions; |
|---|
| 1776 | source is int indexable (x[n]) - c# compiler pick up currently |
|---|
| 1777 | Assignment block contains |
|---|
| 1778 | lh_mt_serialNum = <source> |
|---|
| 1779 | id0 = lh_mt_serialNum[0] # id0 is contents of targets[0] |
|---|
| 1780 | id1 = lh_mt_serialNum[1] # id1 is contents of targets[1] |
|---|
| 1781 | (... repeated for number of items in targets specifying ids (lvalues)) |
|---|
| 1782 | """ |
|---|
| 1783 | stmts = List<of Stmt>() |
|---|
| 1784 | assignTok = .lastToken # the one we stored in init |
|---|
| 1785 | serialNum = .compiler.curBox.makeNextPrivateSerialNumber |
|---|
| 1786 | tmpName = 'lh_mt_[serialNum]' # lh=local handler, mt = multiTarget |
|---|
| 1787 | ttoken = .lastToken.copy('XXX', 'xxx') # invalid template token for copying |
|---|
| 1788 | idToken = ttoken.copy('ID', tmpName) |
|---|
| 1789 | |
|---|
| 1790 | tmpId = IdentifierExpr(idToken, idToken.text) |
|---|
| 1791 | s = AssignExpr(assignTok, assignTok.which, tmpId, _source) |
|---|
| 1792 | stmts.add(s) |
|---|
| 1793 | |
|---|
| 1794 | count = 0 |
|---|
| 1795 | for id in _targets |
|---|
| 1796 | intToken = ttoken.copy('INTEGER_LIT', '[count]') |
|---|
| 1797 | intToken.value = count |
|---|
| 1798 | countIntLit = IntegerLit(intToken) |
|---|
| 1799 | exprs = List<of Expr>() |
|---|
| 1800 | exprs.add(countIntLit) |
|---|
| 1801 | tmpId = IdentifierExpr(ttoken.copy('ID', tmpName), tmpName) |
|---|
| 1802 | idxExpr = IndexExpr(ttoken.copy('LBRACKET', r'['), tmpId, exprs) |
|---|
| 1803 | stmts.add(AssignExpr(assignTok, assignTok.which, id, idxExpr)) |
|---|
| 1804 | count += 1 |
|---|
| 1805 | _block = BlockStmt(ttoken.copy('INDENT', ''), stmts) |
|---|