同步KDL工程源码到云仓库

This commit is contained in:
wangdequan
2026-06-27 08:45:38 -04:00
parent 93d8ede54b
commit 95c684fc4d
93 changed files with 25712 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
import { describe, expect, it } from "vitest";
import type { GrlProcedureDeclaration } from "../../src/grl/ast/index.js";
import { parseGrl } from "../../src/grl/parser/index.js";
import { parseProcedureControlFlow } from "../../src/grl/semantic/index.js";
function procedure(source: string): GrlProcedureDeclaration {
return parseGrl(source).module.declarations.find(
(decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration"
)!;
}
describe("GRL control-flow compilation", () => {
it("compiles if, elseif, else, while, for, switch, labels, and jumps", () => {
const proc = procedure(`language grl 0.1
module Main
proc main()
label retry
if ready == true
wait io.di[1] == true
elseif fault == true
jump recovery
else
jump retry
end
while all(io.di[1] == true, io.di[2] == false)
continue
end
for i = 1 to 3 step 1
movej home
end
switch mode
case 1
break
case 2
jump done
default
jump recovery
end
label recovery
label done
end
end
`);
const flow = parseProcedureControlFlow(proc);
expect(flow).toEqual([
expect.objectContaining({ kind: "LABEL", name: "retry", scopePath: [] }),
expect.objectContaining({
kind: "IF",
branches: [
expect.objectContaining({
branchKind: "if",
condition: expect.objectContaining({ text: "ready == true" }),
body: [expect.objectContaining({ kind: "RAW_STATEMENT", text: "wait io . di [ 1 ] == true" })]
}),
expect.objectContaining({
branchKind: "elseif",
condition: expect.objectContaining({ text: "fault == true" }),
body: [expect.objectContaining({ kind: "JUMP", label: "recovery" })]
}),
expect.objectContaining({
branchKind: "else",
body: [expect.objectContaining({ kind: "JUMP", label: "retry" })]
})
]
}),
expect.objectContaining({
kind: "WHILE",
condition: expect.objectContaining({
text: "all ( io . di [ 1 ] == true , io . di [ 2 ] == false )"
}),
body: [expect.objectContaining({ kind: "CONTINUE" })]
}),
expect.objectContaining({
kind: "FOR",
iterator: "i",
from: expect.objectContaining({ text: "1" }),
to: expect.objectContaining({ text: "3" }),
step: expect.objectContaining({ text: "1" }),
body: [expect.objectContaining({ kind: "RAW_STATEMENT", text: "movej home" })]
}),
expect.objectContaining({
kind: "SWITCH",
expression: expect.objectContaining({ text: "mode" }),
cases: [
expect.objectContaining({ caseKind: "case", value: 1, body: [expect.objectContaining({ kind: "BREAK" })] }),
expect.objectContaining({ caseKind: "case", value: 2, body: [expect.objectContaining({ kind: "JUMP", label: "done" })] }),
expect.objectContaining({ caseKind: "default", body: [expect.objectContaining({ kind: "JUMP", label: "recovery" })] })
]
}),
expect.objectContaining({ kind: "LABEL", name: "recovery", scopePath: [] }),
expect.objectContaining({ kind: "LABEL", name: "done", scopePath: [] })
]);
});
it("reports non-boolean control conditions", () => {
const proc = procedure(`language grl 0.1
module Main
proc main()
if 1
end
end
end
`);
expect(() => parseProcedureControlFlow(proc)).toThrowError(
expect.objectContaining({ code: "GRL_CONTROL_CONDITION_NOT_BOOL" })
);
});
it("reports break and continue outside valid blocks", () => {
const breakProc = procedure(`language grl 0.1
module Main
proc main()
break
end
end
`);
const continueProc = procedure(`language grl 0.1
module Main
proc main()
switch mode
case 1
continue
end
end
end
`);
expect(() => parseProcedureControlFlow(breakProc)).toThrowError(
expect.objectContaining({ code: "GRL_BREAK_OUTSIDE_FLOW" })
);
expect(() => parseProcedureControlFlow(continueProc)).toThrowError(
expect.objectContaining({ code: "GRL_CONTINUE_OUTSIDE_LOOP" })
);
});
it("reports duplicate or non-constant switch cases", () => {
const duplicateProc = procedure(`language grl 0.1
module Main
proc main()
switch mode
case 1
break
case 1
break
end
end
end
`);
const nonConstantProc = procedure(`language grl 0.1
module Main
proc main()
switch mode
case mode + 1
break
end
end
end
`);
expect(() => parseProcedureControlFlow(duplicateProc)).toThrowError(
expect.objectContaining({ code: "GRL_SWITCH_CASE_DUPLICATE" })
);
expect(() => parseProcedureControlFlow(nonConstantProc)).toThrowError(
expect.objectContaining({ code: "GRL_SWITCH_CASE_NOT_CONSTANT" })
);
});
it("reports labels that cannot be reached by jump", () => {
const intoBlockProc = procedure(`language grl 0.1
module Main
proc main()
jump inner
if ready == true
label inner
end
end
end
`);
const missingLabelProc = procedure(`language grl 0.1
module Main
proc main()
jump missing
end
end
`);
expect(() => parseProcedureControlFlow(intoBlockProc)).toThrowError(
expect.objectContaining({ code: "GRL_JUMP_INTO_BLOCK" })
);
expect(() => parseProcedureControlFlow(missingLabelProc)).toThrowError(
expect.objectContaining({ code: "GRL_LABEL_NOT_FOUND" })
);
});
});