Alias would remove unnecessary repetitions and less relative paths make changes easier when moving around files. This commit cleans also up some relative paths ('../../../') by using the alias and orders imports. It updates both path alias in tsconfig and module alias in Vue CLI's bundler (vuejs/vue-cli#2398).
127 lines
5.2 KiB
TypeScript
127 lines
5.2 KiB
TypeScript
import { FunctionData, InstructionHolder } from 'js-yaml-loader!@/*';
|
|
import { SharedFunction } from './SharedFunction';
|
|
import { SharedFunctionCollection } from './SharedFunctionCollection';
|
|
import { ISharedFunctionCollection } from './ISharedFunctionCollection';
|
|
import { IFunctionCompiler } from './IFunctionCompiler';
|
|
import { IFunctionCallCompiler } from '../FunctionCall/IFunctionCallCompiler';
|
|
import { FunctionCallCompiler } from '../FunctionCall/FunctionCallCompiler';
|
|
|
|
export class FunctionCompiler implements IFunctionCompiler {
|
|
public static readonly instance: IFunctionCompiler = new FunctionCompiler();
|
|
protected constructor(
|
|
private readonly functionCallCompiler: IFunctionCallCompiler = FunctionCallCompiler.instance) {
|
|
}
|
|
public compileFunctions(functions: readonly FunctionData[]): ISharedFunctionCollection {
|
|
const collection = new SharedFunctionCollection();
|
|
if (!functions || !functions.length) {
|
|
return collection;
|
|
}
|
|
ensureValidFunctions(functions);
|
|
functions
|
|
.filter((func) => hasCode(func))
|
|
.forEach((func) => {
|
|
const shared = new SharedFunction(func.name, func.parameters, func.code, func.revertCode);
|
|
collection.addFunction(shared);
|
|
});
|
|
functions
|
|
.filter((func) => hasCall(func))
|
|
.forEach((func) => {
|
|
const code = this.functionCallCompiler.compileCall(func.call, collection);
|
|
const shared = new SharedFunction(func.name, func.parameters, code.code, code.revertCode);
|
|
collection.addFunction(shared);
|
|
});
|
|
return collection;
|
|
}
|
|
}
|
|
|
|
function hasCode(data: FunctionData): boolean {
|
|
return Boolean(data.code);
|
|
}
|
|
|
|
function hasCall(data: FunctionData): boolean {
|
|
return Boolean(data.call);
|
|
}
|
|
|
|
|
|
function ensureValidFunctions(functions: readonly FunctionData[]) {
|
|
ensureNoUndefinedItem(functions);
|
|
ensureNoDuplicatesInFunctionNames(functions);
|
|
ensureNoDuplicatesInParameterNames(functions);
|
|
ensureNoDuplicateCode(functions);
|
|
ensureEitherCallOrCodeIsDefined(functions);
|
|
ensureExpectedParameterNameTypes(functions);
|
|
}
|
|
|
|
function printList(list: readonly string[]): string {
|
|
return `"${list.join('","')}"`;
|
|
}
|
|
|
|
function ensureEitherCallOrCodeIsDefined(holders: readonly InstructionHolder[]) {
|
|
// Ensure functions do not define both call and code
|
|
const withBothCallAndCode = holders.filter((holder) => hasCode(holder) && hasCall(holder));
|
|
if (withBothCallAndCode.length) {
|
|
throw new Error(`both "code" and "call" are defined in ${printNames(withBothCallAndCode)}`);
|
|
}
|
|
// Ensure functions have either code or call
|
|
const hasEitherCodeOrCall = holders.filter((holder) => !hasCode(holder) && !hasCall(holder));
|
|
if (hasEitherCodeOrCall.length) {
|
|
throw new Error(`neither "code" or "call" is defined in ${printNames(hasEitherCodeOrCall)}`);
|
|
}
|
|
}
|
|
|
|
function ensureExpectedParameterNameTypes(functions: readonly FunctionData[]) {
|
|
const unexpectedFunctions = functions.filter((func) => func.parameters && !isArrayOfStrings(func.parameters));
|
|
if (unexpectedFunctions.length) {
|
|
throw new Error(`unexpected parameter name type in ${printNames(unexpectedFunctions)}`);
|
|
}
|
|
function isArrayOfStrings(value: any): boolean {
|
|
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
}
|
|
}
|
|
|
|
function printNames(holders: readonly InstructionHolder[]) {
|
|
return printList(holders.map((holder) => holder.name));
|
|
}
|
|
|
|
function ensureNoDuplicatesInFunctionNames(functions: readonly FunctionData[]) {
|
|
const duplicateFunctionNames = getDuplicates(functions
|
|
.map((func) => func.name.toLowerCase()));
|
|
if (duplicateFunctionNames.length) {
|
|
throw new Error(`duplicate function name: ${printList(duplicateFunctionNames)}`);
|
|
}
|
|
}
|
|
function ensureNoUndefinedItem(functions: readonly FunctionData[]) {
|
|
if (functions.some((func) => !func)) {
|
|
throw new Error(`some functions are undefined`);
|
|
}
|
|
}
|
|
function ensureNoDuplicatesInParameterNames(functions: readonly FunctionData[]) {
|
|
const functionsWithParameters = functions
|
|
.filter((func) => func.parameters && func.parameters.length > 0);
|
|
for (const func of functionsWithParameters) {
|
|
const duplicateParameterNames = getDuplicates(func.parameters);
|
|
if (duplicateParameterNames.length) {
|
|
throw new Error(`"${func.name}": duplicate parameter name: ${printList(duplicateParameterNames)}`);
|
|
}
|
|
}
|
|
}
|
|
function ensureNoDuplicateCode(functions: readonly FunctionData[]) {
|
|
const duplicateCodes = getDuplicates(functions
|
|
.map((func) => func.code)
|
|
.filter((code) => code),
|
|
);
|
|
if (duplicateCodes.length > 0) {
|
|
throw new Error(`duplicate "code" in functions: ${printList(duplicateCodes)}`);
|
|
}
|
|
const duplicateRevertCodes = getDuplicates(functions
|
|
.filter((func) => func.revertCode)
|
|
.map((func) => func.revertCode));
|
|
if (duplicateRevertCodes.length > 0) {
|
|
throw new Error(`duplicate "revertCode" in functions: ${printList(duplicateRevertCodes)}`);
|
|
}
|
|
}
|
|
|
|
function getDuplicates(texts: readonly string[]): string[] {
|
|
return texts.filter((item, index) => texts.indexOf(item) !== index);
|
|
}
|