Feature: ADL Program types walker (#586)
This commit is contained in:
Родитель
2ec0c6c455
Коммит
34ea3a669a
|
@ -46,6 +46,20 @@ import {
|
|||
UnionType,
|
||||
} from "./types.js";
|
||||
|
||||
export interface Checker {
|
||||
getTypeForNode(node: Node): Type;
|
||||
checkProgram(program: Program): void;
|
||||
getGlobalNamespaceType(): NamespaceType;
|
||||
|
||||
getLiteralType(node: StringLiteralNode): StringLiteralType;
|
||||
getLiteralType(node: NumericLiteralNode): NumericLiteralType;
|
||||
getLiteralType(node: BooleanLiteralNode): BooleanLiteralType;
|
||||
getLiteralType(node: LiteralNode): LiteralType;
|
||||
|
||||
getTypeName(type: Type): string;
|
||||
getNamespaceString(type: NamespaceType | undefined): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map keyed by a set of objects.
|
||||
*
|
||||
|
@ -92,11 +106,20 @@ interface PendingModelInfo {
|
|||
type: ModelType;
|
||||
}
|
||||
|
||||
export function createChecker(program: Program) {
|
||||
export function createChecker(program: Program): Checker {
|
||||
let templateInstantiation: Type[] = [];
|
||||
let instantiatingTemplate: Node | undefined;
|
||||
let currentSymbolId = 0;
|
||||
const symbolLinks = new Map<number, SymbolLinks>();
|
||||
const root = createType({
|
||||
kind: "Namespace",
|
||||
name: "",
|
||||
node: program.globalNamespace,
|
||||
models: new Map(),
|
||||
operations: new Map(),
|
||||
namespaces: new Map(),
|
||||
});
|
||||
|
||||
const errorType: ErrorType = { kind: "Intrinsic", name: "ErrorType" };
|
||||
|
||||
// This variable holds on to the model type that is currently
|
||||
|
@ -133,7 +156,7 @@ export function createChecker(program: Program) {
|
|||
getLiteralType,
|
||||
getTypeName,
|
||||
getNamespaceString,
|
||||
checkOperation,
|
||||
getGlobalNamespaceType,
|
||||
};
|
||||
|
||||
function getTypeForNode(node: Node): Type {
|
||||
|
@ -197,8 +220,7 @@ export function createChecker(program: Program) {
|
|||
function getNamespaceString(type: NamespaceType | undefined): string {
|
||||
if (!type) return "";
|
||||
const parent = type.namespace;
|
||||
|
||||
return parent ? `${getNamespaceString(parent)}.${type.name}` : type.name;
|
||||
return parent && parent.name !== "" ? `${getNamespaceString(parent)}.${type.name}` : type.name;
|
||||
}
|
||||
|
||||
function getEnumName(e: EnumType): string {
|
||||
|
@ -472,7 +494,8 @@ export function createChecker(program: Program) {
|
|||
function getParentNamespaceType(
|
||||
node: ModelStatementNode | NamespaceStatementNode | OperationStatementNode | EnumStatementNode
|
||||
): NamespaceType | undefined {
|
||||
if (!node.namespaceSymbol) return undefined;
|
||||
if (node === root.node) return undefined;
|
||||
if (!node.namespaceSymbol) return root;
|
||||
|
||||
const symbolLinks = getSymbolLinks(node.namespaceSymbol);
|
||||
compilerAssert(symbolLinks.type, "Parent namespace isn't typed yet.", node);
|
||||
|
@ -494,6 +517,10 @@ export function createChecker(program: Program) {
|
|||
return type;
|
||||
}
|
||||
|
||||
function getGlobalNamespaceType() {
|
||||
return root;
|
||||
}
|
||||
|
||||
function checkTupleExpression(node: TupleExpressionNode): TupleType {
|
||||
return createType({
|
||||
kind: "Tuple",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { dirname, extname, isAbsolute, resolve } from "path";
|
||||
import resolveModule from "resolve";
|
||||
import { createBinder, createSymbolTable } from "./binder.js";
|
||||
import { createChecker } from "./checker.js";
|
||||
import { Checker, createChecker } from "./checker.js";
|
||||
import { createDiagnostic, createSourceFile, DiagnosticTarget, NoTarget } from "./diagnostics.js";
|
||||
import { Message } from "./messages.js";
|
||||
import { CompilerOptions } from "./options.js";
|
||||
|
@ -30,7 +30,7 @@ export interface Program {
|
|||
sourceFiles: ADLScriptNode[];
|
||||
literalTypes: Map<string | number | boolean, LiteralType>;
|
||||
host: CompilerHost;
|
||||
checker?: ReturnType<typeof createChecker>;
|
||||
checker?: Checker;
|
||||
readonly diagnostics: readonly Diagnostic[];
|
||||
evalAdlScript(adlScript: string, filePath?: string): void;
|
||||
onBuild(cb: (program: Program) => void): Promise<void> | void;
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { assert } from "console";
|
||||
import { createTestHost, TestHost } from "../test-host.js";
|
||||
|
||||
describe("adl: global namespace", () => {
|
||||
let testHost: TestHost;
|
||||
|
||||
beforeEach(async () => {
|
||||
testHost = await createTestHost();
|
||||
});
|
||||
|
||||
describe("it adds top level entities to the global namespace", () => {
|
||||
it("adds top-level namespaces", async () => {
|
||||
testHost.addAdlFile("main.adl", `namespace Foo {}`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.namespaces.get("Foo"),
|
||||
"Namespace Foo was added to global namespace type"
|
||||
);
|
||||
});
|
||||
|
||||
it("adds top-level models", async () => {
|
||||
testHost.addAdlFile("main.adl", `model MyModel {}`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.models.get("MyModel"),
|
||||
"model MyModel was added to global namespace type"
|
||||
);
|
||||
});
|
||||
|
||||
it("adds top-level oeprations", async () => {
|
||||
testHost.addAdlFile("main.adl", `op myOperation(): string;`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.operations.get("myOperation"),
|
||||
"operation myOperation was added to global namespace type"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("it adds top level entities used in other files to the global namespace", () => {
|
||||
beforeEach(() => {
|
||||
testHost.addAdlFile(
|
||||
"main.adl",
|
||||
`
|
||||
import "./a.adl";
|
||||
|
||||
model Base {}
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it("adds top-level namespaces", async () => {
|
||||
testHost.addAdlFile("a.adl", `namespace Foo {}`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.namespaces.get("Foo"),
|
||||
"Namespace Foo was added to global namespace type"
|
||||
);
|
||||
assert(
|
||||
globalNamespaceType?.namespaces.get("Base"),
|
||||
"Should still reference main file top-level entities"
|
||||
);
|
||||
});
|
||||
|
||||
it("adds top-level models", async () => {
|
||||
testHost.addAdlFile("a.adl", `model MyModel {}`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.models.get("MyModel"),
|
||||
"model MyModel was added to global namespace type"
|
||||
);
|
||||
});
|
||||
|
||||
it("adds top-level oeprations", async () => {
|
||||
testHost.addAdlFile("a.adl", `op myOperation(): string;`);
|
||||
|
||||
await testHost.compile("./");
|
||||
|
||||
const globalNamespaceType = testHost.program.checker?.getGlobalNamespaceType();
|
||||
assert(
|
||||
globalNamespaceType?.operations.get("myOperation"),
|
||||
"operation myOperation was added to global namespace type"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -421,3 +421,32 @@ describe("adl: blockless namespaces", () => {
|
|||
strictEqual(Foo.namespaces.size, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("adl: namespace type name", () => {
|
||||
let testHost: TestHost;
|
||||
|
||||
beforeEach(async () => {
|
||||
testHost = await createTestHost();
|
||||
});
|
||||
|
||||
it("prefix with the namespace of the entity", async () => {
|
||||
testHost.addAdlFile(
|
||||
"a.adl",
|
||||
`
|
||||
namespace Foo;
|
||||
|
||||
@test()
|
||||
model Model1 {}
|
||||
|
||||
namespace Other.Bar {
|
||||
@test()
|
||||
model Model2 {}
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const { Model1, Model2 } = await testHost.compile("/a.adl");
|
||||
strictEqual(testHost.program.checker?.getTypeName(Model1), "Foo.Model1");
|
||||
strictEqual(testHost.program.checker?.getTypeName(Model2), "Foo.Other.Bar.Model2");
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче