同步KDL工程源码到云仓库
This commit is contained in:
201
kdl-wasm/web/tests/grl/procFunction.test.ts
Normal file
201
kdl-wasm/web/tests/grl/procFunction.test.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { GrlFunctionDeclaration, GrlProcedureDeclaration } from "../../src/grl/ast/index.js";
|
||||
import { parseGrl } from "../../src/grl/parser/index.js";
|
||||
import { analyzeProcFunctionSemantics } from "../../src/grl/semantic/index.js";
|
||||
|
||||
function declarations(source: string) {
|
||||
return parseGrl(source).module.declarations;
|
||||
}
|
||||
|
||||
describe("GRL proc, func, call, return, and scope semantics", () => {
|
||||
it("parses function declarations and analyzes proc/func signatures, calls, returns, and warnings", () => {
|
||||
const decls = declarations(`language grl 0.1
|
||||
module Main
|
||||
var int global_count = 0
|
||||
proc read_sensor(out bool ok)
|
||||
ok = true
|
||||
end
|
||||
proc main(in bool start, out bool done, inout int count)
|
||||
call read_sensor(done)
|
||||
call helper(count)
|
||||
call self_check()
|
||||
return
|
||||
end
|
||||
proc self_check()
|
||||
call self_check()
|
||||
end
|
||||
func int helper(inout int value)
|
||||
var int global_count = 1
|
||||
return value
|
||||
end
|
||||
end
|
||||
`);
|
||||
const func = decls.find((decl): decl is GrlFunctionDeclaration => decl.kind === "FunctionDeclaration")!;
|
||||
|
||||
expect(func).toMatchObject({
|
||||
kind: "FunctionDeclaration",
|
||||
returnType: "int",
|
||||
name: "helper",
|
||||
bodyTokens: [
|
||||
{ raw: "var" },
|
||||
{ raw: "int" },
|
||||
{ raw: "global_count" },
|
||||
{ raw: "=" },
|
||||
{ raw: "1" },
|
||||
{ raw: "return" },
|
||||
{ raw: "value" }
|
||||
]
|
||||
});
|
||||
|
||||
const analysis = analyzeProcFunctionSemantics(decls);
|
||||
|
||||
expect(analysis.procedures).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "read_sensor",
|
||||
parameters: [expect.objectContaining({ name: "ok", typeName: "bool", direction: "out" })]
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "main",
|
||||
parameters: [
|
||||
expect.objectContaining({ name: "start", typeName: "bool", direction: "in" }),
|
||||
expect.objectContaining({ name: "done", typeName: "bool", direction: "out" }),
|
||||
expect.objectContaining({ name: "count", typeName: "int", direction: "inout" })
|
||||
]
|
||||
}),
|
||||
expect.objectContaining({ name: "self_check", parameters: [] })
|
||||
]);
|
||||
expect(analysis.functions).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "helper",
|
||||
returnType: "int",
|
||||
parameters: [expect.objectContaining({ name: "value", typeName: "int", direction: "inout" })]
|
||||
})
|
||||
]);
|
||||
expect(analysis.calls).toEqual([
|
||||
expect.objectContaining({ kind: "CALL", target: "read_sensor", args: [expect.objectContaining({ text: "done" })] }),
|
||||
expect.objectContaining({ kind: "CALL", target: "helper", args: [expect.objectContaining({ text: "count" })] }),
|
||||
expect.objectContaining({ kind: "CALL", target: "self_check", args: [] }),
|
||||
expect.objectContaining({ kind: "CALL", target: "self_check", args: [] })
|
||||
]);
|
||||
expect(analysis.returns).toEqual([
|
||||
expect.objectContaining({ kind: "RETURN" }),
|
||||
expect.objectContaining({ kind: "RETURN", value: expect.objectContaining({ text: "value" }) })
|
||||
]);
|
||||
expect(analysis.diagnostics).toEqual([
|
||||
expect.objectContaining({ severity: "warning", code: "GRL_RECURSIVE_CALL" }),
|
||||
expect.objectContaining({ severity: "warning", code: "GRL_NAME_SHADOWS_OUTER_SCOPE" })
|
||||
]);
|
||||
});
|
||||
|
||||
it("reports out parameters that are not assigned on all normal return paths", () => {
|
||||
const decls = declarations(`language grl 0.1
|
||||
module Main
|
||||
proc main(out bool done)
|
||||
if ready == true
|
||||
done = true
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
`);
|
||||
|
||||
expect(() => analyzeProcFunctionSemantics(decls)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_OUT_PARAM_NOT_ASSIGNED" })
|
||||
);
|
||||
});
|
||||
|
||||
it("reports out and inout call arguments that are not lvalues", () => {
|
||||
const decls = declarations(`language grl 0.1
|
||||
module Main
|
||||
proc set_done(out bool done)
|
||||
done = true
|
||||
end
|
||||
proc main()
|
||||
call set_done(true)
|
||||
end
|
||||
end
|
||||
`);
|
||||
|
||||
expect(() => analyzeProcFunctionSemantics(decls)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_ARGUMENT_NOT_LVALUE" })
|
||||
);
|
||||
});
|
||||
|
||||
it("reports missing or incompatible function returns", () => {
|
||||
const missingReturn = declarations(`language grl 0.1
|
||||
module Main
|
||||
func int bad(in bool ready)
|
||||
if ready == true
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
`);
|
||||
const wrongReturn = declarations(`language grl 0.1
|
||||
module Main
|
||||
func bool bad()
|
||||
return 1
|
||||
end
|
||||
end
|
||||
`);
|
||||
|
||||
expect(() => analyzeProcFunctionSemantics(missingReturn)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_FUNC_MISSING_RETURN" })
|
||||
);
|
||||
expect(() => analyzeProcFunctionSemantics(wrongReturn)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_RETURN_TYPE_MISMATCH" })
|
||||
);
|
||||
});
|
||||
|
||||
it("reports illegal function side effects and procedure return values", () => {
|
||||
const functionSideEffect = declarations(`language grl 0.1
|
||||
module Main
|
||||
func bool bad()
|
||||
wait io.di[1] == true
|
||||
return true
|
||||
end
|
||||
end
|
||||
`);
|
||||
const procedureReturnValue = declarations(`language grl 0.1
|
||||
module Main
|
||||
proc main()
|
||||
return true
|
||||
end
|
||||
end
|
||||
`);
|
||||
|
||||
expect(() => analyzeProcFunctionSemantics(functionSideEffect)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_FUNC_SIDE_EFFECT" })
|
||||
);
|
||||
expect(() => analyzeProcFunctionSemantics(procedureReturnValue)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_RETURN_VALUE_IN_PROC" })
|
||||
);
|
||||
});
|
||||
|
||||
it("reports call target and argument type errors", () => {
|
||||
const missingCall = declarations(`language grl 0.1
|
||||
module Main
|
||||
proc main()
|
||||
call missing()
|
||||
end
|
||||
end
|
||||
`);
|
||||
const typeMismatch = declarations(`language grl 0.1
|
||||
module Main
|
||||
proc expects_int(in int value)
|
||||
return
|
||||
end
|
||||
proc main()
|
||||
call expects_int("bad")
|
||||
end
|
||||
end
|
||||
`);
|
||||
|
||||
expect(() => analyzeProcFunctionSemantics(missingCall)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_CALL_TARGET_NOT_FOUND" })
|
||||
);
|
||||
expect(() => analyzeProcFunctionSemantics(typeMismatch)).toThrowError(
|
||||
expect.objectContaining({ code: "GRL_CALL_ARGUMENT_TYPE" })
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user