brighterscript 1.0.0-alpha.17 → 1.0.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/DiagnosticMessages.d.ts +16 -1
  3. package/dist/DiagnosticMessages.js +15 -0
  4. package/dist/DiagnosticMessages.js.map +1 -1
  5. package/dist/Scope.d.ts +58 -8
  6. package/dist/Scope.js +128 -16
  7. package/dist/Scope.js.map +1 -1
  8. package/dist/astUtils/reflection.d.ts +6 -4
  9. package/dist/astUtils/reflection.js +14 -6
  10. package/dist/astUtils/reflection.js.map +1 -1
  11. package/dist/astUtils/reflection.spec.js +10 -0
  12. package/dist/astUtils/reflection.spec.js.map +1 -1
  13. package/dist/astUtils/visitors.d.ts +4 -1
  14. package/dist/astUtils/visitors.js.map +1 -1
  15. package/dist/files/BrsFile.Class.spec.js +2 -2
  16. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  17. package/dist/files/BrsFile.d.ts +1 -0
  18. package/dist/files/BrsFile.js +65 -39
  19. package/dist/files/BrsFile.js.map +1 -1
  20. package/dist/files/BrsFile.spec.js +16 -3
  21. package/dist/files/BrsFile.spec.js.map +1 -1
  22. package/dist/globalCallables.js +79 -79
  23. package/dist/globalCallables.js.map +1 -1
  24. package/dist/interfaces.d.ts +43 -4
  25. package/dist/parser/Expression.d.ts +8 -3
  26. package/dist/parser/Expression.js +15 -5
  27. package/dist/parser/Expression.js.map +1 -1
  28. package/dist/parser/Parser.Class.spec.js +5 -5
  29. package/dist/parser/Parser.Class.spec.js.map +1 -1
  30. package/dist/parser/Parser.d.ts +2 -5
  31. package/dist/parser/Parser.js +105 -105
  32. package/dist/parser/Parser.js.map +1 -1
  33. package/dist/parser/Parser.spec.js +4 -4
  34. package/dist/parser/Parser.spec.js.map +1 -1
  35. package/dist/parser/SGTypes.d.ts +2 -2
  36. package/dist/parser/SGTypes.js +2 -2
  37. package/dist/parser/SGTypes.js.map +1 -1
  38. package/dist/parser/Statement.d.ts +52 -36
  39. package/dist/parser/Statement.js +109 -76
  40. package/dist/parser/Statement.js.map +1 -1
  41. package/dist/parser/tests/statement/InterfaceStatement.spec.js +181 -0
  42. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  43. package/dist/parser/tests/statement/TryCatch.spec.js +7 -5
  44. package/dist/parser/tests/statement/TryCatch.spec.js.map +1 -1
  45. package/dist/types/ArrayType.js +4 -1
  46. package/dist/types/ArrayType.js.map +1 -1
  47. package/dist/types/ArrayType.spec.js +3 -1
  48. package/dist/types/ArrayType.spec.js.map +1 -1
  49. package/dist/types/BooleanType.js +2 -1
  50. package/dist/types/BooleanType.js.map +1 -1
  51. package/dist/types/BooleanType.spec.js +3 -1
  52. package/dist/types/BooleanType.spec.js.map +1 -1
  53. package/dist/types/BscType.d.ts +1 -0
  54. package/dist/types/BscType.js +16 -1
  55. package/dist/types/BscType.js.map +1 -1
  56. package/dist/types/CustomType.js +10 -0
  57. package/dist/types/CustomType.js.map +1 -1
  58. package/dist/types/DoubleType.js +2 -1
  59. package/dist/types/DoubleType.js.map +1 -1
  60. package/dist/types/DoubleType.spec.js +3 -1
  61. package/dist/types/DoubleType.spec.js.map +1 -1
  62. package/dist/types/FloatType.js +2 -1
  63. package/dist/types/FloatType.js.map +1 -1
  64. package/dist/types/FloatType.spec.js +2 -0
  65. package/dist/types/FloatType.spec.js.map +1 -1
  66. package/dist/types/FunctionType.d.ts +3 -22
  67. package/dist/types/FunctionType.js +8 -68
  68. package/dist/types/FunctionType.js.map +1 -1
  69. package/dist/types/IntegerType.js +2 -1
  70. package/dist/types/IntegerType.js.map +1 -1
  71. package/dist/types/IntegerType.spec.js +3 -1
  72. package/dist/types/IntegerType.spec.js.map +1 -1
  73. package/dist/types/InterfaceType.d.ts +13 -10
  74. package/dist/types/InterfaceType.js +33 -29
  75. package/dist/types/InterfaceType.js.map +1 -1
  76. package/dist/types/InterfaceType.spec.js +36 -16
  77. package/dist/types/InterfaceType.spec.js.map +1 -1
  78. package/dist/types/InvalidType.js +2 -1
  79. package/dist/types/InvalidType.js.map +1 -1
  80. package/dist/types/InvalidType.spec.js +2 -0
  81. package/dist/types/InvalidType.spec.js.map +1 -1
  82. package/dist/types/LazyType.js +4 -0
  83. package/dist/types/LazyType.js.map +1 -1
  84. package/dist/types/LongIntegerType.js +2 -1
  85. package/dist/types/LongIntegerType.js.map +1 -1
  86. package/dist/types/LongIntegerType.spec.js +2 -0
  87. package/dist/types/LongIntegerType.spec.js.map +1 -1
  88. package/dist/types/StringType.js +2 -1
  89. package/dist/types/StringType.js.map +1 -1
  90. package/dist/types/StringType.spec.js +2 -0
  91. package/dist/types/StringType.spec.js.map +1 -1
  92. package/dist/types/TypedFunctionType.d.ts +28 -0
  93. package/dist/types/TypedFunctionType.js +88 -0
  94. package/dist/types/TypedFunctionType.js.map +1 -0
  95. package/dist/types/{FunctionType.spec.d.ts → TypedFunctionType.spec.d.ts} +0 -0
  96. package/dist/types/TypedFunctionType.spec.js +37 -0
  97. package/dist/types/TypedFunctionType.spec.js.map +1 -0
  98. package/dist/types/helpers.js +7 -2
  99. package/dist/types/helpers.js.map +1 -1
  100. package/dist/util.d.ts +2 -2
  101. package/dist/util.js +5 -5
  102. package/dist/util.js.map +1 -1
  103. package/dist/validators/ClassValidator.d.ts +14 -1
  104. package/dist/validators/ClassValidator.js +127 -80
  105. package/dist/validators/ClassValidator.js.map +1 -1
  106. package/package.json +1 -1
  107. package/dist/types/FunctionType.spec.js +0 -35
  108. package/dist/types/FunctionType.spec.js.map +0 -1
  109. package/dist/types/UniversalFunctionType.d.ts +0 -9
  110. package/dist/types/UniversalFunctionType.js +0 -25
  111. package/dist/types/UniversalFunctionType.js.map +0 -1
@@ -187,7 +187,7 @@ class Parser {
187
187
  declaration() {
188
188
  try {
189
189
  if (this.checkAny(TokenKind_1.TokenKind.Sub, TokenKind_1.TokenKind.Function)) {
190
- return this.functionDeclaration(false);
190
+ return this.functionStatement({ hasName: true, hasBody: true, hasEnd: true });
191
191
  }
192
192
  if (this.checkLibrary()) {
193
193
  return this.libraryStatement();
@@ -241,44 +241,29 @@ class Parser {
241
241
  return new Statement_1.EnumMemberStatement(tokens, value);
242
242
  }
243
243
  /**
244
- * Create a new InterfaceMethodStatement. This should only be called from within `interfaceDeclaration`
244
+ * Create a new InterfaceFieldStatement. This should only be called from within `interfaceDeclaration`
245
245
  */
246
246
  interfaceFieldStatement() {
247
247
  const name = this.identifier(...TokenKind_1.AllowedProperties);
248
- let asToken = this.consumeToken(TokenKind_1.TokenKind.As);
249
- let typeExpr = this.typeExpression();
250
- if (!typeExpr.isValidType()) {
251
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionParameterTypeIsInvalid(name.text, typeExpr.getText())), { range: typeExpr.range }));
252
- throw this.lastDiagnosticAsError();
253
- }
254
- return new Statement_1.InterfaceFieldStatement(name, asToken, typeExpr);
255
- }
256
- /**
257
- * Create a new InterfaceMethodStatement. This should only be called from within `interfaceDeclaration()`
258
- */
259
- interfaceMethodStatement() {
260
- const functionType = this.advance();
261
- const name = this.identifier(...TokenKind_1.AllowedProperties);
262
- const leftParen = this.consumeToken(TokenKind_1.TokenKind.LeftParen);
263
- const params = [];
264
- const rightParen = this.consumeToken(TokenKind_1.TokenKind.RightParen);
265
- let asToken = null;
266
- let returnTypeExpr;
248
+ let asToken;
249
+ let typeExpr;
250
+ //look for `as SOME_TYPE`
267
251
  if (this.check(TokenKind_1.TokenKind.As)) {
268
- asToken = this.advance();
269
- returnTypeExpr = this.typeExpression();
270
- if (!returnTypeExpr.isValidType(this.options.mode)) {
271
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionParameterTypeIsInvalid(name.text, returnTypeExpr.getText())), { range: returnTypeExpr.range }));
272
- throw this.lastDiagnosticAsError();
252
+ asToken = this.consumeToken(TokenKind_1.TokenKind.As);
253
+ typeExpr = this.typeExpression();
254
+ //no field type specified
255
+ if (!typeExpr.isValidType()) {
256
+ this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionParameterTypeIsInvalid(name.text, typeExpr.getText())), { range: typeExpr.range }));
273
257
  }
274
258
  }
275
- return new Statement_1.InterfaceMethodStatement(functionType, name, leftParen, params, rightParen, asToken, returnTypeExpr);
259
+ return new Statement_1.InterfaceFieldStatement(name, asToken, typeExpr, this.currentNamespaceName);
276
260
  }
277
261
  interfaceDeclaration() {
278
262
  this.warnIfNotBrighterScriptMode('interface declarations');
279
263
  const parentAnnotations = this.enterAnnotationBlock();
280
264
  const interfaceToken = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedKeyword(TokenKind_1.TokenKind.Interface), TokenKind_1.TokenKind.Interface);
281
- const nameToken = this.identifier(...this.allowedLocalIdentifiers);
265
+ //get the interface name
266
+ let nameToken = this.tryConsume(DiagnosticMessages_1.DiagnosticMessages.expectedIdentifierAfterKeyword('interface'), TokenKind_1.TokenKind.Identifier, ...this.allowedLocalIdentifiers);
282
267
  let extendsToken;
283
268
  let parentInterfaceName;
284
269
  if (this.peek().text.toLowerCase() === 'extends') {
@@ -290,6 +275,10 @@ class Parser {
290
275
  let body = [];
291
276
  while (this.checkAny(TokenKind_1.TokenKind.Comment, TokenKind_1.TokenKind.Identifier, TokenKind_1.TokenKind.At, ...TokenKind_1.AllowedProperties)) {
292
277
  try {
278
+ //break out of this loop if we encountered the `EndInterface` token not followed by `as`
279
+ if (this.check(TokenKind_1.TokenKind.EndInterface) && !this.checkNext(TokenKind_1.TokenKind.As)) {
280
+ break;
281
+ }
293
282
  let decl;
294
283
  //collect leading annotations
295
284
  if (this.check(TokenKind_1.TokenKind.At)) {
@@ -301,7 +290,15 @@ class Parser {
301
290
  //methods (function/sub keyword followed by opening paren)
302
291
  }
303
292
  else if (this.checkAny(TokenKind_1.TokenKind.Function, TokenKind_1.TokenKind.Sub) && this.checkAny(TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedProperties)) {
304
- decl = this.interfaceMethodStatement();
293
+ const functionStatement = this.functionStatement({
294
+ hasName: true,
295
+ hasBody: false,
296
+ hasEnd: false,
297
+ onlyCallableAsMember: true
298
+ });
299
+ decl = new Statement_1.InterfaceMethodStatement(functionStatement.name, functionStatement.func);
300
+ //refer to this statement as parent of the expression
301
+ functionStatement.func.functionStatement = decl;
305
302
  //comments
306
303
  }
307
304
  else if (this.check(TokenKind_1.TokenKind.Comment)) {
@@ -311,10 +308,6 @@ class Parser {
311
308
  this.consumePendingAnnotations(decl);
312
309
  body.push(decl);
313
310
  }
314
- else {
315
- //we didn't find a declaration...flag tokens until next line
316
- this.flagUntil(TokenKind_1.TokenKind.Newline, TokenKind_1.TokenKind.Colon, TokenKind_1.TokenKind.Eof);
317
- }
318
311
  }
319
312
  catch (e) {
320
313
  //throw out any failed members and move on to the next line
@@ -322,14 +315,13 @@ class Parser {
322
315
  }
323
316
  //ensure statement separator
324
317
  this.consumeStatementSeparators();
325
- //break out of this loop if we encountered the `EndInterface` token not followed by `as`
326
- if (this.check(TokenKind_1.TokenKind.EndInterface) && !this.checkNext(TokenKind_1.TokenKind.As)) {
327
- break;
328
- }
329
318
  }
330
319
  //consume the final `end interface` token
331
- const endInterfaceToken = this.consumeToken(TokenKind_1.TokenKind.EndInterface);
332
- const statement = new Statement_1.InterfaceStatement(interfaceToken, nameToken, extendsToken, parentInterfaceName, body, endInterfaceToken, this.currentNamespaceName);
320
+ let endingKeyword = this.advance();
321
+ if (endingKeyword.kind !== TokenKind_1.TokenKind.EndInterface) {
322
+ this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.couldNotFindMatchingEndKeyword('interface')), { range: endingKeyword.range }));
323
+ }
324
+ const statement = new Statement_1.InterfaceStatement(interfaceToken, nameToken, extendsToken, parentInterfaceName, body, endingKeyword, this.currentNamespaceName);
333
325
  this._references.interfaceStatements.push(statement);
334
326
  this.exitAnnotationBlock(parentAnnotations);
335
327
  return statement;
@@ -422,14 +414,12 @@ class Parser {
422
414
  }
423
415
  //methods (function/sub keyword OR identifier followed by opening paren)
424
416
  if (this.checkAny(TokenKind_1.TokenKind.Function, TokenKind_1.TokenKind.Sub) || (this.checkAny(TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedProperties) && this.checkNext(TokenKind_1.TokenKind.LeftParen))) {
425
- const funcDeclaration = this.functionDeclaration(false, false, true);
426
- //remove this function from the lists because it's not a callable
427
- const functionStatement = this._references.functionStatements.pop();
417
+ const functionStatement = this.functionStatement({ hasName: true, hasBody: true, hasEnd: true, onlyCallableAsMember: true });
428
418
  //if we have an overrides keyword AND this method is called 'new', that's not allowed
429
- if (overrideKeyword && funcDeclaration.name.text.toLowerCase() === 'new') {
419
+ if (overrideKeyword && functionStatement.name.text.toLowerCase() === 'new') {
430
420
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.cannotUseOverrideKeywordOnConstructorFunction()), { range: overrideKeyword.range }));
431
421
  }
432
- decl = new Statement_1.ClassMethodStatement(accessModifier, funcDeclaration.name, funcDeclaration.func, overrideKeyword);
422
+ decl = new Statement_1.ClassMethodStatement(accessModifier, functionStatement.name, functionStatement.func, overrideKeyword);
433
423
  //refer to this statement as parent of the expression
434
424
  functionStatement.func.functionStatement = decl;
435
425
  //cache the range property so that plugins can't affect it
@@ -493,20 +483,32 @@ class Parser {
493
483
  }
494
484
  return new Statement_1.ClassFieldStatement(accessModifier, name, asToken, fieldTypeExpr, equal, initialValue, this.currentNamespaceName);
495
485
  }
