Refactor to improve iterations

- Use function abstractions (such as map, reduce, filter etc.) over
  for-of loops to gain benefits of having less side effects and easier
  readability.
- Enable `downLevelIterations` for writing modern code with lazy evaluation.
- Refactor for of loops to named abstractions to clearly express their
  intentions without needing to analyse the loop itself.
- Add missing cases for changes that had no tests.
This commit is contained in:
undergroundwires
2022-01-04 21:45:22 +01:00
parent bd23faa28f
commit 31f70913a2
35 changed files with 342 additions and 343 deletions

View File

@@ -56,14 +56,13 @@ function compileExpressions(
function extractRequiredParameterNames(
expressions: readonly IExpression[],
): string[] {
const usedParameterNames = expressions
return expressions
.map((e) => e.parameters.all
.filter((p) => !p.isOptional)
.map((p) => p.name))
.filter((p) => p)
.flat();
const uniqueParameterNames = Array.from(new Set(usedParameterNames));
return uniqueParameterNames;
.filter(Boolean) // Remove empty or undefined
.flat()
.filter((name, index, array) => array.indexOf(name) === index); // Remove duplicates
}
function ensureParamsUsedInCodeHasArgsProvided(

View File

@@ -16,13 +16,8 @@ export class CompositeExpressionParser implements IExpressionParser {
}
public findExpressions(code: string): IExpression[] {
const expressions = new Array<IExpression>();
for (const parser of this.leafs) {
const newExpressions = parser.findExpressions(code);
if (newExpressions && newExpressions.length) {
expressions.push(...newExpressions);
}
}
return expressions;
return this.leafs.flatMap(
(parser) => parser.findExpressions(code) || [],
);
}
}

View File

@@ -18,35 +18,42 @@ export abstract class RegexParser implements IExpressionParser {
if (!code) {
throw new Error('undefined code');
}
const matches = Array.from(code.matchAll(this.regex));
const matches = code.matchAll(this.regex);
for (const match of matches) {
const startPos = match.index;
const endPos = startPos + match[0].length;
let position: ExpressionPosition;
try {
position = new ExpressionPosition(startPos, endPos);
} catch (error) {
throw new Error(`[${this.constructor.name}] invalid script position: ${error.message}\nRegex ${this.regex}\nCode: ${code}`);
}
const primitiveExpression = this.buildExpression(match);
const parameters = getParameters(primitiveExpression);
const position = this.doOrRethrow(() => createPosition(match), 'invalid script position', code);
const parameters = createParameters(primitiveExpression);
const expression = new Expression(position, primitiveExpression.evaluator, parameters);
yield expression;
}
}
private doOrRethrow<T>(action: () => T, errorText: string, code: string): T {
try {
return action();
} catch (error) {
throw new Error(`[${this.constructor.name}] ${errorText}: ${error.message}\nRegex: ${this.regex}\nCode: ${code}`);
}
}
}
function createPosition(match: RegExpMatchArray): ExpressionPosition {
const startPos = match.index;
const endPos = startPos + match[0].length;
return new ExpressionPosition(startPos, endPos);
}
function createParameters(
expression: IPrimitiveExpression,
): FunctionParameterCollection {
return (expression.parameters || [])
.reduce((parameters, parameter) => {
parameters.addParameter(parameter);
return parameters;
}, new FunctionParameterCollection());
}
export interface IPrimitiveExpression {
evaluator: ExpressionEvaluator;
parameters?: readonly IFunctionParameter[];
}
function getParameters(
expression: IPrimitiveExpression,
): FunctionParameterCollection {
const parameters = new FunctionParameterCollection();
for (const parameter of expression.parameters || []) {
parameters.addParameter(parameter);
}
return parameters;
}

View File

@@ -8,11 +8,9 @@ export class PipelineCompiler implements IPipelineCompiler {
ensureValidArguments(value, pipeline);
const pipeNames = extractPipeNames(pipeline);
const pipes = pipeNames.map((pipeName) => this.factory.get(pipeName));
let valueInCompilation = value;
for (const pipe of pipes) {
valueInCompilation = pipe.apply(valueInCompilation);
}
return valueInCompilation;
return pipes.reduce((previousValue, pipe) => {
return pipe.apply(previousValue);
}, value);
}
}