216 lines
6.4 KiB
TypeScript
216 lines
6.4 KiB
TypeScript
import { DocumentRange } from "../types";
|
|
|
|
/** Type alias for renderer state */
|
|
export type ChefRS = {
|
|
stack: string[];
|
|
currentKitchen: {
|
|
ingredients: IngredientBox;
|
|
bowls: { [k: number]: MixingBowl };
|
|
dishes: { [k: number]: BakingDish };
|
|
};
|
|
};
|
|
|
|
/********************************
|
|
******** UTILITY ALIASES *******
|
|
********************************/
|
|
|
|
/** The name of an ingredient */
|
|
export type IngredientName = string;
|
|
|
|
/** Identifier of a mixing bowl */
|
|
export type BowlId = number;
|
|
|
|
/** Indentifier of a baking dish */
|
|
export type DishId = number;
|
|
|
|
/********************************
|
|
****** RUNTIME CONSTRUCTS ******
|
|
********************************/
|
|
|
|
/** Type of an element in a Chef stack */
|
|
export type StackItemType = "dry" | "liquid" | "unknown";
|
|
|
|
/** An element of Chef's stack constructs */
|
|
export type StackItem = { value: number; type: StackItemType };
|
|
|
|
/** Details of an ingredient - kind and value */
|
|
export type IngredientItem = {
|
|
type: StackItemType;
|
|
value?: number;
|
|
};
|
|
|
|
/** Set of ingredients (global variables) in a Chef program */
|
|
export type IngredientBox = { [k: IngredientName]: IngredientItem };
|
|
|
|
/** A mixing bowl (stack construct) in Chef */
|
|
export type MixingBowl = StackItem[];
|
|
|
|
/** A baking dish (stack construct) in Chef */
|
|
export type BakingDish = StackItem[];
|
|
|
|
/********************************
|
|
***** PROGRAM INSTRUCTIONS *****
|
|
********************************/
|
|
|
|
/** TAKE: Take numeric input from STDIN and write in ingredient `ing` */
|
|
export type StdinOp = { code: "STDIN"; ing: IngredientName };
|
|
|
|
/** PUT: Push value of ingredient `ing` into `bowlId`'th mixing bowl */
|
|
export type PushOp = { code: "PUSH"; ing: IngredientName; bowlId: BowlId };
|
|
|
|
/** FOLD: Pop value from top of `bowlId`'th mixing bowl and put in ingredient `ing` */
|
|
export type PopOp = { code: "POP"; ing: IngredientName; bowlId: BowlId };
|
|
|
|
/** ADD: Add value of `ing` to top value of bowl `bowlId` and push result onto same bowl */
|
|
export type AddOp = { code: "ADD"; ing: IngredientName; bowlId: BowlId };
|
|
|
|
/** REMOVE: Subtract value of `ing` from top value of bowl `bowlId` and push result onto same bowl */
|
|
export type SubtractOp = {
|
|
code: "SUBTRACT";
|
|
ing: IngredientName;
|
|
bowlId: BowlId;
|
|
};
|
|
|
|
/** COMBINE: Multiply value of `ing` with top value of bowl `bowlId` and push result onto same bowl */
|
|
export type MultiplyOp = {
|
|
code: "MULTIPLY";
|
|
ing: IngredientName;
|
|
bowlId: BowlId;
|
|
};
|
|
|
|
/** DIVIDE: Divide top value of bowl `bowlId` by value of `ing` and push result onto same bowl */
|
|
export type DivideOp = { code: "DIVIDE"; ing: IngredientName; bowlId: BowlId };
|
|
|
|
/** ADD DRY: Add values of all dry ingredients and push result on bowl `bowlId` */
|
|
export type AddDryOp = { code: "ADD-DRY"; bowlId: BowlId };
|
|
|
|
/** LIQUEFY: Convert ingredient `ing` to a liquid */
|
|
export type LiquefyIngOp = { code: "LIQ-ING"; ing: IngredientName };
|
|
|
|
/** LIQUEFY CONTENTS: Convert each item in bowl `bowlId` to liquid */
|
|
export type LiquefyBowlOp = { code: "LIQ-BOWL"; bowlId: BowlId };
|
|
|
|
/** STIR BOWL: Rotates top `num` items of bowl `bowlId` topwards (top ingredient goes to ~`num` position) */
|
|
export type RollStackOp = { code: "ROLL-BOWL"; bowlId: BowlId; num: number };
|
|
|
|
/** STIR ING: Rotates top [value of `ing`] items of bowl `bowlId` topwards */
|
|
export type RollIngOp = {
|
|
code: "ROLL-ING";
|
|
bowlId: BowlId;
|
|
ing: IngredientName;
|
|
};
|
|
|
|
/** MIX: Randomizes the order of items in the bowl `bowlId` */
|
|
export type RandomizeOp = { code: "RANDOM"; bowlId: BowlId };
|
|
|
|
/** CLEAN: Remove all items from the bowl `bowlId` */
|
|
export type ClearOp = { code: "CLEAR"; bowlId: BowlId };
|
|
|
|
/** POUR: Copies all items from `bowlId`'th bowl onto `dishId`'th baking dish, in the same order */
|
|
export type CopyToDishOp = { code: "COPY"; bowlId: BowlId; dishId: DishId };
|
|
|
|
/** VERB: Loop-opener, execute inner steps until `ing` is zero - then continues past loop-closer. */
|
|
export type LoopOpenOp = {
|
|
code: "LOOP-OPEN";
|
|
verb: string;
|
|
ing: IngredientName;
|
|
/** Index of corresponding loop-closing op in current method */
|
|
closer: number;
|
|
};
|
|
|
|
/** VERB: Loop-closer - also decrement value of `ing` by 1 on execution, if provided */
|
|
export type LoopCloseOp = {
|
|
code: "LOOP-CLOSE";
|
|
verb: string;
|
|
ing?: IngredientName;
|
|
/** Index of corresponding loop-opener op in current method */
|
|
opener: number;
|
|
};
|
|
|
|
/** SET ASIDE: Break out of innermost loop and continue past loop-closer */
|
|
export type LoopBreakOp = {
|
|
code: "LOOP-BREAK";
|
|
/** Index of closing op of innermost loop in current method */
|
|
closer: number;
|
|
};
|
|
|
|
/** SERVE: Run auxiliary recipe and wait until completion */
|
|
export type FnCallOp = { code: "FNCALL"; recipe: string };
|
|
|
|
/** REFRIGERATE: End recipe execution. If provided, print first `num` baking dishes */
|
|
export type EndOp = { code: "END"; num?: number };
|
|
|
|
/** Four main arithmetic operations in Chef */
|
|
export type ChefArithmeticOp = AddOp | SubtractOp | MultiplyOp | DivideOp;
|
|
|
|
/** Kitchen manipulation operations in Chef */
|
|
export type ChefKitchenOp =
|
|
| StdinOp
|
|
| PushOp
|
|
| PopOp
|
|
| ChefArithmeticOp
|
|
| AddDryOp
|
|
| LiquefyIngOp
|
|
| RollStackOp
|
|
| RollIngOp
|
|
| RandomizeOp
|
|
| ClearOp
|
|
| CopyToDishOp
|
|
| LiquefyIngOp
|
|
| LiquefyBowlOp;
|
|
|
|
/** Flow control operations in Chef */
|
|
export type ChefFlowControlOp =
|
|
| LoopOpenOp
|
|
| LoopCloseOp
|
|
| LoopBreakOp
|
|
| FnCallOp
|
|
| EndOp;
|
|
|
|
/** A single operation of a Chef recipe */
|
|
export type ChefOperation = ChefKitchenOp | ChefFlowControlOp;
|
|
|
|
/** List of codes for flow control operations in Chef */
|
|
const flowControlOpTypes: ChefFlowControlOp["code"][] = [
|
|
"LOOP-OPEN",
|
|
"LOOP-CLOSE",
|
|
"LOOP-BREAK",
|
|
"FNCALL",
|
|
"END",
|
|
];
|
|
|
|
/** Check if a Chef op is a flow control operation */
|
|
export const isFlowControlOp = (op: ChefOperation): op is ChefFlowControlOp => {
|
|
return flowControlOpTypes.includes(op.code as any);
|
|
};
|
|
|
|
/********************************
|
|
******* PROGRAM SEMANTICS ******
|
|
********************************/
|
|
|
|
/** Details about serving of recipe */
|
|
export type ChefRecipeServes = {
|
|
line: number; // Line number of the "Serves" line
|
|
num: number; // Number of servings
|
|
};
|
|
|
|
/** Chef operation with its location in code */
|
|
export type ChefOpWithLocation = {
|
|
location: DocumentRange;
|
|
op: ChefOperation;
|
|
};
|
|
|
|
/** A single Chef recipe */
|
|
export type ChefRecipe = {
|
|
name: string;
|
|
ingredients: IngredientBox;
|
|
method: ChefOpWithLocation[];
|
|
serves?: ChefRecipeServes;
|
|
};
|
|
|
|
/** A parsed Chef program */
|
|
export type ChefProgram = {
|
|
main: ChefRecipe;
|
|
auxes: { [name: string]: ChefRecipe };
|
|
};
|