allow functions to call other functions #53

This commit is contained in:
undergroundwires
2021-01-16 13:26:41 +01:00
parent f1abd7682f
commit 7661575573
38 changed files with 1507 additions and 645 deletions

View File

@@ -0,0 +1,34 @@
import { IExpressionsCompiler, ParameterValueDictionary } from './IExpressionsCompiler';
import { generateIlCode, IILCode } from './ILCode';
export class ExpressionsCompiler implements IExpressionsCompiler {
public static readonly instance: IExpressionsCompiler = new ExpressionsCompiler();
protected constructor() { }
public compileExpressions(code: string, parameters?: ParameterValueDictionary): string {
let intermediateCode = generateIlCode(code);
intermediateCode = substituteParameters(intermediateCode, parameters);
return intermediateCode.compile();
}
}
function substituteParameters(intermediateCode: IILCode, parameters: ParameterValueDictionary): IILCode {
const parameterNames = intermediateCode.getUniqueParameterNames();
ensureValuesProvided(parameterNames, parameters);
for (const parameterName of parameterNames) {
const parameterValue = parameters[parameterName];
intermediateCode = intermediateCode.substituteParameter(parameterName, parameterValue);
}
return intermediateCode;
}
function ensureValuesProvided(names: string[], nameValues: ParameterValueDictionary) {
nameValues = nameValues || {};
const notProvidedNames = names.filter((name) => !Boolean(nameValues[name]));
if (notProvidedNames.length) {
throw new Error(`parameter value(s) not provided for: ${printList(notProvidedNames)}`);
}
}
function printList(list: readonly string[]): string {
return `"${list.join('", "')}"`;
}

View File

@@ -0,0 +1,5 @@
export interface ParameterValueDictionary { [parameterName: string]: string; }
export interface IExpressionsCompiler {
compileExpressions(code: string, parameters?: ParameterValueDictionary): string;
}

View File

@@ -0,0 +1,73 @@
export interface IILCode {
compile(): string;
getUniqueParameterNames(): string[];
substituteParameter(parameterName: string, parameterValue: string): IILCode;
}
export function generateIlCode(rawText: string): IILCode {
const ilCode = generateIl(rawText);
return new ILCode(ilCode);
}
class ILCode implements IILCode {
private readonly ilCode: string;
constructor(ilCode: string) {
this.ilCode = ilCode;
}
public substituteParameter(parameterName: string, parameterValue: string): IILCode {
const newCode = substituteParameter(this.ilCode, parameterName, parameterValue);
return new ILCode(newCode);
}
public getUniqueParameterNames(): string[] {
return getUniqueParameterNames(this.ilCode);
}
public compile(): string {
ensureNoExpressionLeft(this.ilCode);
return this.ilCode;
}
}
// Trim each expression and put them inside "{{exp|}}" e.g. "{{ $hello }}" becomes "{{exp|$hello}}"
function generateIl(rawText: string): string {
return rawText.replace(/\{\{([\s]*[^;\s\{]+[\s]*)\}\}/g, (_, match) => {
return `\{\{exp|${match.trim()}\}\}`;
});
}
// finds all "{{exp|..}} left"
function ensureNoExpressionLeft(ilCode: string) {
const allSubstitutions = ilCode.matchAll(/\{\{exp\|(.*?)\}\}/g);
const allMatches = Array.from(allSubstitutions, (match) => match[1]);
const uniqueExpressions = getDistinctValues(allMatches);
if (uniqueExpressions.length > 0) {
throw new Error(`unknown expression: ${printList(uniqueExpressions)}`);
}
}
// Parses all distinct usages of {{exp|$parameterName}}
function getUniqueParameterNames(ilCode: string) {
const allSubstitutions = ilCode.matchAll(/\{\{exp\|\$([^;\s\{]+[\s]*)\}\}/g);
const allParameters = Array.from(allSubstitutions, (match) => match[1]);
const uniqueParameterNames = getDistinctValues(allParameters);
return uniqueParameterNames;
}
// substitutes {{exp|$parameterName}} to value of the parameter
function substituteParameter(ilCode: string, parameterName: string, parameterValue: string) {
const pattern = `{{exp|$${parameterName}}}`;
return ilCode.split(pattern).join(parameterValue); // as .replaceAll() is not yet supported by TS
}
function getDistinctValues(values: readonly string[]): string[] {
return values.filter((value, index, self) => {
return self.indexOf(value) === index;
});
}
function printList(list: readonly string[]): string {
return `"${list.join('","')}"`;
}