toolbar buttons to select shape

This commit is contained in:
Joeri Exelmans 2025-10-14 18:41:03 +02:00
parent a73d51a31a
commit 5ffa084516
14 changed files with 367 additions and 239 deletions

View file

@ -1,4 +1,4 @@
import { Action, TransitionLabel } from "./label_ast";
import { Action, EventTrigger, TransitionLabel } from "./label_ast";
export type AbstractState = {
uid: string;
@ -37,8 +37,8 @@ export type Statechart = {
variables: Set<string>;
inputEvents: Set<string>;
internalEvents: Set<string>;
inputEvents: EventTrigger[];
internalEvents: EventTrigger[];
outputEvents: Set<string>;
uid2State: Map<string, ConcreteState>;
@ -60,8 +60,8 @@ export const emptyStatechart: Statechart = {
root: emptyRoot,
transitions: new Map(),
variables: new Set(),
inputEvents: new Set(),
internalEvents: new Set(),
inputEvents: [],
internalEvents: [],
outputEvents: new Set(),
uid2State: new Map([["root", emptyRoot]]),
};

View file

@ -29,6 +29,7 @@ export function evalExpr(expr: Expression, environment: Environment): any {
else if (expr.kind === "ref") {
const found = environment.get(expr.variable);
if (found === undefined) {
console.log({environment});
throw new Error(`variable '${expr.variable}' does not exist in environment`);
}
return found;

View file

@ -1,10 +1,10 @@
import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox, isEntirelyWithin } from "../VisualEditor/geometry";
import { Rect2D, Vec2D, Line2D, euclideanDistance, intersectLines, isWithin, lineBBox } from "../VisualEditor/geometry";
import { ARROW_SNAP_THRESHOLD, TEXT_SNAP_THRESHOLD } from "../VisualEditor/parameters";
import { sides } from "../VisualEditor/VisualEditor";
export type Rountangle = {
uid: string;
kind: "and" | "or";
kind: "and" | "or" | "pseudo";
} & Rect2D;
export type Text = {

View file

@ -238,10 +238,10 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec
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);
oldValue = environment.get(event.param);
environment = new Map([
...environment,
[(t.label[0].trigger as EventTrigger).paramName as string, event.param.value],
[(t.label[0].trigger as EventTrigger).paramName as string, event.param],
]);
}
({mode, environment, ...raised} = fireTransition(simtime, t, arena, srcPath, tgtPath, {mode, environment, ...raised}));
@ -251,6 +251,7 @@ export function handleEvent(simtime: number, event: RT_Event, statechart: Statec
...environment,
[(t.label[0].trigger as EventTrigger).paramName as string, oldValue],
]);
console.log('restored environment:', environment);
}
arenasFired.add(arena);
}

View file

@ -231,7 +231,7 @@ function peg$parse(input, options) {
};
}
function peg$f1(event, param) {
return {kind: "event", event, param: param ? param[1] : undefined};
return {kind: "event", event, paramName: param ? param[1] : undefined};
}
function peg$f2(dur) {
return {kind: "after", durationMs: dur};

View file

@ -1,7 +1,7 @@
import { ConcreteState, OrState, Statechart, Transition } from "./abstract_syntax";
import { findNearestArrow, findNearestRountangleSide, findRountangle, Rountangle, VisualEditorState } from "./concrete_syntax";
import { isEntirelyWithin } from "../VisualEditor/geometry";
import { Action, Expression, ParsedText } from "./label_ast";
import { Action, EventTrigger, Expression, ParsedText } from "./label_ast";
import { parse as parseLabel, SyntaxError } from "./label_parser";
@ -11,6 +11,24 @@ export type TraceableError = {
data?: any;
}
function addEvent(events: EventTrigger[], e: EventTrigger, textUid: string) {
const haveEvent = events.find(({event}) => event === e.event);
if (haveEvent) {
if (haveEvent.paramName !== e.paramName === undefined) {
return [{
shapeUid: textUid,
message: "inconsistent event parameter",
}];
}
return [];
}
else {
events.push(e);
events.sort((a,b) => a.event.localeCompare(b.event));
return [];
}
}
export function parseStatechart(state: VisualEditorState): [Statechart, TraceableError[]] {
const errors: TraceableError[] = [];
@ -144,9 +162,9 @@ export function parseStatechart(state: VisualEditorState): [Statechart, Traceabl
}
let variables = new Set<string>();
const inputEvents = new Set<string>();
const inputEvents: EventTrigger[] = [];
const internalEvents: EventTrigger[] = [];
const outputEvents = new Set<string>();
const internalEvents = new Set<string>();
// step 3: figure out labels
@ -176,31 +194,35 @@ export function parseStatechart(state: VisualEditorState): [Statechart, Traceabl
if (belongsToTransition) {
// parse as transition label
belongsToTransition.label.push(parsed);
// collect events
// triggers
if (parsed.trigger.kind === "event") {
const {event} = parsed.trigger;
if (event.startsWith("_")) {
internalEvents.add(event);
errors.push(...addEvent(internalEvents, parsed.trigger, parsed.uid));
}
else {
inputEvents.add(event);
errors.push(...addEvent(inputEvents, parsed.trigger, parsed.uid));
}
}
else if (parsed.trigger.kind === "after") {
belongsToTransition.src.timers.push(parsed.trigger.durationMs);
belongsToTransition.src.timers.sort();
}
for (const action of parsed.actions) {
if (action.kind === "raise") {
const {event} = action;
if (event.startsWith("_")) {
internalEvents.add(event);
}
else {
outputEvents.add(event);
}
}
}
// // raise-actions
// for (const action of parsed.actions) {
// if (action.kind === "raise") {
// const {event} = action;
// if (event.startsWith("_")) {
// internalEvents.add(event);
// }
// else {
// outputEvents.add(event);
// }
// }
// }
// collect variables
variables = variables
.union(findVariables(parsed.guard));

View file

@ -19,6 +19,28 @@ export type Mode = Set<string>; // set of active states
export type Environment = ReadonlyMap<string, any>; // variable name -> value
// export class Environment {
// env: Map<string, any>[];
// constructor(env = [new Map()]) {
// this.env = env;
// }
// with(key: string, value: any): Environment {
// for (let i=0; i<this.env.length; i++) {
// if (this.env[i].has(key)) {
// return new Environment(this.env.with(i, new Map([
// ...this.env[i].entries(),
// [key, value],
// ])));
// }
// }
// return new Environment(this.env.with(-1, new Map([
// ...this.env[this.env.length-1].entries(),
// [key, value],
// ])));
// }
// }
export type RT_Statechart = {
mode: Mode;
environment: Environment;

View file

@ -12,7 +12,7 @@ tlabel = _ trigger:trigger _ guard:("[" _ guard _ "]")? _ actions:("/" _ actions
trigger = afterTrigger / entryTrigger / exitTrigger / eventTrigger
eventTrigger = event:identifier param:("(" identifier ")")? {
return {kind: "event", event, param: param ? param[1] : undefined};
return {kind: "event", event, paramName: param ? param[1] : undefined};
}
afterTrigger = "after" _ dur:durationMs {