import { describe, expect, it } from "vitest"; import { parseGrl } from "../../src/grl/parser/index.js"; import { compileSemanticProgram } from "../../src/grl/semantic/index.js"; const PROGRAM = `language grl 0.1 module Main const speed vj = joint(50 %) const speed vl = linear(200 mm/s) const zone zf = fine target home = joint_target { joints: [0 deg] } target pick = pose_target { pose: pose(500 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg) } path pick_path { defaults { speed: vl, zone: zf } point p0 movej home speed vj zone fine point p1 movel pick event before p1 io.do[1] = true } operation pick_op { kind: handling path: pick_path start_action: io.do[2] = true end_action: io.do[2] = false } proc set_done(out bool done) done = true end proc main(out bool done) set_speed vl set_zone zf movej home speed vj zone fine io.do[3] = true wait io.di[1] == true timeout 1 s if done == false pulse io.do[4] duration 100 ms else alarm DONE "Already done" end call set_done(done) run_path pick_path run_operation pick_op return end end `; describe("GRL semantic analyzer, executable IR, and source map", () => { it("compiles a complete program into unified executable IR and KDL bridge requests", () => { const ir = compileSemanticProgram(parseGrl(PROGRAM), { startJoints: [0], sampleTime: 0.004 }); expect(ir.moduleName).toBe("Main"); expect(ir.semanticChecks).toHaveLength(22); expect(ir.symbols).toEqual([ expect.objectContaining({ kind: "data", name: "vj", typeName: "speed" }), expect.objectContaining({ kind: "data", name: "vl", typeName: "speed" }), expect.objectContaining({ kind: "data", name: "zf", typeName: "zone" }), expect.objectContaining({ kind: "target", name: "home" }), expect.objectContaining({ kind: "target", name: "pick" }), expect.objectContaining({ kind: "path", name: "pick_path" }), expect.objectContaining({ kind: "operation", name: "pick_op" }), expect.objectContaining({ kind: "procedure", name: "set_done" }), expect.objectContaining({ kind: "procedure", name: "main" }) ]); expect(ir.paths).toHaveLength(1); expect(ir.operations).toHaveLength(1); expect(ir.kdlBridge.pathRequests).toEqual([ expect.objectContaining({ pathId: "pick_path", segments: [ expect.objectContaining({ id: "p0", motion: "MOVEJ" }), expect.objectContaining({ id: "p1", motion: "MOVEL" }) ] }) ]); expect(ir.kdlBridge.motionRequests).toEqual([ expect.objectContaining({ startJoints: [0], speed: { kind: "joint_percent", value: 0.5 }, zone: { kind: "fine" }, sampleTime: 0.004 }) ]); const main = ir.procedures.find((procedure) => procedure.name === "main")!; expect(main.instructions).toEqual([ expect.objectContaining({ kind: "MOVEJ" }), expect.objectContaining({ kind: "IO_WRITE", target: expect.objectContaining({ domain: "do", index: 3 }) }), expect.objectContaining({ kind: "WAIT", timeout: 1 }), expect.objectContaining({ kind: "EXEC_IF", branches: [ expect.objectContaining({ branchKind: "if", body: expect.arrayContaining([expect.objectContaining({ kind: "PULSE", duration: 0.1 })]) }), expect.objectContaining({ branchKind: "else", body: expect.arrayContaining([expect.objectContaining({ kind: "ALARM", alarmId: "DONE" })]) }) ] }), expect.objectContaining({ kind: "CALL", target: "set_done" }), expect.objectContaining({ kind: "RUN_PATH", pathId: "pick_path" }), expect.objectContaining({ kind: "RUN_OPERATION", operationId: "pick_op" }), expect.objectContaining({ kind: "RETURN" }) ]); }); it("exposes source map entries for GRL procedure lines, path points, and operation actions", () => { const ir = compileSemanticProgram(parseGrl(PROGRAM), { startJoints: [0], sampleTime: 0.004 }); expect(ir.sourceMap).toEqual( expect.arrayContaining([ expect.objectContaining({ kind: "path_point", pathId: "pick_path", pointId: "p0" }), expect.objectContaining({ kind: "path_point", pathId: "pick_path", pointId: "p1" }), expect.objectContaining({ kind: "operation_action", operationId: "pick_op" }), expect.objectContaining({ kind: "MOVEJ", procedureId: "main", sourceMap: expect.objectContaining({ line: 28 }) }), expect.objectContaining({ kind: "EXEC_IF", procedureId: "main" }), expect.objectContaining({ kind: "RUN_OPERATION", procedureId: "main" }) ]) ); }); it("reports duplicate symbols through semantic diagnostics", () => { const ir = compileSemanticProgram(parseGrl(`language grl 0.1 module Main const speed v = joint(10 %) const speed v = joint(20 %) proc main() end end `), { startJoints: [], sampleTime: 0.004 }); expect(ir.diagnostics).toEqual([ expect.objectContaining({ severity: "error", code: "GRL_SYMBOL_DUPLICATE" }) ]); }); });