From d5548d583eb0548da3ee286b5f978183a970ee95 Mon Sep 17 00:00:00 2001 From: George Cook Date: Wed, 13 Jan 2021 23:17:34 +0100 Subject: [PATCH 01/15] Adds annotation support for classes, attaching any pending annotations that appear before a class keyword --- src/parser/Parser.spec.ts | 17 ++++++++++++++++- src/parser/Parser.ts | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index ed69b9554..b2001b4d8 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -2,7 +2,7 @@ import { expect, assert } from 'chai'; import { Lexer, ReservedWords } from '../lexer'; import { DottedGetExpression, XmlAttributeGetExpression, CallfuncExpression, AnnotationExpression, CallExpression, FunctionExpression } from './Expression'; import { Parser, ParseMode } from './Parser'; -import type { AssignmentStatement, Statement } from './Statement'; +import type { AssignmentStatement, ClassStatement, Statement } from './Statement'; import { PrintStatement, FunctionStatement, NamespaceStatement, ImportStatement } from './Statement'; import { Range } from 'vscode-languageserver'; import { DiagnosticMessages } from '../DiagnosticMessages'; @@ -726,6 +726,21 @@ describe('parser', () => { expect(fn.annotations[0].call).to.be.instanceof(CallExpression); }); + it.only('attaches annotations to a class', () => { + let { statements, diagnostics } = parse(` + @meta1 + class MyClass + function main() + print "hello" + end function + end class + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + expect(cs.annotations?.length).to.equal(1); + expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); + }); + it('can convert argument of an annotation to JS types', () => { let { statements, diagnostics } = parse(` @meta1 diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 29298b485..450a8f57d 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -353,6 +353,9 @@ export class Parser { */ private classDeclaration(): ClassStatement { this.warnIfNotBrighterScriptMode('class declarations'); + let classAnnotations = this.pendingAnnotations; + this.pendingAnnotations = []; + let classKeyword = this.consume( DiagnosticMessages.expectedClassKeyword(), TokenKind.Class @@ -460,6 +463,12 @@ export class Parser { parentClassName, this.currentNamespaceName ); + + //attach annotations to statements + if (classAnnotations?.length) { + result.annotations = classAnnotations; + } + this._references.classStatements.push(result); return result; } From bdab30fc92e9b44588197aad54b146c0e5732a9e Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 00:10:36 +0100 Subject: [PATCH 02/15] fixes linting error --- src/parser/Parser.spec.ts | 2 +- src/parser/Parser.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index b2001b4d8..1fae77d34 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -726,7 +726,7 @@ describe('parser', () => { expect(fn.annotations[0].call).to.be.instanceof(CallExpression); }); - it.only('attaches annotations to a class', () => { + it('attaches annotations to a class', () => { let { statements, diagnostics } = parse(` @meta1 class MyClass diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 450a8f57d..8e6708256 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -355,7 +355,7 @@ export class Parser { this.warnIfNotBrighterScriptMode('class declarations'); let classAnnotations = this.pendingAnnotations; this.pendingAnnotations = []; - + let classKeyword = this.consume( DiagnosticMessages.expectedClassKeyword(), TokenKind.Class From a7fd3011283aa3b540568f02ce52e02af091fa99 Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 10:40:30 +0100 Subject: [PATCH 03/15] adds annotations to class members --- src/parser/Parser.spec.ts | 88 ++++++++++++++++++++++++++++++++++++++- src/parser/Parser.ts | 60 ++++++++++++++++---------- 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index 1fae77d34..f9208d245 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -733,7 +733,7 @@ describe('parser', () => { function main() print "hello" end function - end class + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; @@ -741,6 +741,92 @@ describe('parser', () => { expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); }); + it('attaches annotations to a class constructor', () => { + let { statements, diagnostics } = parse(` + class MyClass + @meta1 + function new() + print "hello" + end function + function methodA() + print "hello" + end function + end class + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + let stat = cs.body[0]; + expect(stat.annotations?.length).to.equal(1); + expect(stat.annotations[0]).to.be.instanceof(AnnotationExpression); + }); + + it('attaches annotations to a class methods', () => { + let { statements, diagnostics } = parse(` + class MyClass + function new() + print "hello" + end function + @meta1 + function methodA() + print "hello" + end function + end class + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + let stat = cs.body[1]; + expect(stat.annotations?.length).to.equal(1); + expect(stat.annotations[0]).to.be.instanceof(AnnotationExpression); + }); + it('attaches annotations to a class methods, fields and constructor', () => { + let { statements, diagnostics } = parse(` + @meta2 + @meta1 + class MyClass + @meta3 + @meta4 + function new() + print "hello" + end function + @meta5 + @meta6 + function methodA() + print "hello" + end function + + @meta5 + @meta6 + public foo="bar" + end class + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + expect(cs.annotations?.length).to.equal(2); + expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); + let stat1 = cs.body[0]; + let stat2 = cs.body[1]; + let f1 = cs.body[2]; + expect(stat1.annotations?.length).to.equal(2); + expect(stat1.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(stat2.annotations?.length).to.equal(2); + expect(stat2.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(f1.annotations?.length).to.equal(2); + expect(f1.annotations[0]).to.be.instanceof(AnnotationExpression); + }); + + it('ignores annotations on commented out lines', () => { + let { statements, diagnostics } = parse(` + '@meta1 + ' @meta1 + function new() + print "hello" + end function + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + expect(cs.annotations).to.be.undefined; + }); + it('can convert argument of an annotation to JS types', () => { let { statements, diagnostics } = parse(` @meta1 diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 8e6708256..0bd5f102c 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -82,7 +82,7 @@ import { } from './Expression'; import type { Diagnostic, Range } from 'vscode-languageserver'; import { Logger } from '../Logger'; -import { isCallExpression, isCallfuncExpression, isClassMethodStatement, isCommentStatement, isDottedGetExpression, isIfStatement, isIndexedGetExpression, isVariableExpression } from '../astUtils/reflection'; +import { isAnnotationExpression, isCallExpression, isCallfuncExpression, isClassMethodStatement, isCommentStatement, isDottedGetExpression, isIfStatement, isIndexedGetExpression, isVariableExpression } from '../astUtils/reflection'; import { createVisitor, WalkMode } from '../astUtils/visitors'; import { createStringLiteral, createToken } from '../astUtils/creators'; @@ -247,15 +247,15 @@ export class Parser { ) { let dec = this.declaration(); if (dec) { - //attach annotations to statements - if (this.pendingAnnotations.length > 0) { - dec.annotations = this.pendingAnnotations; - this.pendingAnnotations = []; + if (!isAnnotationExpression(dec)) { + this.consumePendingAnnotations(dec); + body.statements.push(dec); + //ensure statement separator + this.consumeStatementSeparators(); + } else { + this.consumeStatementSeparators(true); } - body.statements.push(dec); - //ensure statement separator - this.consumeStatementSeparators(); } else { //consume potential separators @@ -306,7 +306,7 @@ export class Parser { return error; } - private declaration(): Statement | undefined { + private declaration(): Statement | AnnotationExpression | undefined { try { if (this.check(TokenKind.Class)) { return this.classDeclaration(); @@ -325,8 +325,7 @@ export class Parser { } if (this.check(TokenKind.At) && this.checkNext(TokenKind.Identifier)) { - this.annotationExpression(); - return; + return this.annotationExpression(); } if (this.check(TokenKind.Comment)) { @@ -377,9 +376,14 @@ export class Parser { //gather up all class members (Fields, Methods) let body = [] as Statement[]; - while (this.checkAny(TokenKind.Public, TokenKind.Protected, TokenKind.Private, TokenKind.Function, TokenKind.Sub, TokenKind.Comment, TokenKind.Identifier, ...AllowedProperties)) { + while (this.checkAny(TokenKind.Public, TokenKind.Protected, TokenKind.Private, TokenKind.Function, TokenKind.Sub, TokenKind.Comment, TokenKind.Identifier, TokenKind.At, ...AllowedProperties)) { try { let accessModifier: Token; + + if (this.check(TokenKind.At) && this.checkNext(TokenKind.Identifier)) { + this.annotationExpression(); + } + if (this.checkAny(TokenKind.Public, TokenKind.Protected, TokenKind.Private)) { //use actual access modifier accessModifier = this.advance(); @@ -390,8 +394,10 @@ export class Parser { overrideKeyword = this.advance(); } + let fieldAnnotations = this.pendingAnnotations; //methods (function/sub keyword OR identifier followed by opening paren) if (this.checkAny(TokenKind.Function, TokenKind.Sub) || (this.checkAny(TokenKind.Identifier, ...AllowedProperties) && this.checkNext(TokenKind.LeftParen))) { + this.pendingAnnotations = []; let funcDeclaration = this.functionDeclaration(false, false); //remove this function from the lists because it's not a callable @@ -410,7 +416,7 @@ export class Parser { funcDeclaration.func, overrideKeyword ); - + methodStatement.annotations = fieldAnnotations; //refer to this statement as parent of the expression functionStatement.func.functionStatement = methodStatement; @@ -418,9 +424,10 @@ export class Parser { //fields } else if (this.checkAny(TokenKind.Identifier, ...AllowedProperties)) { - body.push( - this.classFieldDeclaration(accessModifier) - ); + + const fieldStatement = this.classFieldDeclaration(accessModifier); + fieldStatement.annotations = fieldAnnotations; + body.push(fieldStatement); //class fields cannot be overridden if (overrideKeyword) { @@ -465,7 +472,7 @@ export class Parser { ); //attach annotations to statements - if (classAnnotations?.length) { + if (classAnnotations?.length > 0) { result.annotations = classAnnotations; } @@ -1171,7 +1178,7 @@ export class Parser { return importStatement; } - private annotationExpression(): void { + private annotationExpression() { let annotation = new AnnotationExpression( this.advance(), this.advance() @@ -1183,6 +1190,7 @@ export class Parser { let leftParen = this.advance(); annotation.call = this.finishCall(leftParen, annotation, false); } + return annotation; } private templateString(isTagged: boolean): TemplateStringExpression | TaggedTemplateStringExpression { @@ -1763,12 +1771,10 @@ export class Parser { let loopCurrent = this.current; let dec = this.declaration(); if (dec) { - //attach annotations to statements - if (this.pendingAnnotations.length) { - dec.annotations = this.pendingAnnotations; - this.pendingAnnotations = []; + if (!isAnnotationExpression(dec)) { + this.consumePendingAnnotations(dec); + statements.push(dec); } - statements.push(dec); //ensure statement separator this.consumeStatementSeparators(); @@ -1808,6 +1814,14 @@ export class Parser { return new Block(statements, startingToken.range); } + consumePendingAnnotations(dec: Statement) { + //attach pending annotations to statements + if (this.pendingAnnotations.length) { + dec.annotations = this.pendingAnnotations; + this.pendingAnnotations = []; + } + } + private expression(): Expression { return this.anonymousFunction(); } From 36dbed91f82078007352529298e09c81ac9acee4 Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 10:54:34 +0100 Subject: [PATCH 04/15] adds some more testing for namespaced class annotations --- src/parser/Parser.spec.ts | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index f9208d245..231dc75a0 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -741,6 +741,80 @@ describe('parser', () => { expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); }); + it('attaches annotations to multiple clases', () => { + let { statements, diagnostics } = parse(` + @meta1 + class MyClass + function main() + print "hello" + end function + end class + @meta2 + class MyClass2 + function main() + print "hello" + end function + end class + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let cs = statements[0] as ClassStatement; + expect(cs.annotations?.length).to.equal(1); + expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(cs.annotations[0].name).to.equal('meta1'); + let cs2 = statements[1] as ClassStatement; + expect(cs2.annotations?.length).to.equal(1); + expect(cs2.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(cs2.annotations[0].name).to.equal('meta2'); + }); + + it('attaches annotations to a namespaced class', () => { + let { statements, diagnostics } = parse(` + namespace ns + @meta1 + class MyClass + function main() + print "hello" + end function + end class + end namespace + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let ns = statements[0] as NamespaceStatement; + let cs = ns.body.statements[0] as ClassStatement; + expect(cs.annotations?.length).to.equal(1); + expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); + }); + + it('attaches annotations to a namespaced class - multiple', () => { + let { statements, diagnostics } = parse(` + namespace ns + @meta1 + class MyClass + function main() + print "hello" + end function + end class + @meta2 + class MyClass2 + function main() + print "hello" + end function + end class + end namespace + `, ParseMode.BrighterScript); + expect(diagnostics[0]?.message).not.to.exist; + let ns = statements[0] as NamespaceStatement; + let cs = ns.body.statements[0] as ClassStatement; + expect(cs.annotations?.length).to.equal(1); + expect(cs.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(cs.annotations[0].name).to.equal('meta1'); + let cs2 = ns.body.statements[1] as ClassStatement; + expect(cs2.annotations?.length).to.equal(1); + expect(cs2.annotations[0]).to.be.instanceof(AnnotationExpression); + expect(cs2.annotations[0].name).to.equal('meta2'); + + }); + it('attaches annotations to a class constructor', () => { let { statements, diagnostics } = parse(` class MyClass From 6bad2e1f746318fb70457d6de11e9a4e31044ffb Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 10:56:08 +0100 Subject: [PATCH 05/15] fixes indentations --- src/parser/Parser.spec.ts | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index 231dc75a0..faae64e32 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -728,12 +728,12 @@ describe('parser', () => { it('attaches annotations to a class', () => { let { statements, diagnostics } = parse(` - @meta1 - class MyClass - function main() - print "hello" - end function - end class + @meta1 + class MyClass + function main() + print "hello" + end function + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; @@ -743,18 +743,18 @@ describe('parser', () => { it('attaches annotations to multiple clases', () => { let { statements, diagnostics } = parse(` - @meta1 - class MyClass - function main() - print "hello" - end function - end class - @meta2 - class MyClass2 - function main() - print "hello" - end function - end class + @meta1 + class MyClass + function main() + print "hello" + end function + end class + @meta2 + class MyClass2 + function main() + print "hello" + end function + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; @@ -890,11 +890,11 @@ describe('parser', () => { it('ignores annotations on commented out lines', () => { let { statements, diagnostics } = parse(` - '@meta1 - ' @meta1 - function new() - print "hello" - end function + '@meta1 + ' @meta1 + function new() + print "hello" + end function `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; From c052909247d1e41c4e0006f62cfad1407eeea1fc Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 10:58:56 +0100 Subject: [PATCH 06/15] white space removal --- src/parser/Parser.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 0bd5f102c..e6c23e76e 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -256,7 +256,6 @@ export class Parser { this.consumeStatementSeparators(true); } - } else { //consume potential separators this.consumeStatementSeparators(true); From 13fe8f08db87700499f37a8a167696bd836759af Mon Sep 17 00:00:00 2001 From: George Cook Date: Thu, 14 Jan 2021 11:00:44 +0100 Subject: [PATCH 07/15] remove reduntant else --- src/parser/Parser.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index e6c23e76e..2f8414581 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -255,10 +255,6 @@ export class Parser { } else { this.consumeStatementSeparators(true); } - - } else { - //consume potential separators - this.consumeStatementSeparators(true); } } } catch (parseError) { From 4f5fa0646a8c2baf483a10863ab2fea62e3c0fa5 Mon Sep 17 00:00:00 2001 From: Bronley Date: Thu, 14 Jan 2021 06:03:16 -0500 Subject: [PATCH 08/15] Properly indent bs code in tests --- src/parser/Parser.spec.ts | 114 +++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index faae64e32..a19ec8a42 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -769,14 +769,14 @@ describe('parser', () => { it('attaches annotations to a namespaced class', () => { let { statements, diagnostics } = parse(` - namespace ns - @meta1 - class MyClass - function main() - print "hello" - end function - end class - end namespace + namespace ns + @meta1 + class MyClass + function main() + print "hello" + end function + end class + end namespace `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let ns = statements[0] as NamespaceStatement; @@ -787,20 +787,20 @@ describe('parser', () => { it('attaches annotations to a namespaced class - multiple', () => { let { statements, diagnostics } = parse(` - namespace ns - @meta1 - class MyClass - function main() - print "hello" - end function - end class - @meta2 - class MyClass2 - function main() - print "hello" - end function - end class - end namespace + namespace ns + @meta1 + class MyClass + function main() + print "hello" + end function + end class + @meta2 + class MyClass2 + function main() + print "hello" + end function + end class + end namespace `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let ns = statements[0] as NamespaceStatement; @@ -817,15 +817,15 @@ describe('parser', () => { it('attaches annotations to a class constructor', () => { let { statements, diagnostics } = parse(` - class MyClass - @meta1 - function new() - print "hello" - end function - function methodA() - print "hello" - end function - end class + class MyClass + @meta1 + function new() + print "hello" + end function + function methodA() + print "hello" + end function + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; @@ -836,15 +836,15 @@ describe('parser', () => { it('attaches annotations to a class methods', () => { let { statements, diagnostics } = parse(` - class MyClass - function new() - print "hello" - end function - @meta1 - function methodA() - print "hello" - end function - end class + class MyClass + function new() + print "hello" + end function + @meta1 + function methodA() + print "hello" + end function + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; @@ -854,24 +854,24 @@ describe('parser', () => { }); it('attaches annotations to a class methods, fields and constructor', () => { let { statements, diagnostics } = parse(` - @meta2 - @meta1 - class MyClass - @meta3 - @meta4 - function new() - print "hello" - end function - @meta5 - @meta6 - function methodA() - print "hello" - end function + @meta2 + @meta1 + class MyClass + @meta3 + @meta4 + function new() + print "hello" + end function + @meta5 + @meta6 + function methodA() + print "hello" + end function - @meta5 - @meta6 - public foo="bar" - end class + @meta5 + @meta6 + public foo="bar" + end class `, ParseMode.BrighterScript); expect(diagnostics[0]?.message).not.to.exist; let cs = statements[0] as ClassStatement; From 14fb14d722b50f0dbd92e2f46ab090df8f8cce8d Mon Sep 17 00:00:00 2001 From: Bronley Date: Thu, 14 Jan 2021 06:03:27 -0500 Subject: [PATCH 09/15] Remove unnecessary else block --- src/parser/Parser.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 2f8414581..90db53993 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -250,11 +250,9 @@ export class Parser { if (!isAnnotationExpression(dec)) { this.consumePendingAnnotations(dec); body.statements.push(dec); - //ensure statement separator - this.consumeStatementSeparators(); - } else { - this.consumeStatementSeparators(true); } + //ensure statement separator + this.consumeStatementSeparators(); } } } catch (parseError) { From 28a0a6cdfc92d74ba8a96ed8d3e10e0f3df9c86f Mon Sep 17 00:00:00 2001 From: Bronley Date: Thu, 14 Jan 2021 06:09:10 -0500 Subject: [PATCH 10/15] Better document `consumePendingAnnotations` --- src/parser/Parser.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 90db53993..343fdf77f 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -1807,10 +1807,13 @@ export class Parser { return new Block(statements, startingToken.range); } - consumePendingAnnotations(dec: Statement) { - //attach pending annotations to statements + /** + * Attach pending annotations to the provided statement, + * and then reset the annotations array + */ + consumePendingAnnotations(statement: Statement) { if (this.pendingAnnotations.length) { - dec.annotations = this.pendingAnnotations; + statement.annotations = this.pendingAnnotations; this.pendingAnnotations = []; } } From 352c9e8d05910c05f8d6a5feff1d4d1b32aa9838 Mon Sep 17 00:00:00 2001 From: Bronley Date: Thu, 14 Jan 2021 06:49:13 -0500 Subject: [PATCH 11/15] Restore necessary else block. --- src/parser/Parser.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 343fdf77f..674747a0d 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -250,9 +250,11 @@ export class Parser { if (!isAnnotationExpression(dec)) { this.consumePendingAnnotations(dec); body.statements.push(dec); + //ensure statement separator + this.consumeStatementSeparators(false); + } else { + this.consumeStatementSeparators(true); } - //ensure statement separator - this.consumeStatementSeparators(); } } } catch (parseError) { From 0a6332ac039a1bebb90872878449fe5d1da95c84 Mon Sep 17 00:00:00 2001 From: George Cook Date: Sat, 16 Jan 2021 10:59:12 +0100 Subject: [PATCH 12/15] addresses feedback from Bron --- src/parser/Parser.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index dc28d4492..cc23c9f8b 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -348,6 +348,7 @@ export class Parser { private classDeclaration(): ClassStatement { this.warnIfNotBrighterScriptMode('class declarations'); let classAnnotations = this.pendingAnnotations; + //reset annotations here, so we don't carry them onto class members this.pendingAnnotations = []; let classKeyword = this.consume( @@ -389,9 +390,12 @@ export class Parser { overrideKeyword = this.advance(); } - let fieldAnnotations = this.pendingAnnotations; + //cache annotations to this point, for when we later create the class member, because statements inside blocks can otherwise inherit these annotations + let memberAnnotations = this.pendingAnnotations; + //methods (function/sub keyword OR identifier followed by opening paren) if (this.checkAny(TokenKind.Function, TokenKind.Sub) || (this.checkAny(TokenKind.Identifier, ...AllowedProperties) && this.checkNext(TokenKind.LeftParen))) { + //clear out pending annotations; only if we find a block this.pendingAnnotations = []; let funcDeclaration = this.functionDeclaration(false, false); @@ -411,7 +415,7 @@ export class Parser { funcDeclaration.func, overrideKeyword ); - methodStatement.annotations = fieldAnnotations; + methodStatement.annotations = memberAnnotations; //refer to this statement as parent of the expression functionStatement.func.functionStatement = methodStatement; @@ -421,7 +425,7 @@ export class Parser { } else if (this.checkAny(TokenKind.Identifier, ...AllowedProperties)) { const fieldStatement = this.classFieldDeclaration(accessModifier); - fieldStatement.annotations = fieldAnnotations; + fieldStatement.annotations = memberAnnotations; body.push(fieldStatement); //class fields cannot be overridden From 111791b4829bc66fd1636d706ad6e7a59842cdf8 Mon Sep 17 00:00:00 2001 From: George Cook Date: Sat, 16 Jan 2021 11:55:03 +0100 Subject: [PATCH 13/15] addresses feedback from Philippe --- src/parser/Parser.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index cc23c9f8b..d22560264 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -395,8 +395,6 @@ export class Parser { //methods (function/sub keyword OR identifier followed by opening paren) if (this.checkAny(TokenKind.Function, TokenKind.Sub) || (this.checkAny(TokenKind.Identifier, ...AllowedProperties) && this.checkNext(TokenKind.LeftParen))) { - //clear out pending annotations; only if we find a block - this.pendingAnnotations = []; let funcDeclaration = this.functionDeclaration(false, false); //remove this function from the lists because it's not a callable @@ -415,7 +413,10 @@ export class Parser { funcDeclaration.func, overrideKeyword ); + methodStatement.annotations = memberAnnotations; + this.pendingAnnotations = []; + //refer to this statement as parent of the expression functionStatement.func.functionStatement = methodStatement; From 3cba26dfa641317375c69ab4a06bbc37a627404b Mon Sep 17 00:00:00 2001 From: George Cook Date: Sat, 16 Jan 2021 19:25:19 +0100 Subject: [PATCH 14/15] no need to cache annotations for fileds --- src/parser/Parser.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index d22560264..14eff6762 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -390,11 +390,10 @@ export class Parser { overrideKeyword = this.advance(); } - //cache annotations to this point, for when we later create the class member, because statements inside blocks can otherwise inherit these annotations - let memberAnnotations = this.pendingAnnotations; - //methods (function/sub keyword OR identifier followed by opening paren) if (this.checkAny(TokenKind.Function, TokenKind.Sub) || (this.checkAny(TokenKind.Identifier, ...AllowedProperties) && this.checkNext(TokenKind.LeftParen))) { + let memberAnnotations = this.pendingAnnotations; + //cache annotations to this point, for when we later create the class member, because statements inside blocks can otherwise inherit these annotations let funcDeclaration = this.functionDeclaration(false, false); //remove this function from the lists because it's not a callable @@ -426,7 +425,7 @@ export class Parser { } else if (this.checkAny(TokenKind.Identifier, ...AllowedProperties)) { const fieldStatement = this.classFieldDeclaration(accessModifier); - fieldStatement.annotations = memberAnnotations; + this.consumePendingAnnotations(fieldStatement); body.push(fieldStatement); //class fields cannot be overridden From 52665467be60de840049afe41b363c781348a8c6 Mon Sep 17 00:00:00 2001 From: George Cook Date: Sat, 16 Jan 2021 21:50:17 +0100 Subject: [PATCH 15/15] fixes improper clearing out of member annotations --- src/parser/Parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 09d4868cb..e057d1046 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -392,8 +392,9 @@ export class Parser { //methods (function/sub keyword OR identifier followed by opening paren) if (this.checkAny(TokenKind.Function, TokenKind.Sub) || (this.checkAny(TokenKind.Identifier, ...AllowedProperties) && this.checkNext(TokenKind.LeftParen))) { - let memberAnnotations = this.pendingAnnotations; //cache annotations to this point, for when we later create the class member, because statements inside blocks can otherwise inherit these annotations + let memberAnnotations = this.pendingAnnotations; + this.pendingAnnotations = []; let funcDeclaration = this.functionDeclaration(false, false); //remove this function from the lists because it's not a callable @@ -414,7 +415,6 @@ export class Parser { ); methodStatement.annotations = memberAnnotations; - this.pendingAnnotations = []; //refer to this statement as parent of the expression functionStatement.func.functionStatement = methodStatement;