149 lines
5.1 KiB
TypeScript
149 lines
5.1 KiB
TypeScript
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" })
|
|
]);
|
|
});
|
|
});
|