496
- functionDeclaration(isAnonymous, checkIdentifier = true, forClassMethod = false) {
486
+ functionStatement(options) {
487
+ options.hasName = true;
488
+ const funcResult = this.functionDeclaration(options);
489
+ if (funcResult) {
490
+ let result = new Statement_1.FunctionStatement(funcResult.name, funcResult.functionExpression, this.currentNamespaceName);
491
+ funcResult.functionExpression.functionStatement = result;
492
+ if (!options.onlyCallableAsMember) {
493
+ this._references.functionStatements.push(result);
494
+ }
495
+ return result;
496
+ }
497
+ }
498
+ functionDeclaration(options = {}) {
497
499
  var _a, _b, _c, _d;
498
500
  let previousCallExpressions = this.callExpressions;
499
501
  this.callExpressions = [];
500
502
  try {
501
503
  //track depth to help certain statements need to know if they are contained within a function body
502
504
  this.namespaceAndFunctionDepth++;
503
- let functionType;
505
+ let functionKeyword;
504
506
  if (this.checkAny(TokenKind_1.TokenKind.Sub, TokenKind_1.TokenKind.Function)) {
505
- functionType = this.advance();
507
+ functionKeyword = this.advance();
506
508
  }
507
509
  else {
508
510
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.missingCallableKeyword()), { range: this.peek().range }));
509
- functionType = {
511
+ functionKeyword = {
510
512
  isReserved: true,
511
513
  kind: TokenKind_1.TokenKind.Function,
512
514
  text: 'function',
@@ -518,24 +520,24 @@ class Parser {
518
520
  leadingWhitespace: ''
519
521
  };
520
522
  }
521
- let isSub = (functionType === null || functionType === void 0 ? void 0 : functionType.kind) === TokenKind_1.TokenKind.Sub;
522
- let functionTypeText = isSub ? 'sub' : 'function';
523
+ let isSub = (functionKeyword === null || functionKeyword === void 0 ? void 0 : functionKeyword.kind) === TokenKind_1.TokenKind.Sub;
524
+ let functionKeywordText = isSub ? 'sub' : 'function';
523
525
  let name;
524
526
  let leftParen;
525
- if (isAnonymous) {
526
- leftParen = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedLeftParenAfterCallable(functionTypeText), TokenKind_1.TokenKind.LeftParen);
527
+ if (!options.hasName) {
528
+ leftParen = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedLeftParenAfterCallable(functionKeywordText), TokenKind_1.TokenKind.LeftParen);
527
529
  }
528
530
  else {
529
- name = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedNameAfterCallableKeyword(functionTypeText), TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedProperties);
530
- leftParen = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedLeftParenAfterCallableName(functionTypeText), TokenKind_1.TokenKind.LeftParen);
531
+ name = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedNameAfterCallableKeyword(functionKeywordText), TokenKind_1.TokenKind.Identifier, ...TokenKind_1.AllowedProperties);
532
+ leftParen = this.consume(DiagnosticMessages_1.DiagnosticMessages.expectedLeftParenAfterCallableName(functionKeywordText), TokenKind_1.TokenKind.LeftParen);
531
533
  //prevent functions from ending with type designators
