statebuddy/src/App/ShowAST.tsx
2025-10-20 16:29:48 +02:00

113 lines
4 KiB
TypeScript

import { ConcreteState, PseudoState, stateDescription, Transition } from "../statecharts/abstract_syntax";
import { Action, EventTrigger, Expression } from "../statecharts/label_ast";
import { RT_Statechart } from "../statecharts/runtime_types";
import "./AST.css";
export function ShowTransition(props: {transition: Transition}) {
return <> {stateDescription(props.transition.tgt)}</>;
}
export function ShowExpr(props: {expr: Expression}) {
if (props.expr.kind === "literal") {
return <>{props.expr.value}</>;
}
else if (props.expr.kind === "ref") {
return <>{props.expr.variable}</>;
}
else if (props.expr.kind === "unaryExpr") {
return <>{props.expr.operator}<ShowExpr expr={props.expr.expr}/></>;
}
else if (props.expr.kind === "binaryExpr") {
return <><ShowExpr expr={props.expr.lhs}/>{props.expr.operator}<ShowExpr expr={props.expr.rhs}/></>;
}
}
export function ShowAction(props: {action: Action}) {
if (props.action.kind === "raise") {
return <>^<span className="outputEvent">{props.action.event}</span></>;
}
else if (props.action.kind === "assignment") {
return <>{props.action.lhs} = <ShowExpr expr={props.action.rhs}/>;</>;
}
}
export function ShowAST(props: {root: ConcreteState | PseudoState, transitions: Map<string, Transition[]>, rt: RT_Statechart | undefined, highlightActive: Set<string>}) {
const description = stateDescription(props.root);
// const outgoing = props.transitions.get(props.root.uid) || [];
return <li>{props.root.kind}: {description}
{props.root.kind !== "pseudo" && props.root.children.length>0 &&
<ul>
{props.root.children.map(child =>
<ShowAST key={child.uid} root={child} transitions={props.transitions} rt={props.rt} highlightActive={props.highlightActive} />
)}
</ul>
}
</li>;
return <details open={true} className={"stateTree" + (props.highlightActive.has(props.root.uid) ? " active" : "")}>
<summary>{props.root.kind}: {description}</summary>
{/* {props.root.kind !== "pseudo" && props.root.entryActions.length>0 &&
props.root.entryActions.map(action =>
<div>&emsp;entry / <ShowAction action={action}/></div>
)
}
{props.root.kind !== "pseudo" && props.root.exitActions.length>0 &&
props.root.exitActions.map(action =>
<div>&emsp;exit / <ShowAction action={action}/></div>
)
} */}
{props.root.kind !== "pseudo" && props.root.children.length>0 &&
props.root.children.map(child =>
<ShowAST key={child.uid} root={child} transitions={props.transitions} rt={props.rt} highlightActive={props.highlightActive} />
)
}
{/* {outgoing.length>0 &&
outgoing.map(transition => <>&emsp;<ShowTransition transition={transition}/><br/></>)
} */}
</details>;
}
import BoltIcon from '@mui/icons-material/Bolt';
export function ShowInputEvents({inputEvents, onRaise, disabled}: {inputEvents: EventTrigger[], onRaise: (e: string, p: any) => void, disabled: boolean}) {
return inputEvents.map(({event, paramName}) =>
<div key={event+'/'+paramName} className="toolbarGroup">
<button
className="inputEvent"
title={`raise this input event`}
disabled={disabled}
onClick={() => {
// @ts-ignore
const param = document.getElementById(`input-${event}-param`)?.value;
let paramParsed;
try {
if (param) {
paramParsed = JSON.parse(param); // may throw
}
}
catch (e) {
alert("invalid json");
return;
}
onRaise(event, paramParsed);
}}>
<BoltIcon fontSize="small"/>
{event}
</button>
{paramName &&
<><input id={`input-${event}-param`} style={{width: 20}} placeholder={paramName}/></>
}
&nbsp;
</div>
)
}
export function ShowOutputEvents(props: {outputEvents: Set<string>}) {
return [...props.outputEvents].map(eventName => {
return <><div className="outputEvent">{eventName}</div> </>;
});
}