import { describe, expect, it } from "vitest"; import type { GrlDataDeclaration, GrlProcedureDeclaration, GrlTargetDeclaration } from "../../src/grl/ast/index.js"; import { parseGrl } from "../../src/grl/parser/index.js"; import { buildMotionContext, compileMotionToKdlRequest, parseProcedureMotionInstructions } from "../../src/grl/semantic/index.js"; const PROGRAM = `language grl 0.1 module Main persistent tool gripper = tool { tcp: pose(0 mm, 0 mm, 100 mm, 0 deg, 0 deg, 0 deg), mass: 1 kg } persistent frame fixture = frame { origin: pose(800 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg) } const speed v_joint = joint(60 %) const speed v_linear = linear(300 mm/s) const zone z10 = z(10 mm) target home = joint_target { joints: [0 deg, 0 deg] } target pick = pose_target { pose: pose(500 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg), tool: gripper, frame: fixture } target mid = pose_target { pose: pose(550 mm, 50 mm, 0 mm, 0 deg, 0 deg, 0 deg) } target arc_end = pose_target { pose: pose(600 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg) } proc main() set_tool gripper set_frame fixture set_speed v_linear set_zone z10 movej home speed v_joint zone fine movel pick movec via mid target arc_end speed linear(150 mm/s) zone fine end end `; function declarations() { return parseGrl(PROGRAM).module.declarations; } describe("GRL motion instruction compilation", () => { it("parses movej, movel, and movec from procedure body tokens", () => { const decls = declarations(); const context = buildMotionContext( decls.filter( (decl): decl is GrlDataDeclaration | GrlTargetDeclaration => decl.kind === "DataDeclaration" || decl.kind === "TargetDeclaration" ) ); const procedure = decls.find((decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration")!; const instructions = parseProcedureMotionInstructions(procedure, context); expect(instructions.map((instruction) => instruction.kind)).toEqual(["MOVEJ", "MOVEL", "MOVEC"]); expect(instructions[0]).toMatchObject({ kind: "MOVEJ", speed: { kind: "joint_percent", value: 0.6 }, zone: { kind: "fine" }, target: { joints: [0, 0] }, sourceMap: { line: 32 } }); expect(instructions[1]).toMatchObject({ kind: "MOVEL", speed: { kind: "linear", velocity: 0.3 }, zone: { kind: "distance", value: 0.01 } }); expect(instructions[1]?.tool?.position).toEqual([0, 0, 0.1]); expect(instructions[1]?.frame?.position).toEqual([0.8, 0, 0]); expect(instructions[2]).toMatchObject({ kind: "MOVEC", speed: { kind: "linear", velocity: 0.15 }, zone: { kind: "fine" } }); }); it("compiles motion instructions to KDL request shapes", () => { const decls = declarations(); const context = buildMotionContext( decls.filter( (decl): decl is GrlDataDeclaration | GrlTargetDeclaration => decl.kind === "DataDeclaration" || decl.kind === "TargetDeclaration" ) ); const procedure = decls.find((decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration")!; const [movej, movel, movec] = parseProcedureMotionInstructions(procedure, context); expect(compileMotionToKdlRequest(movej!, { startJoints: [0, 0], sampleTime: 0.004 })).toMatchObject({ startJoints: [0, 0], target: { joints: [0, 0] }, speed: { kind: "joint_percent", value: 0.6 }, zone: { kind: "fine" }, sampleTime: 0.004 }); expect(compileMotionToKdlRequest(movel!, { startJoints: [0, 0], sampleTime: 0.004 })).toMatchObject({ startJoints: [0, 0], target: { pose: { position: [0.5, 0, 0] } }, speed: { kind: "linear", velocity: 0.3 }, zone: { kind: "distance", value: 0.01 }, tool: { position: [0, 0, 0.1] }, frame: { position: [0.8, 0, 0] }, sampleTime: 0.004 }); expect(compileMotionToKdlRequest(movec!, { startJoints: [0, 0], sampleTime: 0.004 })).toMatchObject({ startJoints: [0, 0], via: { pose: { position: [0.55, 0.05, 0] } }, target: { pose: { position: [0.6, 0, 0] } }, speed: { kind: "linear", velocity: 0.15 }, zone: { kind: "fine" }, sampleTime: 0.004 }); }); });