532
534
  let lastChar = name.text[name.text.length - 1];
533
535
  if (['$', '%', '!', '#', '&'].includes(lastChar)) {
534
536
  //don't throw this error; let the parser continue
535
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionNameCannotEndWithTypeDesignator(functionTypeText, name.text, lastChar)), { range: name.range }));
537
+ this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionNameCannotEndWithTypeDesignator(functionKeywordText, name.text, lastChar)), { range: name.range }));
536
538
  }
537
- //flag functions with keywords for names (only for standard functions)
538
- if (checkIdentifier && TokenKind_1.DisallowedFunctionIdentifiersText.has(name.text.toLowerCase())) {
539
+ //flag functions with keywords for names (only for standard functions - not for class methods)
540
+ if (!options.onlyCallableAsMember && TokenKind_1.DisallowedFunctionIdentifiersText.has(name.text.toLowerCase())) {
539
541
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.cannotUseReservedWordAsIdentifier(name.text)), { range: name.range }));
540
542
  }
541
543
  }
@@ -564,9 +566,12 @@ class Parser {
564
566
  }
565
567
  return haveFoundOptional || !!param.defaultValue;
566
568
  }, false);
567
- this.consumeStatementSeparators(true);
569
+ if (options.hasEnd && options.hasBody) {
570
+ // do not go to next statement - we don't care about any other statement
571
+ this.consumeStatementSeparators(true);
572
+ }
568
573
  let func = new Expression_1.FunctionExpression(params, undefined, //body
569
- functionType, undefined, //ending keyword
574
+ functionKeyword, undefined, //ending keyword
570
575
  leftParen, rightParen, asToken, typeExpr, //return type
571
576
  this.currentFunctionExpression, this.currentNamespaceName, (_c = (_b = this.currentNamespace) === null || _b === void 0 ? void 0 : _b.symbolTable) !== null && _c !== void 0 ? _c : this.symbolTable);
572
577
  //if there is a parent function, register this function with the parent
@@ -574,7 +579,7 @@ class Parser {
574
579
  this.currentFunctionExpression.childFunctionExpressions.push(func);
575
580
  }
576
581
  // add the function to the relevant symbol tables
577
- if (!isAnonymous && !forClassMethod) {
582
+ if (!options.onlyCallableAsMember && name) {
578
583
  const funcType = func.getFunctionType();
579
584
  funcType.setName(name.text);
580
585
  // add the function as declared to the current namespace's table
@@ -587,42 +592,35 @@ class Parser {
587
592
  this.currentSymbolTable.addSymbol(fullyQualifiedName, name.range, funcType);
588
593
  }
589
594
  this._references.functionExpressions.push(func);
590
- let previousFunctionExpression = this.currentFunctionExpression;
591
- this.currentFunctionExpression = func;
592
- //make sure to restore the currentFunctionExpression even if the body block fails to parse
593
- try {
594
- //support ending the function with `end sub` OR `end function`
595
- func.body = this.block();
596
- }
597
- finally {
598
- this.currentFunctionExpression = previousFunctionExpression;
599
- }
600
- if (!func.body) {
601
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.callableBlockMissingEndKeyword(functionTypeText)), { range: this.peek().range }));
602
- throw this.lastDiagnosticAsError();
595
+ if (options.hasBody) {
596
+ let previousFunctionExpression = this.currentFunctionExpression;
597
+ this.currentFunctionExpression = func;
598
+ //make sure to restore the currentFunctionExpression even if the body block fails to parse
599
+ try {
600
+ //support ending the function with `end sub` OR `end function`
601
+ func.body = this.block();
602
+ }
603
+ finally {
604
+ this.currentFunctionExpression = previousFunctionExpression;
605
+ }
606
+ if (!func.body) {
607
+ this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.callableBlockMissingEndKeyword(functionKeywordText)), { range: this.peek().range }));
608
+ throw this.lastDiagnosticAsError();
609
+ }
603
610
  }
604
- // consume 'end sub' or 'end function'
605
- func.end = this.advance();
606
- let expectedEndKind = isSub ? TokenKind_1.TokenKind.EndSub : TokenKind_1.TokenKind.EndFunction;
607
- //if `function` is ended with `end sub`, or `sub` is ended with `end function`, then
608
- //add an error but don't hard-fail so the AST can continue more gracefully
609
- if (func.end.kind !== expectedEndKind) {
610
- this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchedEndCallableKeyword(functionTypeText, func.end.text)), { range: this.peek().range }));
611
+ if (options.hasEnd) {
612
+ // consume 'end sub' or 'end function'
613
+ func.end = this.advance();
614
+ let expectedEndKind = isSub ? TokenKind_1.TokenKind.EndSub : TokenKind_1.TokenKind.EndFunction;
615
+ //if `function` is ended with `end sub`, or `sub` is ended with `end function`, then
616
+ //add an error but don't hard-fail so the AST can continue more gracefully
617
+ if (func.end.kind !== expectedEndKind) {
618
+ this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchedEndCallableKeyword(functionKeywordText, func.end.text)), { range: this.peek().range }));
619
+ }
611
620
  }
612
621
  func.callExpressions = this.callExpressions;
613
- if (isAnonymous) {
614
- //cache the range property so that plugins can't affect it
615
- func.cacheRange();
616
- return func;
617
- }
618
- else {
619
- let result = new Statement_1.FunctionStatement(name, func, this.currentNamespaceName);
620
- func.functionStatement = result;
621
- this._references.functionStatements.push(result);
622
- //cache the range property so that plugins can't affect it
623
- result.cacheRange();
624
- return result;
625
- }
622
+ func.cacheRange();
623
+ return { name: name, functionExpression: func };
626
624
  }
627
625
  finally {
628
626
  this.namespaceAndFunctionDepth--;
@@ -1118,7 +1116,7 @@ class Parser {
1118
1116
  }
1119
1117
  tryCatchStatement() {
1120
1118
  const tryToken = this.advance();
1121
- const statement = new Statement_1.TryCatchStatement(tryToken);
1119
+ const statement = new Statement_1.TryCatchStatement({ try: tryToken });
1122
1120
  //ensure statement separator
1123
1121
  this.consumeStatementSeparators();
1124
1122
  statement.tryBranch = this.block(TokenKind_1.TokenKind.Catch, TokenKind_1.TokenKind.EndTry);
@@ -1127,27 +1125,26 @@ class Parser {
1127
1125
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.expectedCatchBlockInTryCatch()), { range: this.peek().range }));
1128
1126
  //gracefully handle end-try
1129
1127
  if (peek.kind === TokenKind_1.TokenKind.EndTry) {
1130
- statement.endTryToken = this.advance();
1128
+ statement.tokens.endTry = this.advance();
1131
1129
  }
1132
1130
  return statement;
1133
1131
  }
1134
- else {
1135
- statement.catchToken = this.advance();
1136
- }
1132
+ const catchStmt = new Statement_1.CatchStatement({ catch: this.advance() });
1133
+ statement.catchStatement = catchStmt;
1137
1134
  const exceptionVarToken = this.tryConsume(DiagnosticMessages_1.DiagnosticMessages.missingExceptionVarToFollowCatch(), TokenKind_1.TokenKind.Identifier, ...this.allowedLocalIdentifiers);
1138
1135
  if (exceptionVarToken) {
1139
1136
  // force it into an identifier so the AST makes some sense
1140
1137
  exceptionVarToken.kind = TokenKind_1.TokenKind.Identifier;
1141
- statement.exceptionVariable = exceptionVarToken;
1138
+ catchStmt.exceptionVariable = exceptionVarToken;
1142
1139
  }
1143
1140
  //ensure statement sepatator
1144
1141
  this.consumeStatementSeparators();
1145
- statement.catchBranch = this.block(TokenKind_1.TokenKind.EndTry);
1142
+ catchStmt.catchBranch = this.block(TokenKind_1.TokenKind.EndTry);
1146
1143
  if (this.peek().kind !== TokenKind_1.TokenKind.EndTry) {
1147
1144
  this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.expectedEndTryToTerminateTryCatch()), { range: this.peek().range }));
1148
1145
  }
1149
1146
  else {
1150
- statement.endTryToken = this.advance();
1147
+ statement.tokens.endTry = this.advance();
1151
1148
  }
1152
1149
  return statement;
1153
1150
  }
@@ -1593,7 +1590,7 @@ class Parser {
1593
1590
  }
1594
1591
  anonymousFunction() {
1595
1592
  if (this.checkAny(TokenKind_1.TokenKind.Sub, TokenKind_1.TokenKind.Function)) {
1596
- const func = this.functionDeclaration(true);
1593
+ const func = this.functionDeclaration({ hasName: false, hasBody: true, hasEnd: true }).functionExpression;
1597
1594
  //if there's an open paren after this, this is an IIFE
1598
1595
  if (this.check(TokenKind_1.TokenKind.LeftParen)) {
1599
1596
  return this.finishCall(this.advance(), func);
@@ -2400,6 +2397,9 @@ class Parser {
2400
2397
  this._references.expressions.add(s.initialValue);
2401
2398
  }
2402
2399
  },
2400
+ InterfaceStatement: s => {
2401
+ this._references.interfaceStatements.push(s);
2402
+ },
2403
2403
  NamespaceStatement: s => {
2404
2404
  this._references.namespaceStatements.push(s);
2405
2405
  },
@@ -2413,7 +2413,7 @@ class Parser {
2413
2413
  this._references.libraryStatements.push(s);
2414
2414
  },
2415
2415
  FunctionExpression: (expression, parent) => {
2416
- if (!(0, reflection_1.isClassMethodStatement)(parent)) {
2416
+ if (!(0, reflection_1.isClassMethodStatement)(parent) && !(0, reflection_1.isInterfaceMethodStatement)(parent)) {
2417
2417
  this._references.functionExpressions.push(expression);
2418
2418
  }
2419
2419
  },
@@ -2545,7 +2545,7 @@ class References {
2545
2545
  if (!this._interfaceStatementLookup) {
2546
2546
  this._interfaceStatementLookup = new Map();
2547
2547
  for (const stmt of this.interfaceStatements) {
2548
- this._interfaceStatementLookup.set(stmt.fullName.toLowerCase(), stmt);
2548
+ this._interfaceStatementLookup.set(stmt.getName(ParseMode.BrighterScript).toLowerCase(), stmt);
2549
2549
  }
2550
2550
  }
2551
2551
  return this._interfaceStatementLookup;
@@ -2601,7 +2601,7 @@ function getBscTypeFromExpression(expression, functionExpression) {
2601
2601
  //function call
2602
2602
  }
2603
2603
  else if ((0, reflection_1.isNewExpression)(expression)) {
2604
- return (0, helpers_1.getTypeFromNewExpression)(expression, functionExpression); // new CustomType(expression.className.getName(ParseMode.BrighterScript));
2604
+ return (0, helpers_1.getTypeFromNewExpression)(expression, functionExpression);
2605
2605
  //Function call
2606
2606
  }
2607
2607
  else if ((0, reflection_1.isCallExpression)(expression)) {