cleanup code a bit
This commit is contained in:
parent
5e7b944978
commit
b14b9e205c
11 changed files with 327 additions and 155 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { evalExpr } from "./actionlang_interpreter";
|
||||
import { computeArena, ConcreteState, getDescendants, isOverlapping, OrState, Statechart, stateDescription, Transition } from "./abstract_syntax";
|
||||
import { Action } from "./label_ast";
|
||||
import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput, TimerElapseEvent, Timers } from "./runtime_types";
|
||||
import { Action, EventTrigger } from "./label_ast";
|
||||
import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised, BigStepOutput, Timers, RT_Event } from "./runtime_types";
|
||||
|
||||
export function initialize(ast: Statechart): BigStepOutput {
|
||||
let {enteredStates, environment, ...raised} = enterDefault(0, ast.root, {
|
||||
|
|
@ -27,7 +27,7 @@ export function entryActions(simtime: number, state: ConcreteState, actionScope:
|
|||
const timers: Timers = [...(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: state.uid, timeDurMs: timeOffset}]);
|
||||
timers.push([futureSimTime, {kind: "timer", state: state.uid, timeDurMs: timeOffset}]);
|
||||
}
|
||||
timers.sort((a,b) => a[0] - b[0]); // smallest futureSimTime comes first
|
||||
environment.set("_timers", timers);
|
||||
|
|
@ -167,40 +167,46 @@ export function execAction(action: Action, rt: ActionScope): ActionScope {
|
|||
};
|
||||
}
|
||||
else if (action.kind === "raise") {
|
||||
const raisedEvent = {
|
||||
name: action.event,
|
||||
param: action.param && evalExpr(action.param, rt.environment),
|
||||
};
|
||||
if (action.event.startsWith('_')) {
|
||||
// append to internal events
|
||||
return {
|
||||
...rt,
|
||||
internalEvents: [...rt.internalEvents, action.event],
|
||||
internalEvents: [...rt.internalEvents, raisedEvent],
|
||||
};
|
||||
}
|
||||
else {
|
||||
// append to output events
|
||||
return {
|
||||
...rt,
|
||||
outputEvents: [...rt.outputEvents, action.event],
|
||||
outputEvents: [...rt.outputEvents, raisedEvent],
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("should never reach here");
|
||||
}
|
||||
|
||||
export function handleEvent(simtime: number, event: string | TimerElapseEvent, statechart: Statechart, activeParent: ConcreteState, {environment, mode, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
export function handleEvent(simtime: number, event: RT_Event, statechart: Statechart, activeParent: ConcreteState, {environment, mode, ...raised}: RT_Statechart & RaisedEvents): RT_Statechart & RaisedEvents {
|
||||
const arenasFired = new Set<OrState>();
|
||||
for (const state of activeParent.children) {
|
||||
if (mode.has(state.uid)) {
|
||||
const outgoing = statechart.transitions.get(state.uid) || [];
|
||||
let triggered;
|
||||
if (typeof event === 'string') {
|
||||
if (event.kind === "input") {
|
||||
// get transitions triggered by event
|
||||
triggered = outgoing.filter(transition => {
|
||||
const trigger = transition.label[0].trigger;
|
||||
if (trigger.kind === "event") {
|
||||
return trigger.event === event;
|
||||
return trigger.event === event.name;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
// get transitions triggered by timeout
|
||||
triggered = outgoing.filter(transition => {
|
||||
const trigger = transition.label[0].trigger;
|
||||
if (trigger.kind === "after") {
|
||||
|
|
@ -209,6 +215,7 @@ export function handleEvent(simtime: number, event: string | TimerElapseEvent, s
|
|||
return false;
|
||||
});
|
||||
}
|
||||
// eval guard
|
||||
const enabled = triggered.filter(transition =>
|
||||
evalExpr(transition.label[0].guard, environment)
|
||||
);
|
||||
|
|
@ -227,7 +234,24 @@ export function handleEvent(simtime: number, event: string | TimerElapseEvent, s
|
|||
}
|
||||
if (!overlapping) {
|
||||
console.log('^ firing');
|
||||
let oldValue;
|
||||
if (event.kind === "input" && event.param !== undefined) {
|
||||
// input events may have a parameter
|
||||
// *temporarily* add event to environment (dirty!)
|
||||
oldValue = environment.get(event.param.name);
|
||||
environment = new Map([
|
||||
...environment,
|
||||
[(t.label[0].trigger as EventTrigger).paramName as string, event.param.value],
|
||||
]);
|
||||
}
|
||||
({mode, environment, ...raised} = fireTransition(simtime, t, arena, srcPath, tgtPath, {mode, environment, ...raised}));
|
||||
if (event.kind === "input" && event.param) {
|
||||
// restore original value of variable that had same name as input parameter
|
||||
environment = new Map([
|
||||
...environment,
|
||||
[(t.label[0].trigger as EventTrigger).paramName as string, oldValue],
|
||||
]);
|
||||
}
|
||||
arenasFired.add(arena);
|
||||
}
|
||||
else {
|
||||
|
|
@ -243,7 +267,7 @@ export function handleEvent(simtime: number, event: string | TimerElapseEvent, s
|
|||
return {environment, mode, ...raised};
|
||||
}
|
||||
|
||||
export function handleInputEvent(simtime: number, event: string, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStepOutput {
|
||||
export function handleInputEvent(simtime: number, event: RT_Event, statechart: Statechart, {mode, environment}: {mode: Mode, environment: Environment}): BigStepOutput {
|
||||
let raised = initialRaised;
|
||||
|
||||
({mode, environment, ...raised} = handleEvent(simtime, event, statechart, statechart.root, {mode, environment, ...raised}));
|
||||
|
|
@ -254,7 +278,9 @@ export function handleInputEvent(simtime: number, event: string, statechart: Sta
|
|||
export function handleInternalEvents(simtime: number, statechart: Statechart, {mode, environment, ...raised}: RT_Statechart & RaisedEvents): BigStepOutput {
|
||||
while (raised.internalEvents.length > 0) {
|
||||
const [internalEvent, ...rest] = raised.internalEvents;
|
||||
({mode, environment, ...raised} = handleEvent(simtime, internalEvent, statechart, statechart.root, {mode, environment, internalEvents: rest, outputEvents: raised.outputEvents}));
|
||||
({mode, environment, ...raised} = handleEvent(simtime,
|
||||
{kind: "input", ...internalEvent}, // internal event becomes input event
|
||||
statechart, statechart.root, {mode, environment, internalEvents: rest, outputEvents: raised.outputEvents}));
|
||||
}
|
||||
return {mode, environment, outputEvents: raised.outputEvents};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export type Trigger = EventTrigger | AfterTrigger | EntryTrigger | ExitTrigger;
|
|||
export type EventTrigger = {
|
||||
kind: "event";
|
||||
event: string;
|
||||
paramName?: string;
|
||||
}
|
||||
|
||||
export type AfterTrigger = {
|
||||
|
|
@ -45,6 +46,7 @@ export type Assignment = {
|
|||
export type RaiseEvent = {
|
||||
kind: "raise";
|
||||
event: string;
|
||||
param?: Expression;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -167,19 +167,19 @@ function peg$parse(input, options) {
|
|||
const peg$c0 = "[";
|
||||
const peg$c1 = "]";
|
||||
const peg$c2 = "/";
|
||||
const peg$c3 = "after";
|
||||
const peg$c4 = "entry";
|
||||
const peg$c5 = "exit";
|
||||
const peg$c6 = "ms";
|
||||
const peg$c7 = "s";
|
||||
const peg$c8 = ";";
|
||||
const peg$c9 = "=";
|
||||
const peg$c10 = "==";
|
||||
const peg$c11 = "!=";
|
||||
const peg$c12 = "<=";
|
||||
const peg$c13 = ">=";
|
||||
const peg$c14 = "(";
|
||||
const peg$c15 = ")";
|
||||
const peg$c3 = "(";
|
||||
const peg$c4 = ")";
|
||||
const peg$c5 = "after";
|
||||
const peg$c6 = "entry";
|
||||
const peg$c7 = "exit";
|
||||
const peg$c8 = "ms";
|
||||
const peg$c9 = "s";
|
||||
const peg$c10 = ";";
|
||||
const peg$c11 = "=";
|
||||
const peg$c12 = "==";
|
||||
const peg$c13 = "!=";
|
||||
const peg$c14 = "<=";
|
||||
const peg$c15 = ">=";
|
||||
const peg$c16 = "true";
|
||||
const peg$c17 = "false";
|
||||
const peg$c18 = "^";
|
||||
|
|
@ -196,24 +196,24 @@ function peg$parse(input, options) {
|
|||
const peg$e0 = peg$literalExpectation("[", false);
|
||||
const peg$e1 = peg$literalExpectation("]", false);
|
||||
const peg$e2 = peg$literalExpectation("/", false);
|
||||
const peg$e3 = peg$literalExpectation("after", false);
|
||||
const peg$e4 = peg$literalExpectation("entry", false);
|
||||
const peg$e5 = peg$literalExpectation("exit", false);
|
||||
const peg$e6 = peg$literalExpectation("ms", false);
|
||||
const peg$e7 = peg$literalExpectation("s", false);
|
||||
const peg$e8 = peg$literalExpectation(";", false);
|
||||
const peg$e9 = peg$literalExpectation("=", false);
|
||||
const peg$e10 = peg$classExpectation([["0", "9"], ["A", "Z"], "_", ["a", "z"]], false, false, false);
|
||||
const peg$e11 = peg$classExpectation([["0", "9"]], false, false, false);
|
||||
const peg$e12 = peg$literalExpectation("==", false);
|
||||
const peg$e13 = peg$literalExpectation("!=", false);
|
||||
const peg$e14 = peg$classExpectation(["<", ">"], false, false, false);
|
||||
const peg$e15 = peg$literalExpectation("<=", false);
|
||||
const peg$e16 = peg$literalExpectation(">=", false);
|
||||
const peg$e17 = peg$classExpectation(["+", "-"], false, false, false);
|
||||
const peg$e18 = peg$classExpectation(["*", "/"], false, false, false);
|
||||
const peg$e19 = peg$literalExpectation("(", false);
|
||||
const peg$e20 = peg$literalExpectation(")", false);
|
||||
const peg$e3 = peg$literalExpectation("(", false);
|
||||
const peg$e4 = peg$literalExpectation(")", false);
|
||||
const peg$e5 = peg$literalExpectation("after", false);
|
||||
const peg$e6 = peg$literalExpectation("entry", false);
|
||||
const peg$e7 = peg$literalExpectation("exit", false);
|
||||
const peg$e8 = peg$literalExpectation("ms", false);
|
||||
const peg$e9 = peg$literalExpectation("s", false);
|
||||
const peg$e10 = peg$literalExpectation(";", false);
|
||||
const peg$e11 = peg$literalExpectation("=", false);
|
||||
const peg$e12 = peg$classExpectation([["0", "9"], ["A", "Z"], "_", ["a", "z"]], false, false, false);
|
||||
const peg$e13 = peg$classExpectation([["0", "9"]], false, false, false);
|
||||
const peg$e14 = peg$literalExpectation("==", false);
|
||||
const peg$e15 = peg$literalExpectation("!=", false);
|
||||
const peg$e16 = peg$classExpectation(["<", ">"], false, false, false);
|
||||
const peg$e17 = peg$literalExpectation("<=", false);
|
||||
const peg$e18 = peg$literalExpectation(">=", false);
|
||||
const peg$e19 = peg$classExpectation(["+", "-"], false, false, false);
|
||||
const peg$e20 = peg$classExpectation(["*", "/"], false, false, false);
|
||||
const peg$e21 = peg$literalExpectation("true", false);
|
||||
const peg$e22 = peg$literalExpectation("false", false);
|
||||
const peg$e23 = peg$literalExpectation("^", false);
|
||||
|
|
@ -230,8 +230,8 @@ function peg$parse(input, options) {
|
|||
actions: actions ? actions[2] : [],
|
||||
};
|
||||
}
|
||||
function peg$f1(event) {
|
||||
return {kind: "event", event};
|
||||
function peg$f1(event, param) {
|
||||
return {kind: "event", event, param: param ? param[1] : undefined};
|
||||
}
|
||||
function peg$f2(dur) {
|
||||
return {kind: "after", durationMs: dur};
|
||||
|
|
@ -305,8 +305,8 @@ function peg$parse(input, options) {
|
|||
function peg$f17() {
|
||||
return text() === "true";
|
||||
}
|
||||
function peg$f18(event) {
|
||||
return {kind: "raise", event};
|
||||
function peg$f18(event, param) {
|
||||
return {kind: "raise", event, param: param ? param[1] : undefined};
|
||||
}
|
||||
function peg$f19() { return null; }
|
||||
function peg$f20(text) {
|
||||
|
|
@ -597,15 +597,53 @@ function peg$parse(input, options) {
|
|||
}
|
||||
|
||||
function peg$parseeventTrigger() {
|
||||
let s0, s1;
|
||||
let s0, s1, s2, s3, s4, s5;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parseidentifier();
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 40) {
|
||||
s3 = peg$c3;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
}
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parseidentifier();
|
||||
if (s4 !== peg$FAILED) {
|
||||
if (input.charCodeAt(peg$currPos) === 41) {
|
||||
s5 = peg$c4;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s3 = [s3, s4, s5];
|
||||
s2 = s3;
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s2;
|
||||
s2 = peg$FAILED;
|
||||
}
|
||||
if (s2 === peg$FAILED) {
|
||||
s2 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f1(s1);
|
||||
s0 = peg$f1(s1, s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
s0 = s1;
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
|
@ -614,12 +652,12 @@ function peg$parse(input, options) {
|
|||
let s0, s1, s2, s3;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 5) === peg$c3) {
|
||||
s1 = peg$c3;
|
||||
if (input.substr(peg$currPos, 5) === peg$c5) {
|
||||
s1 = peg$c5;
|
||||
peg$currPos += 5;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parse_();
|
||||
|
|
@ -643,12 +681,12 @@ function peg$parse(input, options) {
|
|||
let s0, s1;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 5) === peg$c4) {
|
||||
s1 = peg$c4;
|
||||
if (input.substr(peg$currPos, 5) === peg$c6) {
|
||||
s1 = peg$c6;
|
||||
peg$currPos += 5;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
|
|
@ -663,12 +701,12 @@ function peg$parse(input, options) {
|
|||
let s0, s1;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 4) === peg$c5) {
|
||||
s1 = peg$c5;
|
||||
if (input.substr(peg$currPos, 4) === peg$c7) {
|
||||
s1 = peg$c7;
|
||||
peg$currPos += 4;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
|
|
@ -705,21 +743,21 @@ function peg$parse(input, options) {
|
|||
function peg$parsetimeUnit() {
|
||||
let s0, s1;
|
||||
|
||||
if (input.substr(peg$currPos, 2) === peg$c6) {
|
||||
s0 = peg$c6;
|
||||
if (input.substr(peg$currPos, 2) === peg$c8) {
|
||||
s0 = peg$c8;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
}
|
||||
if (s0 === peg$FAILED) {
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 115) {
|
||||
s1 = peg$c7;
|
||||
s1 = peg$c9;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
|
|
@ -741,11 +779,11 @@ function peg$parse(input, options) {
|
|||
s3 = peg$currPos;
|
||||
s4 = peg$parse_();
|
||||
if (input.charCodeAt(peg$currPos) === 59) {
|
||||
s5 = peg$c8;
|
||||
s5 = peg$c10;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s6 = peg$parse_();
|
||||
|
|
@ -766,11 +804,11 @@ function peg$parse(input, options) {
|
|||
s3 = peg$currPos;
|
||||
s4 = peg$parse_();
|
||||
if (input.charCodeAt(peg$currPos) === 59) {
|
||||
s5 = peg$c8;
|
||||
s5 = peg$c10;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s6 = peg$parse_();
|
||||
|
|
@ -789,11 +827,11 @@ function peg$parse(input, options) {
|
|||
}
|
||||
s3 = peg$parse_();
|
||||
if (input.charCodeAt(peg$currPos) === 59) {
|
||||
s4 = peg$c8;
|
||||
s4 = peg$c10;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
}
|
||||
if (s4 === peg$FAILED) {
|
||||
s4 = null;
|
||||
|
|
@ -827,11 +865,11 @@ function peg$parse(input, options) {
|
|||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parse_();
|
||||
if (input.charCodeAt(peg$currPos) === 61) {
|
||||
s3 = peg$c9;
|
||||
s3 = peg$c11;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
||||
}
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parse_();
|
||||
|
|
@ -865,7 +903,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e12); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
while (s2 !== peg$FAILED) {
|
||||
|
|
@ -875,7 +913,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e12); }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -900,7 +938,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e13); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
while (s2 !== peg$FAILED) {
|
||||
|
|
@ -910,7 +948,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e13); }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -934,20 +972,20 @@ function peg$parse(input, options) {
|
|||
s2 = peg$currPos;
|
||||
s3 = peg$currPos;
|
||||
s4 = peg$parse_();
|
||||
if (input.substr(peg$currPos, 2) === peg$c10) {
|
||||
s5 = peg$c10;
|
||||
if (input.substr(peg$currPos, 2) === peg$c12) {
|
||||
s5 = peg$c12;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e12); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
||||
}
|
||||
if (s5 === peg$FAILED) {
|
||||
if (input.substr(peg$currPos, 2) === peg$c11) {
|
||||
s5 = peg$c11;
|
||||
if (input.substr(peg$currPos, 2) === peg$c13) {
|
||||
s5 = peg$c13;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e13); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e15); }
|
||||
}
|
||||
if (s5 === peg$FAILED) {
|
||||
s5 = input.charAt(peg$currPos);
|
||||
|
|
@ -955,23 +993,23 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e16); }
|
||||
}
|
||||
if (s5 === peg$FAILED) {
|
||||
if (input.substr(peg$currPos, 2) === peg$c12) {
|
||||
s5 = peg$c12;
|
||||
if (input.substr(peg$currPos, 2) === peg$c14) {
|
||||
s5 = peg$c14;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e15); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
||||
}
|
||||
if (s5 === peg$FAILED) {
|
||||
if (input.substr(peg$currPos, 2) === peg$c13) {
|
||||
s5 = peg$c13;
|
||||
if (input.substr(peg$currPos, 2) === peg$c15) {
|
||||
s5 = peg$c15;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e16); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1025,7 +1063,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e19); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s6 = peg$parse_();
|
||||
|
|
@ -1075,7 +1113,7 @@ function peg$parse(input, options) {
|
|||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e20); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s6 = peg$parse_();
|
||||
|
|
@ -1130,11 +1168,11 @@ function peg$parse(input, options) {
|
|||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 40) {
|
||||
s1 = peg$c14;
|
||||
s1 = peg$c3;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e19); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = peg$parse_();
|
||||
|
|
@ -1142,11 +1180,11 @@ function peg$parse(input, options) {
|
|||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parse_();
|
||||
if (input.charCodeAt(peg$currPos) === 41) {
|
||||
s5 = peg$c15;
|
||||
s5 = peg$c4;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e20); }
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
|
|
@ -1228,7 +1266,7 @@ function peg$parse(input, options) {
|
|||
}
|
||||
|
||||
function peg$parseraise() {
|
||||
let s0, s1, s2, s3;
|
||||
let s0, s1, s2, s3, s4, s5, s6, s7;
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 94) {
|
||||
|
|
@ -1242,8 +1280,44 @@ function peg$parse(input, options) {
|
|||
s2 = peg$parse_();
|
||||
s3 = peg$parseidentifier();
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 40) {
|
||||
s5 = peg$c3;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s5 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
}
|
||||
if (s5 !== peg$FAILED) {
|
||||
s6 = peg$parsecompare();
|
||||
if (s6 !== peg$FAILED) {
|
||||
if (input.charCodeAt(peg$currPos) === 41) {
|
||||
s7 = peg$c4;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s7 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
}
|
||||
if (s7 !== peg$FAILED) {
|
||||
s5 = [s5, s6, s7];
|
||||
s4 = s5;
|
||||
} else {
|
||||
peg$currPos = s4;
|
||||
s4 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s4;
|
||||
s4 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s4;
|
||||
s4 = peg$FAILED;
|
||||
}
|
||||
if (s4 === peg$FAILED) {
|
||||
s4 = null;
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f18(s3);
|
||||
s0 = peg$f18(s3, s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,14 @@ import { Action, Expression, ParsedText } from "./label_ast";
|
|||
|
||||
import { parse as parseLabel, SyntaxError } from "./label_parser";
|
||||
|
||||
export function parseStatechart(state: VisualEditorState): [Statechart, [string,string][]] {
|
||||
const errorShapes: [string, string][] = [];
|
||||
export type TraceableError = {
|
||||
shapeUid: string;
|
||||
message: string;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
export function parseStatechart(state: VisualEditorState): [Statechart, TraceableError[]] {
|
||||
const errors: TraceableError[] = [];
|
||||
|
||||
// implicitly, the root is always an Or-state
|
||||
const root: OrState = {
|
||||
|
|
@ -37,6 +43,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
|
||||
// we assume that the rountangles are sorted from big to small:
|
||||
for (const rt of state.rountangles) {
|
||||
// @ts-ignore
|
||||
const state: ConcreteState = {
|
||||
kind: rt.kind,
|
||||
uid: rt.uid,
|
||||
|
|
@ -45,11 +52,11 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
entryActions: [],
|
||||
exitActions: [],
|
||||
timers: [],
|
||||
}
|
||||
};
|
||||
if (state.kind === "or") {
|
||||
state.initial = [];
|
||||
(state as unknown as OrState).initial = [];
|
||||
}
|
||||
uid2State.set(rt.uid, state);
|
||||
uid2State.set(rt.uid, (state));
|
||||
|
||||
// iterate in reverse:
|
||||
for (let i=parentCandidates.length-1; i>=0; i--) {
|
||||
|
|
@ -57,7 +64,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
if (candidate.uid === "root" || isEntirelyWithin(rt, candidate)) {
|
||||
// found our parent :)
|
||||
const parentState = uid2State.get(candidate.uid)!;
|
||||
parentState.children.push(state);
|
||||
parentState.children.push(state as unknown as ConcreteState);
|
||||
parentCandidates.push(rt);
|
||||
parentLinks.set(rt.uid, candidate.uid);
|
||||
state.parent = parentState;
|
||||
|
|
@ -78,7 +85,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
if (!srcUID) {
|
||||
if (!tgtUID) {
|
||||
// dangling edge - todo: display error...
|
||||
errorShapes.push([arr.uid, "dangling"]);
|
||||
errors.push({shapeUid: arr.uid, message: "dangling"});
|
||||
}
|
||||
else {
|
||||
// target but no source, so we treat is as an 'initial' marking
|
||||
|
|
@ -89,13 +96,19 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
}
|
||||
else {
|
||||
// and states do not have an 'initial' state - todo: display error...
|
||||
errorShapes.push([arr.uid, "AND-state cannot have an initial state"]);
|
||||
errors.push({
|
||||
shapeUid: arr.uid,
|
||||
message: "AND-state cannot have an initial state",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!tgtUID) {
|
||||
errorShapes.push([arr.uid, "no target"]);
|
||||
errors.push({
|
||||
shapeUid: arr.uid,
|
||||
message: "no target",
|
||||
});
|
||||
}
|
||||
else {
|
||||
// add transition
|
||||
|
|
@ -116,10 +129,16 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
for (const state of uid2State.values()) {
|
||||
if (state.kind === "or") {
|
||||
if (state.initial.length > 1) {
|
||||
errorShapes.push(...state.initial.map(([uid,childState])=>[uid,"multiple initial states"] as [string, string]));
|
||||
errors.push(...state.initial.map(([uid,childState]) => ({
|
||||
shapeUid: uid,
|
||||
message: "multiple initial states",
|
||||
})));
|
||||
}
|
||||
else if (state.initial.length === 0) {
|
||||
errorShapes.push([state.uid, "no initial state"]);
|
||||
errors.push({
|
||||
shapeUid: state.uid,
|
||||
message: "no initial state",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -138,7 +157,11 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
parsed = parseLabel(text.text); // may throw
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
errorShapes.push([text.uid, e]);
|
||||
errors.push({
|
||||
shapeUid: text.uid,
|
||||
message: e.message,
|
||||
data: e,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
|
|
@ -202,10 +225,11 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
belongsToState.exitActions.push(...parsed.actions);
|
||||
}
|
||||
else {
|
||||
errorShapes.push([text.uid, {
|
||||
errors.push({
|
||||
shapeUid: text.uid,
|
||||
message: "states can only have entry/exit triggers",
|
||||
location: {start: {offset: 0}, end: {offset: text.text.length}},
|
||||
}]);
|
||||
data: {start: {offset: 0}, end: {offset: text.text.length}},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -217,10 +241,16 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
|
||||
for (const transition of uid2Transition.values()) {
|
||||
if (transition.label.length === 0) {
|
||||
errorShapes.push([transition.uid, "no label"]);
|
||||
errors.push({
|
||||
shapeUid: transition.uid,
|
||||
message: "no label",
|
||||
});
|
||||
}
|
||||
else if (transition.label.length > 1) {
|
||||
errorShapes.push([transition.uid, "multiple labels"]);
|
||||
errors.push({
|
||||
shapeUid: transition.uid,
|
||||
message: "multiple labels",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +262,7 @@ export function parseStatechart(state: VisualEditorState): [Statechart, [string,
|
|||
internalEvents,
|
||||
outputEvents,
|
||||
uid2State,
|
||||
}, errorShapes];
|
||||
}, errors];
|
||||
}
|
||||
|
||||
function findVariables(expr: Expression): Set<string> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
export type Timestamp = number; // milliseconds since begin of simulation
|
||||
export type Event = string;
|
||||
|
||||
export type RT_Event = InputEvent | TimerElapseEvent;
|
||||
|
||||
export type InputEvent = {
|
||||
kind: "input",
|
||||
name: string,
|
||||
param?: any,
|
||||
}
|
||||
|
||||
export type TimerElapseEvent = {
|
||||
kind: "timer",
|
||||
state: string,
|
||||
timeDurMs: number,
|
||||
}
|
||||
|
||||
|
||||
export type Mode = Set<string>; // set of active states
|
||||
|
||||
|
||||
export type Environment = ReadonlyMap<string, any>; // variable name -> value
|
||||
|
||||
export type RT_Statechart = {
|
||||
|
|
@ -13,7 +26,7 @@ export type RT_Statechart = {
|
|||
}
|
||||
|
||||
export type BigStepOutput = RT_Statechart & {
|
||||
outputEvents: string[],
|
||||
outputEvents: RaisedEvent[],
|
||||
};
|
||||
|
||||
export type BigStep = {
|
||||
|
|
@ -21,10 +34,16 @@ export type BigStep = {
|
|||
simtime: number,
|
||||
} & BigStepOutput;
|
||||
|
||||
// internal or output event
|
||||
export type RaisedEvent = {
|
||||
name: string,
|
||||
param?: any,
|
||||
}
|
||||
|
||||
|
||||
export type RaisedEvents = {
|
||||
internalEvents: string[];
|
||||
outputEvents: string[];
|
||||
internalEvents: RaisedEvent[];
|
||||
outputEvents: RaisedEvent[];
|
||||
};
|
||||
|
||||
// export type Timers = Map<string, number>; // transition uid -> timestamp
|
||||
|
|
@ -34,6 +53,4 @@ export const initialRaised: RaisedEvents = {
|
|||
outputEvents: [],
|
||||
};
|
||||
|
||||
export type TimerElapseEvent = { state: string; timeDurMs: number; };
|
||||
export type Timers = [number, TimerElapseEvent][];
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ tlabel = _ trigger:trigger _ guard:("[" _ guard _ "]")? _ actions:("/" _ actions
|
|||
|
||||
trigger = afterTrigger / entryTrigger / exitTrigger / eventTrigger
|
||||
|
||||
eventTrigger = event:identifier {
|
||||
return {kind: "event", event};
|
||||
eventTrigger = event:identifier param:("(" identifier ")")? {
|
||||
return {kind: "event", event, param: param ? param[1] : undefined};
|
||||
}
|
||||
|
||||
afterTrigger = "after" _ dur:durationMs {
|
||||
|
|
@ -111,8 +111,8 @@ boolean = ("true" / "false") {
|
|||
return text() === "true";
|
||||
}
|
||||
|
||||
raise = "^" _ event:identifier {
|
||||
return {kind: "raise", event};
|
||||
raise = "^" _ event:identifier param:("(" expr ")")? {
|
||||
return {kind: "raise", event, param: param ? param[1] : undefined};
|
||||
}
|
||||
|
||||
_ "whitespace"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue