@yantrix/codegen
Version:
Yantrix framework code generator API
1,387 lines (1,313 loc) • 100 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ModuleNames: () => ModuleNames,
Modules: () => Modules,
TAssignTypeDict: () => TAssignTypeDict,
generateAutomataFromStateDiagram: () => generateAutomataFromStateDiagram
});
module.exports = __toCommonJS(src_exports);
var import_yantrix_parser10 = require("@yantrix/yantrix-parser");
// src/core/InjectFunctionsProcess.ts
var import_node_path = __toESM(require("path"), 1);
// src/core/modules/Java.ts
var import_automata = require("@yantrix/automata");
var import_mermaid_parser = require("@yantrix/mermaid-parser");
// src/core/shared.ts
var import_yantrix_parser = require("@yantrix/yantrix-parser");
// src/constants.ts
var ByPassAction = "[-]";
var DEFAULT_USER_FUNCTIONS_NAMESPACE = "userFunctions";
// src/core/shared.ts
function fillDictionaries(diagram, stateDictionary, actionDictionary, eventDictionary) {
if (stateDictionary) {
const stateKeys = diagram.states.map((s) => s.id);
stateDictionary.addStates({ keys: stateKeys });
}
if (actionDictionary) {
for (const state2 of diagram.states) {
for (const path2 of state2.actionsPath.map((p) => p.action)) {
const firstAction = path2[0];
const isUniqueAction = actionDictionary.getActionValues({ keys: [firstAction] })[0] === null;
if (!isUniqueAction) {
continue;
}
actionDictionary.addActions({ keys: [firstAction] });
}
}
}
if (eventDictionary) {
for (const state2 of diagram.states) {
const emittedEventsKeys = state2.notes?.emit.map((event) => event.identifier);
if (emittedEventsKeys && emittedEventsKeys.length > 0) {
const uniqueKeys = emittedEventsKeys.filter((e) => eventDictionary.getEventValues({ keys: [e] })[0] == null);
eventDictionary.addEvents({ keys: uniqueKeys });
}
const subscribedEventsKeys = state2.notes?.subscribe.map((event) => event.identifier);
if (subscribedEventsKeys && subscribedEventsKeys.length > 0) {
const uniqueKeys = subscribedEventsKeys.filter((e) => eventDictionary.getEventValues({ keys: [e] })[0] == null);
eventDictionary.addEvents({ keys: uniqueKeys });
}
}
}
}
__name(fillDictionaries, "fillDictionaries");
function getStatesByPass(diagram, stateDictionary) {
const byPassed = [];
diagram.states.forEach((state2) => {
if (state2.notes?.byPass) {
const actionChains = diagram.actionChains[state2.id];
if (!actionChains) throw new Error(`ByPassed state ${state2.id} doesn't have transition`);
const byPassAction = actionChains[ByPassAction];
if (!byPassAction) {
throw new Error(`ByPass action ${ByPassAction} not found for state ${state2.id}`);
}
byPassAction.chains.forEach(({ chain }) => {
if (byPassAction.chains.length > 1 && chain.length === 0) {
throw new Error(`ByPass action ${ByPassAction} should have more than one transition`);
}
});
const value = stateDictionary.getStateValues({ keys: [state2.id] })[0];
if (!value) throw new Error(`State ${state2.id} not found`);
byPassed.push(value);
}
});
return byPassed;
}
__name(getStatesByPass, "getStatesByPass");
var pathRecord = {
[import_yantrix_parser.ExpressionTypes.Constant]: "constant",
[import_yantrix_parser.ExpressionTypes.Context]: "prevContext",
[import_yantrix_parser.ExpressionTypes.Payload]: "payload"
};
// src/core/modules/Java.ts
var JavaCodegen = class {
static {
__name(this, "JavaCodegen");
}
stateDictionary;
actionDictionary;
diagram;
handlersDict;
initialContext;
initialContextKeys;
changeStateHandlers;
dictionaries;
imports = {
"@yantrix/automata": ["GenericAutomata"]
};
package = "org.example";
// base package name for all automata files
constructor({ diagram }) {
this.actionDictionary = new import_automata.BasicActionDictionary();
this.stateDictionary = new import_automata.BasicStateDictionary();
this.diagram = diagram;
this.handlersDict = [];
this.changeStateHandlers = [];
this.dictionaries = [];
this.initialContextKeys = [];
this.initialContext = this.getInitialContext();
fillDictionaries(diagram, this.stateDictionary, this.actionDictionary);
this.setupDictionaries();
}
getCode(options) {
return `
${this.getImports()}
${this.getClassTemplate(options.className)}
`;
}
// Package declaration and imports necessary for the automata to function
getImports() {
const lines = [
`package ${this.package};`,
`import java.util.Map;`,
`import java.util.Objects;`,
`import java.util.HashMap;`,
`import java.util.function.Function;`
];
return lines.join("\n");
}
getDictionaries() {
return this.dictionaries.join("\n");
}
getActionToStateFromState() {
return `public final Map<TAutomataBaseState, Map<TAutomataBaseAction, AutomataStateTransitionResult>> stateTransitionMatrix =
Map.ofEntries(
${this.getStateTransitionMatrix()}
);
`;
}
// State transition matrix
getStateTransitionMatrix() {
return Object.entries(this.diagram.transitions).map(([state2, transitions]) => {
const value = this.stateDictionary.getStateValues({ keys: [state2] })[0];
if (!value)
throw new Error(`State ${state2} not found`);
return `
Map.entry(
TAutomataBaseState.of(${value}L),
Map.of(
${this.getTransitions(transitions).join(",\n")}
)
)
`;
}).join(",\n");
}
getTransitions(transitions) {
return Object.entries(transitions).map(([state2, transition]) => {
const newState = this.stateDictionary.getStateValues({ keys: [state2] })[0];
return transition.actionsPath.map(({ action }) => {
const actionValue = this.actionDictionary.getActionValues({
keys: action
})[0];
if (!actionValue)
throw new Error(`Action ${action} not found`);
if (!newState)
throw new Error(`State ${state2} not found`);
return `
TAutomataBaseAction.of(${actionValue}L),
new AutomataStateTransitionResult(
TAutomataBaseState.of(${newState}L),
(payloadContext) -> {
var prevContext = getDefaultContext(payloadContext);
return prevContext;
}
)
`;
});
}).flatMap((el) => `${el.join(",\n ")}`);
}
getDefaultContext() {
return `
private TAutomataBaseContext getDefaultContext(AutomataPayloadContext arg) {
TAutomataBaseContext prevContext = arg.context();
return prevContext;
}
`;
}
// Full class declaration with all dictionaries and handlers inside
getClassTemplate(className) {
return `
public final class ${className} {
${this.getDictionaries()}
${this.getActionToStateFromState()}
${this.setupClassMembers()}
${this.getDefaultConstructor(className)}
${this.setupClassMembersAccessors()}
${this.getDefaultContext()}
${this.dispatchMethod()}
${this.toStringMethod()}
${this.getTypes()}
}
`;
}
// Default constructor for the class
getDefaultConstructor(className) {
return `
public ${className}() {
this.state = TAutomataBaseState.of(${this.getInitialState()}L);
this.context = ${this.getInitialContext()};
this.rootReducer = ${this.getRootReducer()};
}
`;
}
// Transforms codegen dictionaries into language-specific text representations
setupDictionaries() {
this.dictionaries.push(`
public static final Map<String, TAutomataBaseState> statesDictionary = Map.of(
${Object.entries(this.stateDictionary.getDictionary()).map(([key, value]) => `"${key}", TAutomataBaseState.of(${value}L)`).join(",\n")}
);
`);
this.dictionaries.push(`
public static final Map<String, TAutomataBaseAction> actionsDictionary = Map.of(
${Object.entries(this.actionDictionary.getDictionary()).map(([key, value]) => `"${key}", TAutomataBaseAction.of(${value}L)`).join(",\n")}
);
`);
}
// Initial context is empty
getInitialContext() {
return "new TAutomataBaseContext()";
}
// Fetches first state from list
getInitialState() {
return this.stateDictionary.getStateValues({ keys: [import_mermaid_parser.StartState] })[0];
}
// Root reducer function
getRootReducer() {
return `
(obj) -> {
if(obj.action == null || obj.payload == null) {
return TAutomataStateContext.of(obj.state, obj.context);
}
${this.getRootReducerStateValidation()}
${this.getRootReducerActionValidation()}
AutomataStateTransitionResult res = stateTransitionMatrix.get(obj.state).get(obj.action);
return TAutomataStateContext.of(
res.newState(),
res.getNewContext().apply(new AutomataPayloadContext(obj.payload, obj.context))
);
}
`;
}
// Checks if state can be found in dictionary for the automata
getRootReducerStateValidation() {
return `
// state validation
if(!stateTransitionMatrix.containsKey(obj.state)) {
throw new RuntimeException("Invalid state, maybe machine isn't running");
}
`;
}
// Checks if action can be found in dictionary for the automata & if the action is appropriate for the automata state
getRootReducerActionValidation() {
return `
// action validation
if(!stateTransitionMatrix.get(obj.state).containsKey(obj.action)) {
return TAutomataStateContext.of(
obj.state,
obj.context
);
}
`;
}
setupClassMembers() {
return `
private TAutomataBaseState state;
private TAutomataBaseContext context;
private IAutomataReducer rootReducer;
`;
}
setupClassMembersAccessors() {
return `
public Map getStateTransitionMatrix() { return this.stateTransitionMatrix; }
public TAutomataStateContext getContext() { return new TAutomataStateContext(this.state, this.context); }
public IAutomataReducer getReducer() { return this.rootReducer; }
public void setContext(TAutomataStateContext context) {
this.state = context.state;
this.context = context.context;
}
`;
}
dispatchMethod() {
return `
public TAutomataStateContext dispatch(TAutomataActionPayload action) {
TAutomataStateContext reducedValue =
this.rootReducer.apply(new TAutomataStateContextActionPayload(this.state, this.context, action.action, action.payload));
this.setContext(reducedValue);
return reducedValue;
}
`;
}
toStringMethod() {
return `
@Override
public String toString() {
return "GeneratedAutomata{" +
"statesDictionary=" + statesDictionary +
", actionsDictionary=" + actionsDictionary +
", stateTransitionMatrix=" + stateTransitionMatrix +
'}';
}
`;
}
// Types necessary for the automata
getTypes() {
return `
public abstract static class TAutomataBaseType {
protected Long value;
public Long getValue() { return value; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TAutomataBaseType that = (TAutomataBaseType) o;
return Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
public static class TAutomataBaseState extends TAutomataBaseType {
private TAutomataBaseState() {}
private TAutomataBaseState(Long value) { this.value = value; }
public static TAutomataBaseState of(Long value) { return new TAutomataBaseState(value); }
}
public static class TAutomataBaseAction extends TAutomataBaseType {
private TAutomataBaseAction() {}
private TAutomataBaseAction(Long value) { this.value = value; }
public static TAutomataBaseAction of(Long value) { return new TAutomataBaseAction(value); }
}
public static class TAutomataBaseContext extends HashMap<String, Object> {
public TAutomataBaseContext(Map<String, Object> map) { super(map); }
private TAutomataBaseContext(TAutomataBasePayload payload) {
super();
this.putAll(payload);
}
public static TAutomataBaseContext fromPayload(TAutomataBasePayload payload) {
return new TAutomataBaseContext(payload);
}
}
public static class TAutomataBasePayload extends HashMap<String, Object> {
}
public record AutomataStateTransitionResult (
TAutomataBaseState newState,
Function<AutomataPayloadContext, TAutomataBaseContext> getNewContext
) {
}
public interface IAutomataReducer extends Function<TAutomataStateContextActionPayload, TAutomataStateContext> {}
public record TAutomataStateContext(TAutomataBaseState state, TAutomataBaseContext context) {}
public record TAutomataStateContextActionPayload(TAutomataBaseState state, TAutomataBaseContext context, TAutomataBaseAction action, TAutomataBasePayload payload) {}
public record AutomataPayloadContext(TAutomataBasePayload payload,TAutomataBaseContext context) {}
public record TAutomataActionPayload(TAutomataBaseAction action, TAutomataBasePayload payload) {}
`;
}
};
// src/core/modules/JavaScript/codegen.ts
var import_automata2 = require("@yantrix/automata");
// src/core/modules/JavaScript/JavaScriptCompiler/class/serializer.ts
var import_mermaid_parser3 = require("@yantrix/mermaid-parser");
// src/core/templates/JavaScript.ts
var JavaScriptTemplate = `
export class %CLASSNAME% extends GenericAutomata {
static id = %ID%;
static actions = %ACTIONS_MAP%;
static states = %STATES_MAP%;
static getState = %GET_STATE%;
static hasState = %HAS_STATE%;
static getAction = %GET_ACTION%;
static createAction = %CREATE_ACTION%;
constructor() {
super(eventAdapter);
this.init({
state: %STATE%,
context:%CONTEXT%,
rootReducer: %REDUCER%,
stateValidator: %STATE_VALIDATOR%,
actionValidator: %ACTION_VALIDATOR%,
functionRegistry: %FUNCTION_REGISTRY%
});
}
isKeyOf = %IS_KEY_OF%;
}
export default %CLASSNAME%;`;
// src/utils/utils.ts
function replaceFileContents(replacementMap) {
let res = JavaScriptTemplate;
Object.entries(replacementMap).forEach(([template, str]) => {
res = res.replaceAll(template, str);
});
return res;
}
__name(replaceFileContents, "replaceFileContents");
// src/core/modules/JavaScript/JavaScriptCompiler/context/core.ts
function getInitialContextShape(props) {
const states = props.diagram.states.filter((state2) => state2.id === props.stateName);
if (states.length) {
return states.reduce(
(acc, curr) => {
curr.notes?.contextDescription.forEach((el) => {
el.context.forEach((el2) => {
acc[el2.keyItem.identifier] = null;
});
});
return acc;
},
{}
);
}
return null;
}
__name(getInitialContextShape, "getInitialContextShape");
// src/core/modules/JavaScript/JavaScriptCompiler/context/serializer.ts
var import_mermaid_parser2 = require("@yantrix/mermaid-parser");
var import_yantrix_parser4 = require("@yantrix/yantrix-parser");
// src/core/modules/JavaScript/JavaScriptCompiler/expressions/core.ts
var import_yantrix_parser3 = require("@yantrix/yantrix-parser");
// src/core/modules/JavaScript/JavaScriptCompiler/expressions/functions.ts
var import_functions = require("@yantrix/functions");
var import_yantrix_parser2 = require("@yantrix/yantrix-parser");
function getFunctionBody(props) {
if (props.expression.expressionType === import_yantrix_parser2.ExpressionTypes.Function) {
const { FunctionName, Arguments } = props.expression.FunctionDeclaration;
if (import_functions.ReservedInternalFunctionNames.includes(FunctionName)) {
return `(function() {
const func = functionDictionary.get('${FunctionName}');
return func(automata);
})()`;
}
const argsList = Arguments.map((arg) => {
if (arg.expressionType === import_yantrix_parser2.ExpressionTypes.Function) {
return getFunctionBody({
expression: arg,
expressions: props.expressions
});
} else {
return getExpressionValueDefine({
expression: arg,
expressions: props.expressions
});
}
}).join(", ");
return `(function() {
const func = functionDictionary.get('${FunctionName}');
return func(${argsList});
})()`;
} else {
return getExpressionValueDefine({
expression: props.expression,
expressions: props.expressions
});
}
}
__name(getFunctionBody, "getFunctionBody");
// src/core/modules/JavaScript/JavaScriptCompiler/expressions/serializer.ts
function getDefaultPropertyContext(path2, indetifier, expression) {
const fullPath = getReferenceString(path2, indetifier);
return `(function(){
if(${path2} !== null && ${fullPath} !== undefined && ${fullPath} !== null) {
return ${path2}['${indetifier}']
}
else {
return ${expression ?? "null"}
}
}())`;
}
__name(getDefaultPropertyContext, "getDefaultPropertyContext");
function getReferenceString(path2, identifier) {
return `${path2}['${identifier}']`;
}
__name(getReferenceString, "getReferenceString");
function getFunctionFromDictionary(name) {
return `functionDictionary.get('${name}')`;
}
__name(getFunctionFromDictionary, "getFunctionFromDictionary");
var expressionsSerializer = {
getDefaultPropertyContext,
getFunctionFromDictionary
};
// src/core/modules/JavaScript/JavaScriptCompiler/expressions/core.ts
function setupExpressions(props) {
const expressionRecord = {
[import_yantrix_parser3.ExpressionTypes.ArrayDeclaration]: () => "[]",
[import_yantrix_parser3.ExpressionTypes.Constant]: ({ identifier }) => {
if (props.constants === null) {
throw new Error("Missing dictionary with constants");
}
if (props.constants[identifier] === void 0) {
throw new Error(
`The identifier is missing in the const dictionary: ${identifier}`
);
}
if (typeof props.constants[identifier] === "string")
return `"${props.constants[identifier]}"`;
return `${props.constants[identifier]}`;
},
[import_yantrix_parser3.ExpressionTypes.Function]: () => {
return "null";
},
[import_yantrix_parser3.ExpressionTypes.DecimalDeclaration]: ({ NumberDeclaration }) => {
return `${NumberDeclaration}`;
},
[import_yantrix_parser3.ExpressionTypes.IntegerDeclaration]: ({ NumberDeclaration }) => {
return `${NumberDeclaration}`;
},
[import_yantrix_parser3.ExpressionTypes.StringDeclaration]: ({ StringDeclaration }) => {
return `'${StringDeclaration}'`;
},
[import_yantrix_parser3.ExpressionTypes.Context]: ({ identifier }) => {
return `prevContext === null || (prevContext === undefined || prevContext['${identifier}'] === undefined) ? null : prevContext['${identifier}']`;
},
[import_yantrix_parser3.ExpressionTypes.Payload]: ({ identifier }) => {
return `payload === null || (payload === undefined || payload['${identifier}'] === undefined) ? null : payload['${identifier}']`;
}
};
expressionRecord[import_yantrix_parser3.ExpressionTypes.Function] = (func) => {
let currentRecLevel = 0;
const recursive = /* @__PURE__ */ __name((func2) => {
const { FunctionDeclaration } = func2;
const { FunctionName, Arguments } = FunctionDeclaration;
const res2 = [];
if (currentRecLevel < import_yantrix_parser3.maxNestedFuncLevel) {
Arguments.forEach((item) => {
if ((0, import_yantrix_parser3.isKeyItemReference)(item)) {
const { expressionType, identifier } = item;
const path2 = pathRecord[expressionType];
if ((0, import_yantrix_parser3.isKeyItemWithExpression)(item)) {
const { expression } = item;
if (expression.expressionType === import_yantrix_parser3.ExpressionTypes.Function) {
currentRecLevel++;
res2.push(recursive(expression));
}
const valueExpression = getExpressionValue({
expressionRecord,
expression
});
res2.push(
`${expressionsSerializer.getDefaultPropertyContext(path2, identifier, valueExpression)}`
);
} else {
if (item.expressionType === import_yantrix_parser3.ExpressionTypes.Constant) {
const expressionValueRight = getExpressionValue({
expressionRecord,
expression: item
});
res2.push(expressionValueRight);
} else {
res2.push(
`${expressionsSerializer.getDefaultPropertyContext(path2, identifier)}`
);
}
}
} else {
if (item.expressionType === import_yantrix_parser3.ExpressionTypes.Function) {
res2.push(recursive(item));
} else {
const valueExpression = getExpressionValue({
expressionRecord,
expression: item
});
res2.push(valueExpression);
}
}
});
} else {
throw new Error(
`Max level of nested functions reached ${import_yantrix_parser3.maxNestedFuncLevel}`
);
}
return expressionsSerializer.getFunctionFromDictionary(FunctionName).concat(
`(${res2.join(",")})`
);
}, "recursive");
const res = recursive(func);
return res;
};
return expressionRecord;
}
__name(setupExpressions, "setupExpressions");
function getExpressionValue(props) {
return props.expressionRecord[props.expression.expressionType](props.expression);
}
__name(getExpressionValue, "getExpressionValue");
function getExpressionValueDefine(props) {
switch (props.expression.expressionType) {
case import_yantrix_parser3.ExpressionTypes.Identifier:
return props.expression.identifier;
case import_yantrix_parser3.ExpressionTypes.Function:
return getFunctionBody({
expression: props.expression,
expressions: props.expressions
});
default:
return getExpressionValue({
expressionRecord: props.expressions,
expression: props.expression
});
}
}
__name(getExpressionValueDefine, "getExpressionValueDefine");
// src/core/modules/JavaScript/JavaScriptCompiler/expressions/index.ts
var expressions = {
serializer: expressionsSerializer,
functions: {
getExpressionValue,
setupExpressions
}
};
// src/core/modules/JavaScript/JavaScriptCompiler/context/serializer.ts
function getStateReducerCode(props) {
return `const reducer = {
${getStateToContext(props).join(",\n ")}
}`;
}
__name(getStateReducerCode, "getStateReducerCode");
function getContextItem(props) {
if ((0, import_yantrix_parser4.isContextWithReducer)(props.ctx)) {
const { context: context2, reducer } = props.ctx;
return getBoundValues({
expressions: props.expressions,
arr: mapReducerItems({ reducer, expressions: props.expressions }),
context: context2
});
} else {
const { context: context2 } = props.ctx;
return context2.map(({ keyItem }) => {
const { identifier } = keyItem;
if ((0, import_yantrix_parser4.isKeyItemWithExpression)(keyItem)) {
const expressionValue = expressions.functions.getExpressionValue({
expression: keyItem.expression,
expressionRecord: props.expressions
});
return `${identifier}: ${expressions.serializer.getDefaultPropertyContext("prevContext", identifier, expressionValue)}`;
} else {
return `${identifier}: ${expressions.serializer.getDefaultPropertyContext("prevContext", identifier)}`;
}
});
}
}
__name(getContextItem, "getContextItem");
function mapReducerItems(props) {
return props.reducer.map(({ keyItem }) => {
if ((0, import_yantrix_parser4.isKeyItemReference)(keyItem)) {
const { expressionType, identifier: boundIdentifier } = keyItem;
const path2 = props.sourcePath ?? pathRecord[expressionType];
if (keyItem.expressionType === import_yantrix_parser4.ExpressionTypes.Constant) {
const expressionValueRight = expressions.functions.getExpressionValue({
expression: keyItem,
expressionRecord: props.expressions
});
return `(function(){
return ${expressionValueRight}
}())`;
}
if ((0, import_yantrix_parser4.isKeyItemWithExpression)(keyItem)) {
const { expression } = keyItem;
const expressionValueRight = expressions.functions.getExpressionValue({
expression,
expressionRecord: props.expressions
});
return expressions.serializer.getDefaultPropertyContext(path2, boundIdentifier, expressionValueRight);
}
return expressions.serializer.getDefaultPropertyContext(path2, boundIdentifier);
} else {
const { expression } = keyItem;
const expressionValueRight = expressions.functions.getExpressionValue({
expression,
expressionRecord: props.expressions
});
return `(function(){
return ${expressionValueRight}
}())`;
}
});
}
__name(mapReducerItems, "mapReducerItems");
function getBoundValues(props) {
return props.arr.map((el, index) => {
const item = props.context[index];
if (!item) {
throw new Error("Unexpected index bound property");
}
const { keyItem } = item;
const { identifier: targetProperty } = keyItem;
if ((0, import_yantrix_parser4.isKeyItemWithExpression)(keyItem)) {
const { expression } = keyItem;
const expressionValueRight = expressions.functions.getExpressionValue({
expression,
expressionRecord: props.expressions
});
return `${targetProperty}: (function(){
const boundValue = ${el}
if(boundValue !== null){
return boundValue
}
else {
return ${expressionValueRight}
}
}())`;
} else {
return `${targetProperty}: (function(){
const boundValue = ${el}
return boundValue
}())`;
}
});
}
__name(getBoundValues, "getBoundValues");
function getContextTransition(props) {
const stateFromDict = props.stateDictionary.getStateKeys({ states: [props.value] })[0];
if (stateFromDict === null) {
throw new Error(`Invalid state - ${props.value}`);
}
const diagramState = props.diagram.states.find((diagramState2) => {
return diagramState2.id === stateFromDict;
});
if (!diagramState) {
throw new Error(`Invalid state - ${props.value}`);
}
const ctxRes = [];
diagramState.notes?.contextDescription.forEach((ctx) => {
const newContext = getContextItem({
ctx,
expressions: props.expressions
});
ctxRes.push(...newContext);
});
if (ctxRes.length === 0) return "prevContext";
return `{${ctxRes.join(",\n ")}}`;
}
__name(getContextTransition, "getContextTransition");
function getStateToContext(props) {
return props.diagram.states.map((state2) => {
const stateValue = props.stateDictionary.getStateValues({ keys: [state2.id] })[0];
if (!stateValue) {
throw new Error("Invalid state");
}
return `${stateValue}: (prevContext, payload, functionDictionary, automata) => {
return ${getContextTransition({
value: stateValue,
stateDictionary: props.stateDictionary,
diagram: props.diagram,
expressions: props.expressions
})}
}`;
});
}
__name(getStateToContext, "getStateToContext");
function getDefaultContext(props) {
const state2 = props.stateDictionary.getStateValues({ keys: [import_mermaid_parser2.StartState] })[0];
if (state2) {
const ctx = getContextTransition({
diagram: props.diagram,
expressions: props.expressions,
stateDictionary: props.stateDictionary,
value: state2
});
return `const getDefaultContext = (prevContext, payload) => {
const ctx = ${ctx}
return Object.assign({}, prevContext, ctx);
}
`;
}
return `const getDefaultContext = (prevContext, payload) => {
return prevContext
}`;
}
__name(getDefaultContext, "getDefaultContext");
var contextSerializer = {
getStateReducerCode,
getContextItem,
mapReducerItems,
getBoundValues,
getStateToContext,
getDefaultContext
};
// src/core/modules/JavaScript/JavaScriptCompiler/context/index.ts
var context = {
serializer: contextSerializer,
functions: {
getInitialContextShape
}
};
// src/core/modules/JavaScript/JavaScriptCompiler/state/core.ts
function getInitialState(props) {
const hasInitial = props.diagram.states.find((state2) => {
return Boolean(state2.notes?.initialState);
});
if (hasInitial) {
return hasInitial.id;
}
const firstState = props.diagram.states[0]?.id;
if (!firstState) {
throw new Error("Invalid state");
}
return firstState;
}
__name(getInitialState, "getInitialState");
// src/core/modules/JavaScript/JavaScriptCompiler/state/index.ts
var state = {
functions: {
getInitialState
}
};
// src/core/modules/JavaScript/JavaScriptCompiler/class/serializer.ts
function getClassTemplate(props) {
const { diagram } = props;
const initialState = state.functions.getInitialState({
diagram
});
const stateValue = props.stateDictionary.getStateValues({ keys: [initialState] })[0];
if (stateValue === null) {
throw new Error("GetClassTemplate: Invalid state");
}
const a = context.functions.getInitialContextShape({
diagram,
stateName: import_mermaid_parser3.StartState
});
const b = context.functions.getInitialContextShape({
diagram,
stateName: initialState
});
const initialContext = Object.assign({}, a, b);
return replaceFileContents(
{
"%CLASSNAME%": props.className,
"%ID%": `'${props.className}_${Date.now()}'`,
"%ACTIONS_MAP%": "actionsMap",
"%STATES_MAP%": "statesMap",
"%GET_STATE%": props.classSerializer.getGetStateFunc().toString(),
"%HAS_STATE%": props.classSerializer.getHasStateFunc({ className: props.className }).toString(),
"%GET_ACTION%": props.classSerializer.getGetActionFunc().toString(),
"%CREATE_ACTION%": props.classSerializer.getCreateActionFunc({ className: props.className }).toString(),
"%STATE%": (stateValue ?? -1).toString(),
"%CONTEXT%": JSON.stringify(initialContext),
"%REDUCER%": props.classSerializer.getRootReducer().toString(),
"%STATE_VALIDATOR%": props.classSerializer.getStateValidator().toString(),
"%ACTION_VALIDATOR%": props.classSerializer.getActionValidator().toString(),
"%FUNCTION_REGISTRY%": "functionDictionary",
"%EVENT_DICTIONARY%": "GlobalEventDictionary",
"%IS_KEY_OF%": props.classSerializer.getIsKeyOf().toString()
}
);
}
__name(getClassTemplate, "getClassTemplate");
function getHasStateFunc(props) {
return `(instance, state) => instance.state === ${props.className}.getState(state)`;
}
__name(getHasStateFunc, "getHasStateFunc");
function getGetStateFunc() {
return `(state) => statesDictionary[state]`;
}
__name(getGetStateFunc, "getGetStateFunc");
function getGetActionFunc() {
return `(action) => actionsDictionary[action];`;
}
__name(getGetActionFunc, "getGetActionFunc");
function getCreateActionFunc(props) {
return `
(action, payload) => {
const actionId = ${props.className}.getAction(action);
return {
action: actionId,
payload,
}
}`;
}
__name(getCreateActionFunc, "getCreateActionFunc");
function getActionValidator() {
return `(a) => Object.values(actionsDictionary).includes(a)`;
}
__name(getActionValidator, "getActionValidator");
function getRootReducer() {
return `({ action, context, payload, state }) => {
if (!action || payload === null) return { state, context };
${getRootReducerStateValidation()}
${getRootReducerActionValidation()}
const getNew = (action,state,context,payload) => {
this.lastAction = action;
const actionMove = actionToStateFromStateDict[state][action];
const newStateObject = { state: actionMove.state[0] }
const contextWithInitial = getDefaultContext(context,payload)
${getRootReducerNewStatePredicateResolution()}
const newState = newStateObject.state;
const newContextFunc = reducer[newState]
if(typeof newContextFunc !== 'function') {
throw new Error('Invalid newContextFunc')
}
return {state:newState, context: newContextFunc(contextWithInitial, payload, this.getFunctionRegistry(), this)};
}
let localCtx = getNew(action,state,context,payload)
while(byPassedStates.has(localCtx.state)) {
localCtx = getNew(actionsDictionary['${ByPassAction}'], localCtx.state, localCtx.context, {})
}
this.incrementCycle(); // increment automata local cycle counter
incrementEpoch(); // increment global epoch counter
return localCtx
}`;
}
__name(getRootReducer, "getRootReducer");
function getRootReducerNewStatePredicateResolution() {
return `
if(actionMove.state.length > 1 && actionMove.predicate != null) {
// determine new state from predicate
const resolvedPredicateValue = actionMove.predicate(contextWithInitial, payload, functionDictionary);
if(resolvedPredicateValue == null) return { state, context };
newStateObject.state = resolvedPredicateValue;
}
`;
}
__name(getRootReducerNewStatePredicateResolution, "getRootReducerNewStatePredicateResolution");
function getRootReducerStateValidation() {
return `${getRootReducerStateValidationHead()} ${getRootReducerStateValidationError()}`;
}
__name(getRootReducerStateValidation, "getRootReducerStateValidation");
function getRootReducerStateValidationHead() {
return `if (!this.isKeyOf(state, actionToStateFromStateDict))`;
}
__name(getRootReducerStateValidationHead, "getRootReducerStateValidationHead");
function getRootReducerStateValidationError() {
return `throw new Error("Invalid state, maybe machine isn't running.")`;
}
__name(getRootReducerStateValidationError, "getRootReducerStateValidationError");
function getRootReducerActionValidation() {
return `if (!this.isKeyOf(action, actionToStateFromStateDict[state])) return { state, context };`;
}
__name(getRootReducerActionValidation, "getRootReducerActionValidation");
function getStateValidator() {
return `(s) => Object.values(statesDictionary).includes(s)`;
}
__name(getStateValidator, "getStateValidator");
function getIsKeyOf() {
return `(key, obj) => key in obj`;
}
__name(getIsKeyOf, "getIsKeyOf");
var classSerializer = {
getClassTemplate,
getStateValidator,
getHasStateFunc,
getGetStateFunc,
getGetActionFunc,
getCreateActionFunc,
getActionValidator,
getRootReducer,
getRootReducerStateValidation,
getRootReducerStateValidationHead,
getRootReducerStateValidationError,
getRootReducerActionValidation,
getIsKeyOf
};
// src/core/modules/JavaScript/JavaScriptCompiler/class/index.ts
var classModule = {
serializer: classSerializer
};
// src/core/modules/JavaScript/JavaScriptCompiler/functions/core.ts
var import_functions3 = require("@yantrix/functions");
var import_yantrix_parser5 = require("@yantrix/yantrix-parser");
function getFunctionBody2(props) {
if (props.expression.expressionType === import_yantrix_parser5.ExpressionTypes.Function) {
const { FunctionName, Arguments } = props.expression.FunctionDeclaration;
const argsList = Arguments.map((arg) => {
if (arg.expressionType === import_yantrix_parser5.ExpressionTypes.Function) {
return getFunctionBody2({
expression: arg,
expressions: props.expressions
});
} else {
return getExpressionValueDefine({
expression: arg,
expressions: props.expressions
});
}
}).join(", ");
return `(function() {
const func = functionDictionary.get('${FunctionName}');
return func(${argsList});
})()`;
} else {
return getExpressionValueDefine({
expression: props.expression,
expressions: props.expressions
});
}
}
__name(getFunctionBody2, "getFunctionBody");
function checkUserFunctionsDefined(props) {
const { injectedPath, injects } = props;
const identifiers = injects.map((inject) => `'${inject.identifier}'`);
if (!injectedPath) return ``;
return `
(function (){
const injects = [${identifiers.join(",")}]
const defaults = ${DEFAULT_USER_FUNCTIONS_NAMESPACE}?.default ?? null
if(defaults) {
switch(typeof defaults) {
case 'function':
if(!defaults?.name) throw new Error('Default exported user functions must have a name');
Object.assign(${DEFAULT_USER_FUNCTIONS_NAMESPACE}, {[defaults.name]: defaults});
break;
case 'object':
if(Array.isArray(defaults)) {
throw new Error('Default exported user functions must be an object or a function');
}
Object.assign(${DEFAULT_USER_FUNCTIONS_NAMESPACE}, {...defaults});
break;
default:
throw new Error('Default exported user functions must be a function or an object from ${injectedPath}');
}
}
injects.forEach((identifier) => {
if (!${DEFAULT_USER_FUNCTIONS_NAMESPACE}[identifier]) {
throw new Error(\`Function \${identifier} is not defined in ${injectedPath}\`);
}
if(typeof ${DEFAULT_USER_FUNCTIONS_NAMESPACE}[identifier] !== 'function') {
throw new Error(\`Function \${identifier} is not a function in ${injectedPath}\`);
}
});
})()`;
}
__name(checkUserFunctionsDefined, "checkUserFunctionsDefined");
function registerCustomFunctions(props) {
const { dictionaries: dictionaries2, diagram, injectFunctions, dependencyGraph, expressions: expressions2 } = props;
const newDictionary = dictionaries2;
const defines = diagram.states.flatMap((state2) => state2.notes?.defines ?? []);
const inject = diagram.states.flatMap((state2) => state2.notes?.inject ?? []);
const registered = /* @__PURE__ */ new Set();
const registerFunction = /* @__PURE__ */ __name((funcName, depth = 0) => {
const defineFunction = defines.find((def) => def.identifier === funcName);
const injectFunction = inject.find((def) => def.identifier === funcName);
const isBuiltInIdentifier = Object.hasOwn(import_functions3.builtInFunctions, funcName) && (Boolean(defineFunction) || Boolean(injectFunction));
if (registered.has(funcName)) {
throw new TypeError(`Function identifier ${funcName} is already used`);
}
;
if (isBuiltInIdentifier) {
throw new TypeError(`Function identifier ${funcName} should not be used as it is a built-in function`);
}
if (injectFunction && !injectFunctions.path) {
throw new TypeError(`Function identifier ${funcName} is defined in the diagram but no inject file is provided`);
}
const dependencies = dependencyGraph.get(funcName) || /* @__PURE__ */ new Set();
for (const dep of dependencies) {
if (!registered.has(dep)) {
registerFunction(dep, depth + 1);
}
}
if (injectFunction) {
newDictionary.push(`functionDictionary.register('${funcName}', ${DEFAULT_USER_FUNCTIONS_NAMESPACE}['${funcName}']);`);
registered.add(funcName);
} else if (defineFunction) {
const functionBody = getFunctionBody2({
expression: defineFunction.expression,
expressions: expressions2
});
newDictionary.push(`functionDictionary.register('${funcName}', function(${defineFunction.Arguments.join(", ")}) {
return ${functionBody};
});`);
registered.add(funcName);
}
;
}, "registerFunction");
for (const funcName of dependencyGraph.keys()) {
if (!registered.has(funcName)) {
registerFunction(funcName);
}
}
return newDictionary;
}
__name(registerCustomFunctions, "registerCustomFunctions");
// src/core/modules/JavaScript/JavaScriptCompiler/functions/index.ts
var functions = {
serializer: checkUserFunctionsDefined,
functions: {
registerCustomFunctions
}
};
// src/core/modules/JavaScript/JavaScriptCompiler/imports/core.ts
var import_yantrix_parser6 = require("@yantrix/yantrix-parser");
function buildDependencyGraph(props) {
const defines = props.diagram.states.flatMap((state2) => state2.notes?.defines ?? []);
const injects = props.diagram.states.flatMap((state2) => state2.notes?.inject ?? []);
const addDependencies = /* @__PURE__ */ __name((expression, currentFunc) => {
const { FunctionName, Arguments } = expression.FunctionDeclaration;
if (!props.dependencyGraph.has(currentFunc)) {
props.dependencyGraph.set(currentFunc, /* @__PURE__ */ new Set());
}
props.dependencyGraph.get(currentFunc).add(FunctionName);
for (const arg of Arguments) {
if (arg.expressionType === "function") {
addDependencies(arg, currentFunc);
}
}
}, "addDependencies");
injects.forEach((inject) => {
if (!props.dependencyGraph.has(inject.identifier)) {
props.dependencyGraph.set(inject.identifier, /* @__PURE__ */ new Set());
}
});
for (const define of defines) {
if (define.expression.expressionType === import_yantrix_parser6.ExpressionTypes.Function) {
addDependencies(define.expression, define.identifier);
}
}
return {
imports: props.imports,
dependencyGraph: props.dependencyGraph
};
}
__name(buildDependencyGraph, "buildDependencyGraph");
function checkForCyclicDependencies(props) {
const cycles = detectCycles({
dependencyGraph: props.dependencyGraph
});
if (cycles.length > 0) {
const cycleStrings = cycles.map((cycle) => cycle.join(" -> "));
throw new Error(`Cyclic dependencies detected in function definitions:
${cycleStrings.join("\n")}`);
}
}
__name(checkForCyclicDependencies, "checkForCyclicDependencies");
function detectCycles(props) {
const visited = /* @__PURE__ */ new Set();
const recursionStack = /* @__PURE__ */ new Set();
const cycles = [];
const dfs = /* @__PURE__ */ __name((node, path2 = []) => {
if (!visited.has(node)) {
visited.add(node);
recursionStack.add(node);
const neighbors = props.dependencyGraph.get(node) || /* @__PURE__ */ new Set();
for (const neighbor of neighbors) {
if (!visited.has(neighbor)) {
if (dfs(neighbor, [...path2, node])) {
return true;
}
} else if (recursionStack.has(neighbor)) {
cycles.push([...path2, node, neighbor]);
return true;
}
}
}
recursionStack.delete(node);
return false;
}, "dfs");
for (const node of props.dependencyGraph.keys()) {
if (!visited.has(node)) {
dfs(node);
}
}
return cycles;
}
__name(detectCycles, "detectCycles");
// src/core/modules/JavaScript/JavaScriptCompiler/imports/serializer.ts
var getImport = /* @__PURE__ */ __name(({ key, value }) => `import { ${value.join(", ")} } from '${key}';
`, "getImport");
var getNamespaceImport = /* @__PURE__ */ __name(({ key, value }) => `import * as ${value[0]} from '${key}';
`, "getNamespaceImport");
var processRecordImports = /* @__PURE__ */ __name((importsRecord, processImport) => {
let imports2 = "";
for (const [key, value] of Object.entries(importsRecord)) {
imports2 += processImport({ key, value });
}
return imports2;
}, "processRecordImports");
function getImportsCode(props) {
return processRecordImports(props.imports, getImport);
}
__name(getImportsCode, "getImportsCode");
function importAll(props) {
if (!props.importNamespaces) return "";
return processRecordImports(props.importNamespaces, getNamespaceImport);
}
__name(importAll, "importAll");
var importsSerializer = {
getImportsCode,
importAll
};
// src/core/modules/JavaScript/JavaScriptCompiler/imports/index.ts
var imports = {
serializer: {
getImportsCode: importsSerializer.getImportsCode,
importAll: importsSerializer.importAll
},
functions: {
checkForCyclicDependencies,
buildDependencyGraph
}
};
// src/core/modules/JavaScript/JavaScriptCompiler/dictionaries/core.ts
function getObjectKeysMap(dict) {
const obj = {};
Object.keys(dict).forEach((key) => {
obj[key] = key;
});
return obj;
}
__name(getObjectKeysMap, "getObjectKeysMap");
function setupDictionaries(props) {
let dictionaries2 = [];
dictionaries2.push(
`export const statesDictionary = ${JSON.stringify(props.stateDictionary.getDictionary(), null, 2)}`
);
dictionaries2.push(
`export const actionsDictionary = ${JSON.stringify(props.actionDictionary.getDictionary(), null, 2)}`
);
if (Object.keys(props.eventDictionary.getDictionary()).length > 0) {
dictionaries2.push(
`export const eventDictionary = ${JSON.stringify(props.eventDictionary.getDictionary(), null, 2)}`
);
dictionaries2.push(
`GlobalEventDictionary.addEvents({
keys: Object.keys(eventDictionary).filter(e => GlobalEventDictionary.getEventValues({ keys: [e] })[0] == null)
});`
);
}
dictionaries2.push(`export const functionDictionary = new FunctionDictionary();`);
dictionaries2.push();
imports.functions.checkForCyclicDependencies({
dependencyGraph: props.dependencyGraph
});
dictionaries2 = functions.functions.registerCustomFunctions({
diagram: props.diagram,
expressions: props.expressionRecord,
dependencyGraph: props.dependencyGraph,
dictionaries: dictionaries2,
injectFunctions: props.injectedFunctions
});
return dictionaries2;
}
__name(setupDictionaries, "setupDictionaries");
// src/core/modules/JavaScript/JavaScriptCompiler/dictionaries/serializer.ts
var import_mermaid_parser4 = require("@yantrix/mermaid-parser");
function getActionToStateDict(props) {
const dict = {};
Object.entries(props.transitions).forEach(([key, transition]) => {
const newState = props.stateDictionary.getStateValues({ keys: [key] })[0];
transition.actionsPath.forEach(({ action }) => {
const actionValue = props.actionDictionary.getActionValues({
keys: action
})[0];
if (!actionValue) throw new Error(`Action ${action} not found`);
if (!newState) throw new Error(`State ${key} not found`);
if (!dict[actionValue]) {
dict[actionValue] = [];
}
if (!dict[actionValue].includes(newState)) {
dict[actionValue].push(newState);
}
});
});
const res = Object.entries(dict).map(([actionId, possibleStates]) => {
const predicateString = possibleStates.length > 1 ? `,
predicate: predicates[${props.currentState}][${actionId}]` : "";
return `
${actionId}: {
state: [${possibleStates}]${predicateString}
}
`;
}).join(",\n");
return res;
}
__name(getActionToStateDict, "getActionToStateDict");
function getActionToStateFromStateDict(props) {
const actionToStartStateMatrix = {};
Object.entries(props.diagram.transitions).forEach(([state2, transitions]) => {
if (state2 === import_mermaid_parser4.StartState) {
const entries = Object.entries(transitions);
entries.forEach(([state3, action]) => {
action.actionsPath.forEach(({ action: action2 }) => {
actionToStartStateMatrix[state3] = {
actionsPath: [{ action: action2, note: [] }]
};
});
});
}
})