interpreter initializes statechart
This commit is contained in:
parent
692c052e11
commit
b9327d2eb0
5 changed files with 171 additions and 44 deletions
|
|
@ -1,27 +1,126 @@
|
|||
import { ConcreteState, Statechart } from "./ast";
|
||||
import { Action, Expression } from "./label_ast";
|
||||
import { Environment, RaisedEvents, Mode, RT_Statechart, initialRaised } from "./runtime_types";
|
||||
|
||||
export function initialize(ast: Statechart): RT_Statechart {
|
||||
const rt_root = recursiveEnter(ast.root) as RT_OrState;
|
||||
const {mode, environment, raised} = enter(ast.root, {
|
||||
environment: new Map(),
|
||||
raised: initialRaised,
|
||||
});
|
||||
return {
|
||||
root: rt_root,
|
||||
variables: new Map(),
|
||||
mode,
|
||||
environment,
|
||||
inputEvents: [],
|
||||
...raised,
|
||||
};
|
||||
}
|
||||
|
||||
export function recursiveEnter(state: ConcreteState): RT_ConcreteState {
|
||||
type ActionScope = {
|
||||
environment: Environment,
|
||||
raised: RaisedEvents,
|
||||
};
|
||||
|
||||
export function enter(state: ConcreteState, rt: ActionScope): ({mode: Mode} & ActionScope) {
|
||||
let {environment, raised} = rt;
|
||||
for (const action of state.entryActions) {
|
||||
({environment, raised} = execAction(action, {environment, raised}));
|
||||
}
|
||||
if (state.kind === "and") {
|
||||
return {
|
||||
kind: "and",
|
||||
children: state.children.map(child => recursiveEnter(child)),
|
||||
};
|
||||
const mode: {[uid:string]: Mode} = {};
|
||||
for (const child of state.children) {
|
||||
let childMode;
|
||||
({mode: childMode, environment, raised} = enter(child, {environment, raised}));
|
||||
mode[child.uid] = childMode;
|
||||
}
|
||||
return { mode, environment, raised };
|
||||
}
|
||||
else {
|
||||
const currentState = state.initial[0][1];
|
||||
return {
|
||||
kind: "or",
|
||||
current: currentState.uid,
|
||||
current_rt: recursiveEnter(currentState),
|
||||
};
|
||||
else if (state.kind === "or") {
|
||||
const mode: {[uid:string]: Mode} = {};
|
||||
// same as AND-state, but we only enter the initial state(s)
|
||||
for (const [_, child] of state.initial) {
|
||||
let childMode;
|
||||
({mode: childMode, environment, raised} = enter(child, {environment, raised}));
|
||||
mode[child.uid] = childMode;
|
||||
return { mode, environment, raised };
|
||||
}
|
||||
}
|
||||
throw new Error("should never reach here");
|
||||
}
|
||||
|
||||
export function execAction(action: Action, rt: ActionScope): ActionScope {
|
||||
if (action.kind === "assignment") {
|
||||
const rhs = evalExpr(action.rhs, rt.environment);
|
||||
const newEnvironment = new Map(rt.environment);
|
||||
newEnvironment.set(action.lhs, rhs);
|
||||
return {
|
||||
...rt,
|
||||
environment: newEnvironment,
|
||||
};
|
||||
}
|
||||
else if (action.kind === "raise") {
|
||||
if (action.event.startsWith('_')) {
|
||||
// append to internal events
|
||||
return {
|
||||
...rt,
|
||||
raised: {
|
||||
...rt.raised,
|
||||
internalEvents: [...rt.raised.internalEvents, action.event]},
|
||||
};
|
||||
}
|
||||
else {
|
||||
// append to output events
|
||||
return {
|
||||
...rt,
|
||||
raised: {
|
||||
...rt.raised,
|
||||
outputEvents: [...rt.raised.outputEvents, action.event],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("should never reach here");
|
||||
}
|
||||
|
||||
|
||||
const UNARY_OPERATOR_MAP: Map<string, (x:any)=>any> = new Map([
|
||||
["!", x => !x],
|
||||
["-", x => -x as any],
|
||||
]);
|
||||
|
||||
const BINARY_OPERATOR_MAP: Map<string, (a:any,b:any)=>any> = new Map([
|
||||
["+", (a,b) => a+b],
|
||||
["-", (a,b) => a-b],
|
||||
["*", (a,b) => a*b],
|
||||
["/", (a,b) => a/b],
|
||||
["&&", (a,b) => a&&b],
|
||||
["||", (a,b) => a||b],
|
||||
["==", (a,b) => a==b],
|
||||
["<=", (a,b) => a<=b],
|
||||
[">=", (a,b) => a>=b],
|
||||
["<", (a,b) => a<b],
|
||||
[">", (a,b) => a>b],
|
||||
]);
|
||||
|
||||
export function evalExpr(expr: Expression, environment: Environment): any {
|
||||
if (expr.kind === "literal") {
|
||||
return expr.value;
|
||||
}
|
||||
else if (expr.kind === "ref") {
|
||||
const found = environment.get(expr.variable);
|
||||
if (found === undefined) {
|
||||
throw new Error(`variable '${expr.variable}' does not exist in environment`)
|
||||
}
|
||||
return found;
|
||||
}
|
||||
else if (expr.kind === "unaryExpr") {
|
||||
const arg = evalExpr(expr.expr, environment);
|
||||
return UNARY_OPERATOR_MAP.get(expr.operator)!(arg);
|
||||
}
|
||||
else if (expr.kind === "binaryExpr") {
|
||||
const argA = evalExpr(expr.lhs, environment);
|
||||
const argB = evalExpr(expr.rhs, environment);
|
||||
return BINARY_OPERATOR_MAP.get(expr.operator)!(argA,argB);
|
||||
}
|
||||
throw new Error("should never reach here");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue