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

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" })
]);
});
});