同步KDL工程源码到云仓库
This commit is contained in:
197
kdl-wasm/web/tests/grl/controlFlow.test.ts
Normal file
197
kdl-wasm/web/tests/grl/controlFlow.test.ts
Normal 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" })
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user