allow functions to call other functions #53
This commit is contained in:
@@ -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('", "')}"`;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ParameterValueDictionary { [parameterName: string]: string; }
|
||||
|
||||
export interface IExpressionsCompiler {
|
||||
compileExpressions(code: string, parameters?: ParameterValueDictionary): string;
|
||||
}
|
||||
73
src/application/Parser/Script/Compiler/Expressions/ILCode.ts
Normal file
73
src/application/Parser/Script/Compiler/Expressions/ILCode.ts
Normal 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('","')}"`;
|
||||
}
|
||||
Reference in New Issue
Block a user