From 8362b18ef19af58087a0f87e4a3deda7568547f4 Mon Sep 17 00:00:00 2001 From: Tyler Smith Date: Thu, 3 Oct 2024 10:19:07 -0300 Subject: [PATCH] PLugin logic --- .vscode/settings.json | 7 +++- package-lock.json | 2 +- package.json | 2 +- src/FileTranspilers.ts | 76 +++++++++++++++++++++++++++++++++ src/FileValidator.ts | 40 ++++++++++++++++++ src/Plugin.spec.ts | 95 ++++++++++++++++++++++++++---------------- src/Plugin.ts | 55 ++++++++---------------- src/diagnostics.ts | 11 +++++ 8 files changed, 210 insertions(+), 78 deletions(-) create mode 100644 src/FileTranspilers.ts create mode 100644 src/FileValidator.ts create mode 100644 src/diagnostics.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 68d01a9..7b9641d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,10 +15,15 @@ "**/bower_components": true, "**/dist": true }, + "editor.formatOnSave": true, "editor.tabSize": 4, "editor.insertSpaces": true, "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": true, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, "files.trimTrailingWhitespace": true, - "typescript.tsdk": "node_modules\\typescript\\lib" + "typescript.tsdk": "node_modules\\typescript\\lib", + "cSpell.words": [ + "brighterscript", + "undent" + ] } diff --git a/package-lock.json b/package-lock.json index b5781c2..45a1a3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "rimraf": "^2.7.1", "source-map-support": "^0.5.13", "ts-node": "8.9.1", - "typescript": "^4.7.2", + "typescript": "^4.8.3", "typescript-formatter": "^7.2.2", "undent": "^0.1.0" }, diff --git a/package.json b/package.json index 2626534..14050a6 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "rimraf": "^2.7.1", "source-map-support": "^0.5.13", "ts-node": "8.9.1", - "typescript": "^4.7.2", + "typescript": "^4.8.3", "typescript-formatter": "^7.2.2", "undent": "^0.1.0" }, diff --git a/src/FileTranspilers.ts b/src/FileTranspilers.ts new file mode 100644 index 0000000..b0a3b49 --- /dev/null +++ b/src/FileTranspilers.ts @@ -0,0 +1,76 @@ +import type { AstEditor, FunctionStatement, BrsFile, BscFile, Identifier, CallExpression, Expression, ReturnStatement, Statement, AssignmentStatement } from 'brighterscript'; +import { isBrsFile, Parser, WalkMode, createVisitor, util as bscUtil, isExpressionStatement, GroupingExpression, createToken, TokenKind, isCommentStatement } from 'brighterscript'; + +import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState'; +import { SourceNode } from 'source-map'; + +export class FileTranspiler { + public preprocess(file: BscFile, editor: AstEditor, annotatedFunctions: Map) { + if (isBrsFile(file)) { + this.walkBscExpressions(file, editor, annotatedFunctions); + } + } + private walkBscExpressions(file: BscFile, editor: AstEditor, annotatedFunctions: Map) { + if (isBrsFile(file)) { + file.ast.walk(createVisitor({ + CallExpression: (call) => { + const parts = bscUtil.getAllDottedGetParts(call.callee); + return ( + this.inlineCall(file, editor, call, parts ?? [], annotatedFunctions) + ); + } + }), { + walkMode: WalkMode.visitAllRecursive, + editor: editor + }); + } + } + + private inlineCall(file: BrsFile, editor: AstEditor, call: CallExpression, parts: Identifier[], annotatedFunctions: Map) { + const fullFunctionName = parts?.map(x => x.text).join('.').toLowerCase(); + const annotatedFunction = annotatedFunctions.get(fullFunctionName); + if (annotatedFunction) { + const parameterMap = new Map(); + + for (let index = 0; index < annotatedFunction.func.parameters.length; index++) { + const annotatedParameter = annotatedFunction.func.parameters[index]; + const callArgument = call.args[index] ?? annotatedParameter?.defaultValue; + parameterMap.set(annotatedParameter.name.text, callArgument); + } + + const clonedStatement = this.cloneStatement(annotatedFunction.func.body.statements.filter(x => !isCommentStatement(x))[0], file); + clonedStatement.walk(createVisitor({ + VariableExpression: (variable, parent?, owner?, key?) => { + const arg = parameterMap.get(variable.name.text); + if (arg) { + return this.cloneExpression(arg, file); + } + } + }), { + walkMode: WalkMode.visitAllRecursive, + editor: editor + }); + + if (isExpressionStatement(call.parent)) { + return clonedStatement.value; + } else { + return new GroupingExpression({ + left: createToken(TokenKind.LeftParen), + right: createToken(TokenKind.RightParen) + }, clonedStatement.value!); + } + } + } + + private cloneStatement(statement: Statement, file: BrsFile) { + return statement.clone() as T; + } + + private cloneExpression(expression: Expression, file: BrsFile) { + const newCode = new SourceNode(null, null, null, [ + (expression.transpile(new BrsTranspileState(file)) as any) + ]).toString(); + return (Parser.parse(`__thing = ${newCode}`).ast.statements[0] as AssignmentStatement).value as T; + } +} + diff --git a/src/FileValidator.ts b/src/FileValidator.ts new file mode 100644 index 0000000..bb40997 --- /dev/null +++ b/src/FileValidator.ts @@ -0,0 +1,40 @@ +import type { BscFile, FunctionStatement } from 'brighterscript'; +import { isCommentStatement } from 'brighterscript'; +import { isReturnStatement } from 'brighterscript'; +import { BsDiagnostic, ParseMode } from 'brighterscript'; +import { AstEditor, createVisitor, isBrsFile, WalkMode } from 'brighterscript'; +import { diagnostics } from './diagnostics'; + +export class FileValidator { + private editor = new AstEditor(); + public annotatedFunctions = new Map(); + + public reset() { + this.editor.undoAll(); + this.editor = new AstEditor(); + this.annotatedFunctions.clear(); + } + + public findAnnotations(file: BscFile) { + if (isBrsFile(file)) { + file.ast.walk(createVisitor({ + FunctionStatement: (statement, parent?, owner?, key?) => { + if (statement.annotations?.find(annotation => annotation.name.toLowerCase() === 'inline')) { + this.annotatedFunctions.set(statement.getName(ParseMode.BrighterScript).toLowerCase(), statement); + const bodyStatements = statement.func.body.statements.filter(x => !isCommentStatement(x)); + + if (bodyStatements.length !== 1 || !isReturnStatement(bodyStatements[0])) { + file.addDiagnostic( + diagnostics.badAnnotationBody( + bodyStatements[bodyStatements.length - 1]?.range ?? statement.func.range + ) + ); + } + } + } + }), { + walkMode: WalkMode.visitStatements + }); + } + } +} diff --git a/src/Plugin.spec.ts b/src/Plugin.spec.ts index ee6034a..83eddff 100644 --- a/src/Plugin.spec.ts +++ b/src/Plugin.spec.ts @@ -1,4 +1,4 @@ -import { Program, standardizePath as s } from 'brighterscript'; +import { Program, standardizePath as s, util } from 'brighterscript'; import * as fsExtra from 'fs-extra'; import { Plugin } from './Plugin'; import undent from 'undent'; @@ -25,26 +25,17 @@ describe('Plugin', () => { fsExtra.removeSync(tempDir); }); - it('adds a leading print statement to every named function', async () => { + it('Verifies basic annotation support', async () => { program.setFile('source/main.bs', ` - sub main() - m.name = "main" + sub init() + print subtract(5,3) end sub - function temp() - m.name = "temp" + @inline + function subtract(a, b) as integer + return a - b end function `); - program.setFile('components/CustomButton.xml', ` - -