scheduled timers are put in environment
This commit is contained in:
parent
446829c8ec
commit
166fd6d4bc
5 changed files with 87 additions and 53 deletions
|
|
@ -1,14 +1,14 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { ConcreteState, emptyStatechart, isAncestorOf, Statechart, stateDescription, Transition } from "../VisualEditor/ast";
|
import { ConcreteState, emptyStatechart, Statechart, stateDescription, Transition } from "../VisualEditor/ast";
|
||||||
import { VisualEditor } from "../VisualEditor/VisualEditor";
|
import { handleInputEvent, initialize } from "../VisualEditor/interpreter";
|
||||||
import { BigStepOutput, Environment, Mode, RT_Statechart } from "../VisualEditor/runtime_types";
|
|
||||||
import { initialize, handleEvent, handleInputEvent } from "../VisualEditor/interpreter";
|
|
||||||
import { Action, Expression } from "../VisualEditor/label_ast";
|
import { Action, Expression } from "../VisualEditor/label_ast";
|
||||||
|
import { BigStep, Environment, Mode } from "../VisualEditor/runtime_types";
|
||||||
|
import { VisualEditor } from "../VisualEditor/VisualEditor";
|
||||||
|
import { getSimTime, setPaused, setRealtime, TimeMode } from "../VisualEditor/time";
|
||||||
|
|
||||||
import "../index.css";
|
import "../index.css";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import { getSimTime, setPaused, setRealtime, TimeMode } from "../VisualEditor/time";
|
|
||||||
|
|
||||||
export function ShowTransition(props: {transition: Transition}) {
|
export function ShowTransition(props: {transition: Transition}) {
|
||||||
return <>➔ {stateDescription(props.transition.tgt)}</>;
|
return <>➔ {stateDescription(props.transition.tgt)}</>;
|
||||||
|
|
@ -31,7 +31,7 @@ export function ShowExpr(props: {expr: Expression}) {
|
||||||
|
|
||||||
export function ShowAction(props: {action: Action}) {
|
export function ShowAction(props: {action: Action}) {
|
||||||
if (props.action.kind === "raise") {
|
if (props.action.kind === "raise") {
|
||||||
return <>raise {props.action.event}</>;
|
return <>^{props.action.event}</>;
|
||||||
}
|
}
|
||||||
else if (props.action.kind === "assignment") {
|
else if (props.action.kind === "assignment") {
|
||||||
return <>{props.action.lhs} = <ShowExpr expr={props.action.rhs}/>;</>;
|
return <>{props.action.lhs} = <ShowExpr expr={props.action.rhs}/>;</>;
|
||||||
|
|
@ -91,9 +91,10 @@ export function App() {
|
||||||
console.log('runtime: ', rt);
|
console.log('runtime: ', rt);
|
||||||
setRT([{inputEvent: null, simtime: 0, ...config}]);
|
setRT([{inputEvent: null, simtime: 0, ...config}]);
|
||||||
setRTIdx(0);
|
setRTIdx(0);
|
||||||
|
setTime({kind: "paused", simtime: 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop() {
|
function clear() {
|
||||||
setRT([]);
|
setRT([]);
|
||||||
setRTIdx(null);
|
setRTIdx(null);
|
||||||
setTime({kind: "paused", simtime: 0});
|
setTime({kind: "paused", simtime: 0});
|
||||||
|
|
@ -103,7 +104,7 @@ export function App() {
|
||||||
console.log(rtIdx);
|
console.log(rtIdx);
|
||||||
if (rt.length>0 && rtIdx!==null && ast.inputEvents.has(inputEvent)) {
|
if (rt.length>0 && rtIdx!==null && ast.inputEvents.has(inputEvent)) {
|
||||||
const simtime = getSimTime(time, performance.now());
|
const simtime = getSimTime(time, performance.now());
|
||||||
const nextConfig = handleInputEvent(inputEvent, ast, rt[rtIdx]!);
|
const nextConfig = handleInputEvent(simtime, inputEvent, ast, rt[rtIdx]!);
|
||||||
setRT([...rt.slice(0, rtIdx+1), {inputEvent, simtime, ...nextConfig}]);
|
setRT([...rt.slice(0, rtIdx+1), {inputEvent, simtime, ...nextConfig}]);
|
||||||
setRTIdx(rtIdx+1);
|
setRTIdx(rtIdx+1);
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +166,7 @@ export function App() {
|
||||||
</div>
|
</div>
|
||||||
<div className="panel">
|
<div className="panel">
|
||||||
<button onClick={restart}>(re)start</button>
|
<button onClick={restart}>(re)start</button>
|
||||||
<button onClick={stop} disabled={rtIdx===null}>stop</button>
|
<button onClick={clear} disabled={rtIdx===null}>clear</button>
|
||||||
 
|
 
|
||||||
raise
|
raise
|
||||||
{[...ast.inputEvents].map(event => <button disabled={rtIdx===null} onClick={() => raise(event)}>{event}</button>)}
|
{[...ast.inputEvents].map(event => <button disabled={rtIdx===null} onClick={() => raise(event)}>{event}</button>)}
|
||||||
|
|
@ -201,7 +202,9 @@ export function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ShowEnvironment(props: {environment: Environment}) {
|
function ShowEnvironment(props: {environment: Environment}) {
|
||||||
return <div>{[...props.environment.entries()].map(([variable,value]) =>
|
return <div>{[...props.environment.entries()]
|
||||||
|
.filter(([variable]) => !variable.startsWith('_'))
|
||||||
|
.map(([variable,value]) =>
|
||||||
`${variable}: ${value}`
|
`${variable}: ${value}`
|
||||||
).join(', ')}</div>;
|
).join(', ')}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export type AbstractState = {
|
||||||
entryActions: Action[];
|
entryActions: Action[];
|
||||||
exitActions: Action[];
|
exitActions: Action[];
|
||||||
depth: number;
|
depth: number;
|
||||||
|
timers: number[]; // list of timeouts (e.g., the state having an outgoing transition with trigger "after 4s" would appear as the number 4000 in this list)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AndState = {
|
export type AndState = {
|
||||||
|
|
@ -52,6 +53,7 @@ const emptyRoot: OrState = {
|
||||||
comments: [],
|
comments: [],
|
||||||
entryActions: [],
|
entryActions: [],
|
||||||
exitActions: [],
|
exitActions: [],
|
||||||
|
timers: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const emptyStatechart: Statechart = {
|
export const emptyStatechart: Statechart = {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { evalExpr } from "./actionlang_interpreter";
|
import { evalExpr } from "./actionlang_interpreter";
|
||||||
import { computeArena, ConcreteState, getDescendants, isAncestorOf, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./ast";
|
import { computeArena, ConcreteState, getDescendants, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./ast";
|
||||||
import { Action } from "./label_ast";
|
import { Action } from "./label_ast";
|
||||||
import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput } from "./runtime_types";
|
import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput } from "./runtime_types";
|
||||||
|
|
||||||
export function initialize(ast: Statechart): BigStepOutput {
|
export function initialize(ast: Statechart): BigStepOutput {
|
||||||
let {enteredStates, environment, ...raised} = enterDefault(ast.root, {
|
let {enteredStates, environment, ...raised} = enterDefault(0, ast.root, {
|
||||||
environment: new Map(),
|
environment: new Map(),
|
||||||
...initialRaised,
|
...initialRaised,
|
||||||
});
|
});
|
||||||
return handleInternalEvents(ast, {mode: enteredStates, environment, ...raised});
|
return handleInternalEvents(0, ast, {mode: enteredStates, environment, ...raised});
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionScope = {
|
type ActionScope = {
|
||||||
|
|
@ -17,13 +17,40 @@ type ActionScope = {
|
||||||
|
|
||||||
type EnteredScope = { enteredStates: Mode } & ActionScope;
|
type EnteredScope = { enteredStates: Mode } & ActionScope;
|
||||||
|
|
||||||
export function enterDefault(state: ConcreteState, rt: ActionScope): EnteredScope {
|
export function entryActions(simtime: number, state: ConcreteState, actionScope: ActionScope): ActionScope {
|
||||||
let actionScope = rt;
|
|
||||||
|
|
||||||
// execute entry actions
|
|
||||||
for (const action of state.entryActions) {
|
for (const action of state.entryActions) {
|
||||||
(actionScope = execAction(action, actionScope));
|
(actionScope = execAction(action, actionScope));
|
||||||
}
|
}
|
||||||
|
// schedule timers
|
||||||
|
// we store timers in the environment (dirty!)
|
||||||
|
const environment = new Map(actionScope.environment);
|
||||||
|
const timers: [number,string][] = [...(environment.get("_timers") || [])];
|
||||||
|
for (const timeOffset of state.timers) {
|
||||||
|
const futureSimTime = simtime + timeOffset; // point in simtime when after-trigger becomes enabled
|
||||||
|
timers.push([futureSimTime, state.uid+'/'+timeOffset]);
|
||||||
|
}
|
||||||
|
timers.sort((a,b) => a[0] - b[0]); // smallest futureSimTime comes first
|
||||||
|
environment.set("_timers", timers);
|
||||||
|
return {...actionScope, environment};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exitActions(simtime: number, state: ConcreteState, actionScope: ActionScope): ActionScope {
|
||||||
|
for (const action of state.exitActions) {
|
||||||
|
(actionScope = execAction(action, actionScope));
|
||||||
|
}
|
||||||
|
// cancel timers
|
||||||
|
const environment = new Map(actionScope.environment);
|
||||||
|
const timers: [number,string][] = environment.get("_timers") || [];
|
||||||
|
const filtered = timers.filter(([_, timerId]) => !timerId.startsWith(state.uid+'/'));
|
||||||
|
environment.set("_timers", filtered);
|
||||||
|
return {...actionScope, environment};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enterDefault(simtime: number, state: ConcreteState, rt: ActionScope): EnteredScope {
|
||||||
|
let actionScope = rt;
|
||||||
|
|
||||||
|
// execute entry actions
|
||||||
|
actionScope = entryActions(simtime, state, actionScope);
|
||||||
|
|
||||||
// enter children...
|
// enter children...
|
||||||
let enteredStates = new Set([state.uid]);
|
let enteredStates = new Set([state.uid]);
|
||||||
|
|
@ -31,7 +58,7 @@ export function enterDefault(state: ConcreteState, rt: ActionScope): EnteredScop
|
||||||
// enter every child
|
// enter every child
|
||||||
for (const child of state.children) {
|
for (const child of state.children) {
|
||||||
let enteredChildren;
|
let enteredChildren;
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterDefault(child, actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, child, actionScope));
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +69,7 @@ export function enterDefault(state: ConcreteState, rt: ActionScope): EnteredScop
|
||||||
console.warn(state.uid + ': multiple initial states, only entering one of them');
|
console.warn(state.uid + ': multiple initial states, only entering one of them');
|
||||||
}
|
}
|
||||||
let enteredChildren;
|
let enteredChildren;
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterDefault(state.initial[0][1], actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, state.initial[0][1], actionScope));
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
console.warn(state.uid + ': no initial state');
|
console.warn(state.uid + ': no initial state');
|
||||||
|
|
@ -51,15 +78,13 @@ export function enterDefault(state: ConcreteState, rt: ActionScope): EnteredScop
|
||||||
return {enteredStates, ...actionScope};
|
return {enteredStates, ...actionScope};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enterPath(path: ConcreteState[], rt: ActionScope): EnteredScope {
|
export function enterPath(simtime: number, path: ConcreteState[], rt: ActionScope): EnteredScope {
|
||||||
let actionScope = rt;
|
let actionScope = rt;
|
||||||
|
|
||||||
const [state, ...rest] = path;
|
const [state, ...rest] = path;
|
||||||
|
|
||||||
// execute entry actions
|
// execute entry actions
|
||||||
for (const action of state.entryActions) {
|
actionScope = entryActions(simtime, state, actionScope);
|
||||||
(actionScope = execAction(action, actionScope));
|
|
||||||
}
|
|
||||||
|
|
||||||
// enter children...
|
// enter children...
|
||||||
let enteredStates = new Set([state.uid]);
|
let enteredStates = new Set([state.uid]);
|
||||||
|
|
@ -68,10 +93,10 @@ export function enterPath(path: ConcreteState[], rt: ActionScope): EnteredScope
|
||||||
for (const child of state.children) {
|
for (const child of state.children) {
|
||||||
let enteredChildren;
|
let enteredChildren;
|
||||||
if (rest.length > 0 && child.uid === rest[0].uid) {
|
if (rest.length > 0 && child.uid === rest[0].uid) {
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterPath(rest, actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterPath(simtime, rest, actionScope));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterDefault(child, actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, child, actionScope));
|
||||||
}
|
}
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
|
|
@ -79,14 +104,14 @@ export function enterPath(path: ConcreteState[], rt: ActionScope): EnteredScope
|
||||||
else if (state.kind === "or") {
|
else if (state.kind === "or") {
|
||||||
if (rest.length > 0) {
|
if (rest.length > 0) {
|
||||||
let enteredChildren;
|
let enteredChildren;
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterPath(rest, actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterPath(simtime, rest, actionScope));
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// same as AND-state, but we only enter the initial state(s)
|
// same as AND-state, but we only enter the initial state(s)
|
||||||
for (const [_, child] of state.initial) {
|
for (const [_, child] of state.initial) {
|
||||||
let enteredChildren;
|
let enteredChildren;
|
||||||
({enteredStates: enteredChildren, ...actionScope} = enterDefault(child, actionScope));
|
({enteredStates: enteredChildren, ...actionScope} = enterDefault(simtime, child, actionScope));
|
||||||
enteredStates = enteredStates.union(enteredChildren);
|
enteredStates = enteredStates.union(enteredChildren);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,25 +121,23 @@ export function enterPath(path: ConcreteState[], rt: ActionScope): EnteredScope
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit the given state and all its active descendants
|
// exit the given state and all its active descendants
|
||||||
export function exitCurrent(state: ConcreteState, rt: EnteredScope): ActionScope {
|
export function exitCurrent(simtime: number, state: ConcreteState, rt: EnteredScope): ActionScope {
|
||||||
let {enteredStates, ...actionScope} = rt;
|
let {enteredStates, ...actionScope} = rt;
|
||||||
|
|
||||||
// exit all active children...
|
// exit all active children...
|
||||||
for (const child of state.children) {
|
for (const child of state.children) {
|
||||||
if (enteredStates.has(child.uid)) {
|
if (enteredStates.has(child.uid)) {
|
||||||
actionScope = exitCurrent(child, {enteredStates, ...actionScope});
|
actionScope = exitCurrent(simtime, child, {enteredStates, ...actionScope});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute exit actions
|
// execute exit actions
|
||||||
for (const action of state.exitActions) {
|
actionScope = exitActions(simtime, state, actionScope);
|
||||||
(actionScope = execAction(action, actionScope));
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionScope;
|
return actionScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exitPath(path: ConcreteState[], rt: EnteredScope): ActionScope {
|
export function exitPath(simtime: number, path: ConcreteState[], rt: EnteredScope): ActionScope {
|
||||||
let {enteredStates, ...actionScope} = rt;
|
let {enteredStates, ...actionScope} = rt;
|
||||||
|
|
||||||
const toExit = enteredStates.difference(new Set(path));
|
const toExit = enteredStates.difference(new Set(path));
|
||||||
|
|
@ -122,15 +145,13 @@ export function exitPath(path: ConcreteState[], rt: EnteredScope): ActionScope {
|
||||||
const [state, ...rest] = path;
|
const [state, ...rest] = path;
|
||||||
|
|
||||||
// exit state and all its children, *except* states along the rest of the path
|
// exit state and all its children, *except* states along the rest of the path
|
||||||
actionScope = exitCurrent(state, {enteredStates: toExit, ...actionScope});
|
actionScope = exitCurrent(simtime, state, {enteredStates: toExit, ...actionScope});
|
||||||
if (rest.length > 0) {
|
if (rest.length > 0) {
|
||||||
actionScope = exitPath(rest, {enteredStates, ...actionScope});
|
actionScope = exitPath(simtime, rest, {enteredStates, ...actionScope});
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute exit actions
|
// execute exit actions
|
||||||
for (const action of state.exitActions) {
|
actionScope = exitActions(simtime, state, actionScope);
|
||||||
(actionScope = execAction(action, actionScope));
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionScope;
|
return actionScope;
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +185,7 @@ export function execAction(action: Action, rt: ActionScope): ActionScope {
|
||||||
throw new Error("should never reach here");
|
throw new Error("should never reach here");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleEvent(event: string, statechart: Statechart, activeParent: ConcreteState, {environment, mode, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
export function handleEvent(simtime: number, event: string, statechart: Statechart, activeParent: ConcreteState, {environment, mode, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||||
const arenasFired = new Set<OrState>();
|
const arenasFired = new Set<OrState>();
|
||||||
for (const state of activeParent.children) {
|
for (const state of activeParent.children) {
|
||||||
if (mode.has(state.uid)) {
|
if (mode.has(state.uid)) {
|
||||||
|
|
@ -188,7 +209,7 @@ export function handleEvent(event: string, statechart: Statechart, activeParent:
|
||||||
}
|
}
|
||||||
if (!overlapping) {
|
if (!overlapping) {
|
||||||
console.log('^ firing');
|
console.log('^ firing');
|
||||||
({mode, environment, ...raised} = fireTransition(t, arena, srcPath, tgtPath, {mode, environment, ...raised}));
|
({mode, environment, ...raised} = fireTransition(simtime, t, arena, srcPath, tgtPath, {mode, environment, ...raised}));
|
||||||
arenasFired.add(arena);
|
arenasFired.add(arena);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -197,25 +218,25 @@ export function handleEvent(event: string, statechart: Statechart, activeParent:
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// no enabled outgoing transitions, try the children:
|
// no enabled outgoing transitions, try the children:
|
||||||
({environment, mode, ...raised} = handleEvent(event, statechart, state, {environment, mode, ...raised}));
|
({environment, mode, ...raised} = handleEvent(simtime, event, statechart, state, {environment, mode, ...raised}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {environment, mode, ...raised};
|
return {environment, mode, ...raised};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleInputEvent(event: string, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStepOutput {
|
export function handleInputEvent(simtime: number, event: string, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStepOutput {
|
||||||
let raised = initialRaised;
|
let raised = initialRaised;
|
||||||
|
|
||||||
({mode, environment, ...raised} = handleEvent(event, statechart, statechart.root, {mode, environment, ...raised}));
|
({mode, environment, ...raised} = handleEvent(simtime, event, statechart, statechart.root, {mode, environment, ...raised}));
|
||||||
|
|
||||||
return handleInternalEvents(statechart, {mode, environment, ...raised});
|
return handleInternalEvents(simtime, statechart, {mode, environment, ...raised});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleInternalEvents(statechart: Statechart, {mode, environment, ...raised}: RT_Statechart & RaisedEvents): BigStepOutput {
|
export function handleInternalEvents(simtime: number, statechart: Statechart, {mode, environment, ...raised}: RT_Statechart & RaisedEvents): BigStepOutput {
|
||||||
while (raised.internalEvents.length > 0) {
|
while (raised.internalEvents.length > 0) {
|
||||||
const [internalEvent, ...rest] = raised.internalEvents;
|
const [internalEvent, ...rest] = raised.internalEvents;
|
||||||
({mode, environment, ...raised} = handleEvent(internalEvent, statechart, statechart.root, {mode, environment, internalEvents: rest, outputEvents: raised.outputEvents}));
|
({mode, environment, ...raised} = handleEvent(simtime, internalEvent, statechart, statechart.root, {mode, environment, internalEvents: rest, outputEvents: raised.outputEvents}));
|
||||||
}
|
}
|
||||||
return {mode, environment, outputEvents: raised.outputEvents};
|
return {mode, environment, outputEvents: raised.outputEvents};
|
||||||
}
|
}
|
||||||
|
|
@ -224,12 +245,12 @@ function transitionDescription(t: Transition) {
|
||||||
return stateDescription(t.src) + ' ➔ ' + stateDescription(t.tgt);
|
return stateDescription(t.src) + ' ➔ ' + stateDescription(t.tgt);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fireTransition(t: Transition, arena: OrState, srcPath: ConcreteState[], tgtPath: ConcreteState[], {mode, environment, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
export function fireTransition(simtime: number, t: Transition, arena: OrState, srcPath: ConcreteState[], tgtPath: ConcreteState[], {mode, environment, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||||
|
|
||||||
// console.log('fire ', transitionDescription(t), {arena, srcPath, tgtPath});
|
// console.log('fire ', transitionDescription(t), {arena, srcPath, tgtPath});
|
||||||
|
|
||||||
// exit src
|
// exit src
|
||||||
({environment, ...raised} = exitPath(srcPath.slice(1), {environment, enteredStates: mode, ...raised}));
|
({environment, ...raised} = exitPath(simtime, srcPath.slice(1), {environment, enteredStates: mode, ...raised}));
|
||||||
const toExit = getDescendants(arena);
|
const toExit = getDescendants(arena);
|
||||||
toExit.delete(arena.uid); // do not exit the arena itself
|
toExit.delete(arena.uid); // do not exit the arena itself
|
||||||
const exitedMode = mode.difference(toExit);
|
const exitedMode = mode.difference(toExit);
|
||||||
|
|
@ -241,7 +262,7 @@ export function fireTransition(t: Transition, arena: OrState, srcPath: ConcreteS
|
||||||
|
|
||||||
// enter tgt
|
// enter tgt
|
||||||
let enteredStates;
|
let enteredStates;
|
||||||
({enteredStates, environment, ...raised} = enterPath(tgtPath.slice(1), {environment, ...raised}));
|
({enteredStates, environment, ...raised} = enterPath(simtime, tgtPath.slice(1), {environment, ...raised}));
|
||||||
const enteredMode = exitedMode.union(enteredStates);
|
const enteredMode = exitedMode.union(enteredStates);
|
||||||
|
|
||||||
return {mode: enteredMode, environment, ...raised};
|
return {mode: enteredMode, environment, ...raised};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ export type ParsedText = TransitionLabel | Comment;
|
||||||
|
|
||||||
export type TransitionLabel = {
|
export type TransitionLabel = {
|
||||||
kind: "transitionLabel";
|
kind: "transitionLabel";
|
||||||
|
uid: string; // uid of the text node
|
||||||
trigger: Trigger;
|
trigger: Trigger;
|
||||||
guard: Expression;
|
guard: Expression;
|
||||||
actions: Action[];
|
actions: Action[];
|
||||||
|
|
@ -9,6 +10,7 @@ export type TransitionLabel = {
|
||||||
|
|
||||||
export type Comment = {
|
export type Comment = {
|
||||||
kind: "comment";
|
kind: "comment";
|
||||||
|
uid: string; // uid of the text node
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { act } from "react";
|
|
||||||
import { ConcreteState, OrState, Statechart, Transition } from "./ast";
|
import { ConcreteState, OrState, Statechart, Transition } from "./ast";
|
||||||
import { findNearestArrow, findNearestRountangleSide, findRountangle, Rountangle, VisualEditorState } from "./editor_types";
|
import { findNearestArrow, findNearestRountangleSide, findRountangle, Rountangle, VisualEditorState } from "./editor_types";
|
||||||
import { isEntirelyWithin, transformLine } from "./geometry";
|
import { isEntirelyWithin } from "./geometry";
|
||||||
import { Action, Expression, ParsedText, TransitionLabel } from "./label_ast";
|
import { Action, Expression, ParsedText } from "./label_ast";
|
||||||
|
|
||||||
import { parse as parseLabel, SyntaxError } from "./label_parser";
|
import { parse as parseLabel, SyntaxError } from "./label_parser";
|
||||||
|
|
||||||
|
|
@ -19,6 +18,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
||||||
entryActions: [],
|
entryActions: [],
|
||||||
exitActions: [],
|
exitActions: [],
|
||||||
depth: 0,
|
depth: 0,
|
||||||
|
timers: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
const uid2State = new Map<string, ConcreteState>([["root", root]]);
|
const uid2State = new Map<string, ConcreteState>([["root", root]]);
|
||||||
|
|
@ -44,6 +44,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
||||||
comments: [],
|
comments: [],
|
||||||
entryActions: [],
|
entryActions: [],
|
||||||
exitActions: [],
|
exitActions: [],
|
||||||
|
timers: [],
|
||||||
}
|
}
|
||||||
if (state.kind === "or") {
|
if (state.kind === "or") {
|
||||||
state.initial = [];
|
state.initial = [];
|
||||||
|
|
@ -144,6 +145,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parsed.uid = text.uid;
|
||||||
if (parsed.kind === "transitionLabel") {
|
if (parsed.kind === "transitionLabel") {
|
||||||
const belongsToArrow = findNearestArrow(text.topLeft, state.arrows);
|
const belongsToArrow = findNearestArrow(text.topLeft, state.arrows);
|
||||||
if (belongsToArrow) {
|
if (belongsToArrow) {
|
||||||
|
|
@ -161,6 +163,10 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
||||||
inputEvents.add(event);
|
inputEvents.add(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (parsed.trigger.kind === "after") {
|
||||||
|
belongsToTransition.src.timers.push(parsed.trigger.durationMs);
|
||||||
|
belongsToTransition.src.timers.sort();
|
||||||
|
}
|
||||||
for (const action of parsed.actions) {
|
for (const action of parsed.actions) {
|
||||||
if (action.kind === "raise") {
|
if (action.kind === "raise") {
|
||||||
const {event} = action;
|
const {event} = action;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue