maestro-cli-roku
Version:
command line tools for maestro-roku projects
378 lines (377 loc) • 19 kB
JavaScript
"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;