import { describe, expect, it } from "vitest"; import type { GrlDataDeclaration, GrlTargetDeclaration } from "../../src/grl/ast/index.js"; import { parseGrl } from "../../src/grl/parser/index.js"; import { compileGrlDataDeclaration, compileGrlTargetDeclaration, compileOffsetExpression } from "../../src/grl/semantic/index.js"; function dataDeclarations(source: string): GrlDataDeclaration[] { return parseGrl(source).module.declarations.filter( (declaration): declaration is GrlDataDeclaration => declaration.kind === "DataDeclaration" ); } function targetDeclarations(source: string): GrlTargetDeclaration[] { return parseGrl(source).module.declarations.filter( (declaration): declaration is GrlTargetDeclaration => declaration.kind === "TargetDeclaration" ); } describe("GRL data declarations and target compilation", () => { it("compiles tool and frame declarations into shared structures", () => { const [toolDecl, frameDecl] = dataDeclarations(`language grl 0.1 module Main persistent tool gripper = tool { tcp: pose(0 mm, 0 mm, 180 mm, 0 deg, 0 deg, 0 deg), mass: 2.5 kg, cog: [0 mm, 0 mm, 80 mm] } persistent frame fixture = frame { origin: pose(800 mm, 0 mm, 200 mm, 0 deg, 0 deg, 0 deg) } end `); expect(toolDecl).toMatchObject({ storage: "persistent", typeName: "tool", name: "gripper", initializer: { kind: "ObjectExpression", typeName: "tool" } }); expect(compileGrlDataDeclaration(toolDecl!)).toMatchObject({ name: "gripper", value: { tcp: { position: [0, 0, 0.18], quaternion: [0, 0, 0, 1] }, mass: 2.5, cog: [0, 0, 0.08] } }); expect(compileGrlDataDeclaration(frameDecl!)).toMatchObject({ name: "fixture", value: { origin: { position: [0.8, 0, 0.2], quaternion: [0, 0, 0, 1] } } }); }); it("compiles speed and zone declarations", () => { const declarations = dataDeclarations(`language grl 0.1 module Main const speed v_joint = joint(80 %) const speed v_pick = linear(300 mm/s) const speed v_slow = linear(100 mm/s, acc 500 mm/s2) const zone z_fine = fine const zone z10 = z(10 mm) const zone z_cnt = cnt(30) const zone z_cont = continuous end `); const compiled = declarations.map(compileGrlDataDeclaration); expect(compiled).toMatchObject([ { name: "v_joint", value: { kind: "joint_percent", value: 0.8 } }, { name: "v_pick", value: { kind: "linear", velocity: 0.3 } }, { name: "v_slow", value: { kind: "linear", velocity: 0.1, acceleration: 0.5 } }, { name: "z_fine", value: { kind: "fine" } }, { name: "z10", value: { kind: "distance", value: 0.01 } }, { name: "z_cnt", value: { kind: "cnt", value: 30 } }, { name: "z_cont", value: { kind: "continuous" } } ]); }); it("compiles joint_target and pose_target declarations", () => { const [home, pick] = targetDeclarations(`language grl 0.1 module Main target home = joint_target { joints: [0 deg, -30 deg, 60 deg, 0 deg, 60 deg, 0 deg] } target pick = pose_target { pose: pose(500 mm, 120 mm, 300 mm, 180 deg, 0 deg, 90 deg), config: robot_config(0, 0, 1), tool: gripper, frame: fixture } end `); expect(compileGrlTargetDeclaration(home!)).toMatchObject({ name: "home", target: { joints: [0, -Math.PI / 6, Math.PI / 3, 0, Math.PI / 3, 0] } }); const compiledPick = compileGrlTargetDeclaration(pick!); expect(compiledPick.name).toBe("pick"); expect("pose" in compiledPick.target).toBe(true); if ("pose" in compiledPick.target) { expect(compiledPick.target.pose.position).toEqual([0.5, 0.12, 0.3]); expect(compiledPick.target.config).toEqual({ shoulder: 0, elbow: 0, wrist: 1 }); } }); it("parses and compiles offset expressions", () => { const [declFrame, declTool] = dataDeclarations(`language grl 0.1 module Main var pose_target p2 = pick offset x 20 mm y -10 mm z 50 mm var pose_target p3 = pick offset_in tool z -50 mm end `); expect(declFrame?.initializer).toMatchObject({ kind: "OffsetExpression", mode: "frame", axes: [ { axis: "x" }, { axis: "y" }, { axis: "z" } ] }); if (declFrame?.initializer.kind === "OffsetExpression") { expect(compileOffsetExpression(declFrame.initializer)).toEqual({ mode: "frame", xyz: [0.02, -0.01, 0.05] }); } if (declTool?.initializer.kind === "OffsetExpression") { expect(compileOffsetExpression(declTool.initializer)).toEqual({ mode: "tool", xyz: [0, 0, -0.05] }); } }); });