Files
KDL_WORK/kdl-wasm/web/tests/grl/operationCompile.test.ts
2026-06-27 08:45:38 -04:00

154 lines
4.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type {
GrlOperationDeclaration,
GrlPathDeclaration,
GrlProcedureDeclaration
} from "../../src/grl/ast/index.js";
import { parseGrl } from "../../src/grl/parser/index.js";
import {
compileOperation,
expandRunOperation,
parseProcedureRunOperationStatements
} from "../../src/grl/semantic/index.js";
const PROGRAM = `language grl 0.1
module Main
const speed v = joint(50 %)
const zone zf = fine
target home = joint_target { joints: [0 deg] }
path weld_path {
defaults { speed: v, zone: zf }
point p0 movej home
}
operation weld_op_01 {
kind: arc_welding
path: weld_path
process {
weld_id: "WELD_1"
voltage: 24.0
current: 180.0
weave: none
}
start_action:
io.do[20] = true
end_action:
io.do[20] = false
}
proc main()
run_operation weld_op_01
end
end
`;
function declarations() {
return parseGrl(PROGRAM).module.declarations;
}
function pathsByName(paths: GrlPathDeclaration[]) {
return new Map(paths.map((path) => [path.name, path]));
}
describe("GRL operation compilation", () => {
it("parses operation kind, path, process, and action blocks", () => {
const operation = declarations().find(
(decl): decl is GrlOperationDeclaration => decl.kind === "OperationDeclaration"
)!;
expect(operation).toMatchObject({
kind: "OperationDeclaration",
name: "weld_op_01",
operationKind: "arc_welding",
pathName: "weld_path",
items: [
{ kind: "OperationProcessBlock" },
{ kind: "OperationActionBlock", actionKind: "start_action" },
{ kind: "OperationActionBlock", actionKind: "end_action" }
]
});
});
it("compiles operation process metadata and action statements", () => {
const decls = declarations();
const operation = decls.find((decl): decl is GrlOperationDeclaration => decl.kind === "OperationDeclaration")!;
const paths = decls.filter((decl): decl is GrlPathDeclaration => decl.kind === "PathDeclaration");
const compiled = compileOperation(operation, pathsByName(paths));
expect(compiled).toMatchObject({
operationId: "weld_op_01",
kind: "arc_welding",
pathId: "weld_path",
process: {
weld_id: "WELD_1",
voltage: 24,
current: 180,
weave: "none"
},
startActions: [
{
kind: "ACTION",
actionKind: "start_action",
operationId: "weld_op_01",
statement: "io . do [ 20 ] = true"
}
],
endActions: [
{
kind: "ACTION",
actionKind: "end_action",
operationId: "weld_op_01",
statement: "io . do [ 20 ] = false"
}
]
});
});
it("extracts run_operation and expands to start action, path, and end action", () => {
const decls = declarations();
const procedure = decls.find((decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration")!;
const operation = decls.find((decl): decl is GrlOperationDeclaration => decl.kind === "OperationDeclaration")!;
const paths = decls.filter((decl): decl is GrlPathDeclaration => decl.kind === "PathDeclaration");
const compiled = compileOperation(operation, pathsByName(paths));
const [run] = parseProcedureRunOperationStatements(procedure);
expect(run).toEqual({
kind: "RUN_OPERATION",
operationId: "weld_op_01",
sourceMap: {
line: 25,
column: 5
}
});
expect(expandRunOperation(run!, new Map([[compiled.operationId, compiled]]))).toEqual([
expect.objectContaining({ kind: "ACTION", actionKind: "start_action" }),
{
kind: "RUN_PATH",
pathId: "weld_path",
sourceMap: {
line: 25,
column: 5
}
},
expect.objectContaining({ kind: "ACTION", actionKind: "end_action" })
]);
});
it("reports operations that reference missing paths and missing run_operation targets", () => {
const missingPathOperation = parseGrl(`language grl 0.1
module Main
operation bad_op {
kind: handling
path: missing_path
}
end
`).module.declarations.find((decl): decl is GrlOperationDeclaration => decl.kind === "OperationDeclaration")!;
expect(() => compileOperation(missingPathOperation, new Map())).toThrowError(
expect.objectContaining({ code: "GRL_OPERATION_PATH_NOT_FOUND" })
);
expect(() =>
expandRunOperation({ kind: "RUN_OPERATION", operationId: "missing_op" }, new Map())
).toThrowError(expect.objectContaining({ code: "GRL_OPERATION_NOT_FOUND" }));
});
});