UNPKG

maestro-cli-roku

Version:

command line tools for maestro-roku projects

378 lines (377 loc) 19 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spread = (this && this.__spread) || function () { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); var FileType_1 = require("../fileProcessing/FileType"); var ProcessorSettings_1 = require("../fileProcessing/ProcessorSettings"); var Feedback_1 = require("../utils/Feedback"); var BrsClass_1 = require("./BrsClass"); var BrsClassMember_1 = require("./BrsClassMember"); /* Crude brighterscript class processor */ var ClassProcessor = /** @class */ (function () { function ClassProcessor(projectProcessor) { this.startClassRegex = new RegExp("^( |\\t)*class(?: |\\t)+([a-zA-Z_\\-0-9]+) *(extends ([a-zA-Z_\\-0-9]*))*", "gim"); this.endClassRegex = new RegExp("^( |\\t)*(end class)", "gim"); this.functionMemberRegex = new RegExp('^(?: |\\t)*(?:(public|private)*(?: |\\t))*(?:(override)*(?: |\\t))*(sub|function)(?: |\\t)+([a-zA-Z_0-9]+)(?: |\\t)*\\(((.*))\\)(.*)$', 'gim'); this.varMemberRegex = new RegExp('^(?: |\\t)*(public|private)(?: |\\t)+([a-zA-Z_\\-0-9]+)((?: |\\t)+(?:as)(?: |\\t)+([a-zA-Z_\\-0-9]*)*)*((?: |\\t)+(?:=)(?: |\\t)+(.*))*', 'gim'); this.endFuncRegex = new RegExp('^(?: |\\t)*(end(?: |\\t))*(sub|function)(?:.*)$', 'gim'); this.settings = new ProcessorSettings_1.ProcessorSettings(); this.config = projectProcessor.config; this.fileMap = projectProcessor.fileMap; this.projectProcessor = projectProcessor; } ClassProcessor.prototype.getClassMapFunction = function (namespace) { var text = "\n function " + namespace + "_getClass(name)\n "; this.fileMap.allClasses.length; this.fileMap.allClasses.forEach(function (clazz, index) { text += "\n " + (index === 0 ? '' : 'else ') + " if name = \"" + clazz.className + "\"\n return " + clazz.className + "\n "; }); text += (this.fileMap.allClasses.length > 0 ? 'end if' : '') + "\n end function\n "; return text; }; ClassProcessor.prototype.parseFile = function (file) { if (!file || file.fileType !== FileType_1.FileType.UncompiledBrighterscript) { throw new Error('was given a non-bs file'); } var brsClass = this.createClass(file); if (brsClass) { //Tidy up the file file.brsClass = brsClass; this.projectProcessor.fileMap.addClass(brsClass); this.convertBrighterScriptClassToBrightscript(brsClass); } //fix general brightscript this.convertNewKeywords(file); var callEmptyFuncShortcutRegex = new RegExp('( *|\t)([a-z|A-Z|_|0-9]*)(\\@\\.)([a-z|A-Z|_|0-9]*)(\\s*)(\\((?: *|\\t*)\\))', 'gim'); file.setFileContents(file.getFileContents().replace(callEmptyFuncShortcutRegex, function (v0, v1, v2, v3, v4) { return "" + v1 + v2 + ".callFunc(\"" + v4 + "\", invalid)"; })); var callFuncShortcutRegex = new RegExp('( *|\t)([a-z|A-Z|_|0-9]*)(\\@\\.)([a-z|A-Z|_|0-9]*)(\\s*)(\\()', 'gim'); file.setFileContents(file.getFileContents().replace(callFuncShortcutRegex, function (v0, v1, v2, v3, v4) { return "" + v1 + v2 + ".callFunc(\"" + v4 + "\", "; })); }; ClassProcessor.prototype.convertNewKeywords = function (file) { var instantiateRegex = new RegExp('( |\\t)*(new)+( |\\t)+([a-zA-Z_0-9]*)( |\\t)*\\(', 'gim'); var lines = file.getFileContents().split('\n'); var isDirty = false; var _loop_1 = function (i) { var line = lines[i]; instantiateRegex.lastIndex = 0; if (instantiateRegex.test(line)) { instantiateRegex.lastIndex = 0; lines[i] = line.replace(instantiateRegex, function (m0, m1, m2, m3, m4, m5) { if (m4.trim() === '') { Feedback_1.feedbackError(file, "Found new keyword, with no class name: " + file.filename + ":" + i); } var member = new BrsClassMember_1.BrsClassMember(null, m4, BrsClassMember_1.MemberVisibility.Private, BrsClassMember_1.MemberType.Instantiation, ''); member.lineNumber = i; file.instantiators.push(member); isDirty = true; return (m1 || '') + " " + m3 + m4 + (m5 || '') + "("; }); } }; for (var i = 0; i < lines.length; i++) { _loop_1(i); } if (isDirty) { file.setFileContents(lines.join('\n')); } }; ClassProcessor.prototype.parseNodeClassFile = function (file) { //TODO - ascertain this really is a code behind file if (!file || file.fileType !== FileType_1.FileType.UncompiledBrighterscript) { throw new Error('was given a non-bs file'); } var xmlFile = file.associatedFile.programFile; var parentFile = file.associatedFile.parentFile ? file.associatedFile.parentFile.programFile : null; var extendedName = parentFile ? parentFile.componentName : null; var brsClass = new BrsClass_1.BrsClass(file, xmlFile.componentName, extendedName); var members = this.getMembersForFile(file, xmlFile.componentName); brsClass.members = members; brsClass.classType = BrsClass_1.ClassType.Node; //Tidy up the file file.brsClass = brsClass; this.projectProcessor.fileMap.addNodeClass(brsClass); this.convertBrighterScriptClassToBrightscript(brsClass); }; ClassProcessor.prototype.addClassConstructors = function () { var _this = this; this.projectProcessor.fileMap.allClasses.forEach(function (c) { _this.addConstructorToFile(c.file); c.file.saveFileContents(); }); }; ClassProcessor.prototype.validateClasses = function () { var _this = this; var classMap = this.projectProcessor.fileMap.classesByClassName; this.projectProcessor.fileMap.allClasses.forEach(function (c) { if (c.extendsName && c.extendsName.trim() !== '') { if (!classMap.has(c.extendsName)) { //TODO add compiler options, to allow this to be a warning - coz we might be using precompiled sources Feedback_1.feedbackError(c.file, "Extended class " + c.extendsName + " is not found!"); } else { c.extendedClass = classMap.get(c.extendsName); } } }); this.projectProcessor.fileMap.allClasses.forEach(function (c) { if (c.extendedClass) { _this.validateClassMembers(c); } }); var nonClassFiles = __spread(this.fileMap.allFiles.values()).filter(function (f) { return f.fileType === FileType_1.FileType.Brs || f.fileType === FileType_1.FileType.CodeBehind && !f.brsClass; }); nonClassFiles.forEach(function (f) { f.instantiators.forEach(function (instantiator) { if (!_this.fileMap.classesByClassName.has(instantiator.name)) { Feedback_1.feedbackError(f, "Attempted to instantiate unknown class " + instantiator.name + " " + f.filename + ":(" + instantiator.lineNumber + ")"); } }); }); }; ClassProcessor.prototype.validateClassMembers = function (c) { var e_1, _a; try { for (var _b = __values(c.methods), _c = _b.next(); !_c.done; _c = _b.next()) { var method = _c.value; var implementingClass = c.getImplementingParent(method.name); if (method.isOverride) { if (!implementingClass && method.type !== BrsClassMember_1.MemberType.Constructor) { Feedback_1.feedbackError(c.file, "Overridden method " + c.className + "." + method.name + " is marked as override; but not found in any base class"); continue; } else if (!c.extendsName) { Feedback_1.feedbackError(c.file, "Overridden method " + c.className + "." + method.name + " is marked as override; but the class " + c.className + " does not extend anything"); } } else if (!method.isOverride) { if (implementingClass) { Feedback_1.feedbackError(c.file, "Method " + c.className + "." + method.name + " is not marked as override; but is overriding extended class " + implementingClass.className + "." + method.name); } } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } }; ClassProcessor.prototype.addConstructorToFile = function (file) { if (file.brsClass) { var fileContents = file.getFileContents(); fileContents = file.brsClass.replaceSuperCalls(fileContents); fileContents += '\n\n' + file.brsClass.getBuilderFunction(); file.setFileContents(fileContents + '\n\n' + file.brsClass.getConstructorFunction()); } else { throw new Error('was given a non-bs file - add constructor requires a compiled bs file (i.e. brs file, with a brsClass)'); } }; ClassProcessor.prototype.createClass = function (file) { var className = this.getClassNameFromFile(file); if (className) { var extendedName = this.getExtendedNameFromFile(file); var brsClass = new BrsClass_1.BrsClass(file, className, extendedName); file.brsClass = brsClass; var members = this.getMembersForFile(file, className); brsClass.members = members; return brsClass; } return null; }; ClassProcessor.prototype.convertBrighterScriptClassToBrightscript = function (brsClass) { this.startClassRegex.lastIndex = 0; var updatedContents = brsClass.file .getFileContents() .replace(this.startClassRegex, ''); updatedContents = updatedContents.replace(this.endClassRegex, ''); var fixFunctionRegex = new RegExp('^( |\\t)*((?:(?:public|private)*(?: |\\t)*)*(?:(?:override)*(?: |\\t)*)*)(function|sub)+', 'gim'); updatedContents = updatedContents.replace(fixFunctionRegex, function (m0, m1, m2, m3) { return ''.padEnd((m1 ? m1.length : 0) + (m2 ? m2.length : 0), ' ') + m3; }); updatedContents = updatedContents.replace(fixFunctionRegex, '$1$2$3'); var fixVarRegex = new RegExp('^( |\\t)*(?:public|private)+( |\\t)+.*$', 'gim'); updatedContents = updatedContents.replace(fixVarRegex, ""); brsClass.file.setFileContents(updatedContents); }; ClassProcessor.prototype.getClassNameFromFile = function (file) { var className = null; var matches; var classCount = 0; this.startClassRegex.lastIndex = 0; while ((matches = this.startClassRegex.exec(file.getFileContents()))) { classCount++; if (classCount > 1) { Feedback_1.feedbackError(file, "More than one class defined for file " + file.fullPath); } else if (matches.length > 2) { className = matches[2]; break; } } return className; }; ClassProcessor.prototype.getExtendedNameFromFile = function (file) { var extendedName = null; var matches; var classCount = 0; this.startClassRegex.lastIndex = 0; while ((matches = this.startClassRegex.exec(file.getFileContents()))) { extendedName = matches[4]; break; } return extendedName; }; ClassProcessor.prototype.getMembersForFile = function (file, className) { if (!file || file.fileType !== FileType_1.FileType.UncompiledBrighterscript) { throw new Error('was given a non-bs file'); } var members = new Map(); this.functionMemberRegex.lastIndex = 0; var lines = file.getFileContents().split('\n'); var isInFunction = false; var brsClass = file.brsClass; var currentBlock = new BrsClassMember_1.BrsClassMember(brsClass, className, BrsClassMember_1.MemberVisibility.Public, BrsClassMember_1.MemberType.Class, className); currentBlock.lineNumber = 0; //TODO - get proper block scopes in here for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (isInFunction) { //check end function this.endFuncRegex.lastIndex = 0; if (this.endFuncRegex.test(line)) { isInFunction = false; continue; } var superRegex = new RegExp('( |\\t)*(m.super|super|super\\.[a-z|0-9|_]*)( |\\t)*(\\()+', 'gim'); if (superRegex.test(line)) { if (currentBlock.superCallLineNumber !== -1) { Feedback_1.feedbackError(file, "Method has too many super calls: " + currentBlock.name + ": " + file.filename + ":" + i); } else if (!currentBlock.isOverride && (currentBlock.type !== BrsClassMember_1.MemberType.Constructor)) { Feedback_1.feedbackError(file, "Super call found in non-overriding method: " + currentBlock.name + ": " + file.filename + ":" + i); } else { currentBlock.superCallLineNumber = i; currentBlock.superCallMethodName = currentBlock.name; } } } else { var member = this.processMethodMember(brsClass, line, i) || this.processVarMember(brsClass, line, i); if (member) { if (members.has(member.name)) { Feedback_1.feedbackError(file, "Found duplicate member definition for " + member.name + ": " + file.filename + ":" + i); } if (member.type !== BrsClassMember_1.MemberType.Variable) { currentBlock = member; isInFunction = true; } members.set(member.name, member); } } } return members; }; ClassProcessor.prototype.processMethodMember = function (brsClass, line, lineNumber) { var member = null; //check if it's a member var matches; this.functionMemberRegex.lastIndex = 0; while ((matches = this.functionMemberRegex.exec(line))) { if (matches.length > 4) { var name_1 = matches[4]; var visibility = !matches[1] || matches[1].toLowerCase() === 'public' ? BrsClassMember_1.MemberVisibility.Public : BrsClassMember_1.MemberVisibility.Private; var memberType = void 0; if (name_1.toLowerCase() === 'new') { memberType = BrsClassMember_1.MemberType.Constructor; visibility = BrsClassMember_1.MemberVisibility.Private; } else { memberType = matches[3].toLowerCase() === 'sub' ? BrsClassMember_1.MemberType.Sub : BrsClassMember_1.MemberType.Function; } //HACK! for now the defaultValue maps to the raw text for the args var defaultValue = matches.length > 5 ? matches[5] : ''; var returnType = matches.length > 6 ? matches[7] : ''; member = new BrsClassMember_1.BrsClassMember(brsClass, name_1, visibility, memberType, returnType, defaultValue); member.isOverride = matches[2] === 'override' || memberType === BrsClassMember_1.MemberType.Constructor; member.lineNumber = lineNumber; } return member; } }; ClassProcessor.prototype.processVarMember = function (brsClass, line, lineNumber) { var member = null; //get variable members this.varMemberRegex.lastIndex = 0; var matches; while ((matches = this.varMemberRegex.exec(line))) { if (matches.length > 2) { var name_2 = matches[2]; if (name_2.toLowerCase() !== 'sub' && name_2.toLowerCase() !== 'function') { var visibility = matches[1].toLowerCase() === 'public' ? BrsClassMember_1.MemberVisibility.Public : BrsClassMember_1.MemberVisibility.Private; var memberType = matches.length > 4 ? matches[4] || 'dynamic' : 'dynamic'; var defaultValue = matches.length > 6 ? matches[6] || 'invalid' : 'invalid'; member = new BrsClassMember_1.BrsClassMember(brsClass, name_2, visibility, BrsClassMember_1.MemberType.Variable, memberType, defaultValue); member.isOverride = false; member.lineNumber = lineNumber; } } } return member; }; ClassProcessor.prototype.getDefaultValue = function (text) { var regexp = new RegExp('((?: |\\t)*(=)(?: |\\t)*(.*))', 'gim'); var matches; while ((matches = regexp.exec(text))) { return matches.length > 3 ? matches[3] : ''; } return 'invalid'; }; return ClassProcessor; }()); exports.default = ClassProcessor;