Build workspace agent, capability graph, and versioned cutovers
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
type InterfaceOperation,
|
||||
type InterfaceOperationMode,
|
||||
type InterfaceRevision,
|
||||
type AtomDefinition,
|
||||
type PackageExport,
|
||||
type PackageReceiverRequirement,
|
||||
type PackageRevision,
|
||||
@@ -43,15 +44,19 @@ import { QuixosCapabilityParser,
|
||||
type ConformanceDeclContext,
|
||||
type DependencyBindingBlockContext,
|
||||
type DependencyPortContext,
|
||||
type DocumentContext,
|
||||
type EdgeDeclContext,
|
||||
type EdgeEndpointContext,
|
||||
type InterfaceDeclContext,
|
||||
type ExternalAtomDeclContext,
|
||||
type ExternalInterfaceDeclContext,
|
||||
type InterfaceResourceDeclContext,
|
||||
type PackageConstructorExportContext,
|
||||
type PackageDeclContext,
|
||||
type PackageResourceDeclContext,
|
||||
type PackageFunctionExportContext,
|
||||
type PackageOperationExportContext,
|
||||
type OperationMemberContext,
|
||||
type SourceBlockContext,
|
||||
type ResourceImportDeclContext,
|
||||
type ResourcePreambleContext,
|
||||
type StateDeclContext,
|
||||
type TargetConstraintContext,
|
||||
type ValueMemberContext,
|
||||
@@ -61,6 +66,50 @@ import { QuixosCapabilityParser,
|
||||
type WorkspaceDeclContext,
|
||||
} from "./generated/QuixosCapabilityParser.js";
|
||||
|
||||
export type CapabilityResourceKind = "interface" | "package";
|
||||
|
||||
export type CapabilityResourceImport = {
|
||||
kind: CapabilityResourceKind;
|
||||
binding: string;
|
||||
};
|
||||
|
||||
export type CapabilityExternalInterface = {
|
||||
binding: string;
|
||||
revisionId: InterfaceRevision["revisionId"];
|
||||
};
|
||||
|
||||
export type CapabilityImportEnvironment = {
|
||||
interfaces?: ReadonlyMap<string, InterfaceRevision>;
|
||||
packages?: ReadonlyMap<string, PackageRevision>;
|
||||
/** Complete resolved closure; direct maps above provide local authoring names. */
|
||||
interfaceClosure?: readonly InterfaceRevision[];
|
||||
packageClosure?: readonly PackageRevision[];
|
||||
/** External nominal atoms required by imported resources. */
|
||||
externalAtoms?: readonly AtomDefinition[];
|
||||
/** External nominal interfaces supplied by the importing workspace. */
|
||||
externalInterfaces?: readonly CapabilityExternalInterface[];
|
||||
};
|
||||
|
||||
export type CapabilityResource =
|
||||
| {
|
||||
kind: "interface";
|
||||
imports: CapabilityResourceImport[];
|
||||
externalAtoms: AtomDefinition[];
|
||||
externalInterfaces: CapabilityExternalInterface[];
|
||||
revision: InterfaceRevision;
|
||||
}
|
||||
| {
|
||||
kind: "package";
|
||||
imports: CapabilityResourceImport[];
|
||||
externalAtoms: AtomDefinition[];
|
||||
externalInterfaces: CapabilityExternalInterface[];
|
||||
revision: PackageRevision;
|
||||
};
|
||||
|
||||
export type CapabilityResourceCompileResult =
|
||||
| { ok: true; resource: CapabilityResource; diagnostics: [] }
|
||||
| { ok: false; diagnostics: CapabilitySourceDiagnostic[] };
|
||||
|
||||
export type CapabilitySourceDiagnosticPhase =
|
||||
| "syntax"
|
||||
| "lowering"
|
||||
@@ -81,6 +130,7 @@ export type CapabilitySourceCompileResult =
|
||||
ok: true;
|
||||
workspace: WorkspaceRevision;
|
||||
plan: CompiledWorkspaceRevision;
|
||||
imports: CapabilityResourceImport[];
|
||||
diagnostics: [];
|
||||
}
|
||||
| {
|
||||
@@ -90,6 +140,7 @@ export type CapabilitySourceCompileResult =
|
||||
|
||||
interface InterfaceSymbol {
|
||||
revisionId: InterfaceRevision["revisionId"];
|
||||
contractAvailable: boolean;
|
||||
members: Map<
|
||||
string,
|
||||
{
|
||||
@@ -220,11 +271,6 @@ const requireSymbol = <Value>(
|
||||
return value;
|
||||
};
|
||||
|
||||
const lowerSource = (context: SourceBlockContext): SourceRevision => ({
|
||||
repository: stringValue(context.stringLiteral(0)),
|
||||
commit: stringValue(context.stringLiteral(1)),
|
||||
});
|
||||
|
||||
const lowerCardinality = (
|
||||
context: { getText(): string } | null,
|
||||
): EdgeCardinality => text(context) as EdgeCardinality;
|
||||
@@ -534,7 +580,8 @@ const lowerOperationMember = (
|
||||
|
||||
const lowerInterface = (
|
||||
state: LoweringState,
|
||||
context: InterfaceDeclContext,
|
||||
context: InterfaceResourceDeclContext,
|
||||
source: SourceRevision,
|
||||
): InterfaceRevision => {
|
||||
const alias = identifier(context.identifier());
|
||||
const members = context.interfaceMember().flatMap((entry) => {
|
||||
@@ -572,7 +619,7 @@ const lowerInterface = (
|
||||
interfaceId: capabilityId.interface(stringValue(context.stringLiteral(0))),
|
||||
revisionId: symbol.revisionId,
|
||||
displayName: alias,
|
||||
source: lowerSource(context.sourceBlock()),
|
||||
source,
|
||||
members,
|
||||
};
|
||||
};
|
||||
@@ -652,12 +699,21 @@ const lowerDependencyPort = (
|
||||
context,
|
||||
"interface",
|
||||
);
|
||||
if (target && !target.contractAvailable) {
|
||||
loweringIssue(
|
||||
state,
|
||||
context,
|
||||
"nominal-interface-port",
|
||||
`Interface port ${name} needs the full ${targetName} contract; use import interface ${targetName}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
return target
|
||||
? {
|
||||
id,
|
||||
displayName: name,
|
||||
requirement: {
|
||||
kind: "receiver-interface",
|
||||
kind: "interface",
|
||||
interfaceRevisionId: target.revisionId,
|
||||
},
|
||||
}
|
||||
@@ -815,7 +871,8 @@ const lowerPackageConstructor = (
|
||||
|
||||
const lowerPackage = (
|
||||
state: LoweringState,
|
||||
context: PackageDeclContext,
|
||||
context: PackageResourceDeclContext,
|
||||
source: SourceRevision,
|
||||
): PackageRevision => {
|
||||
const alias = identifier(context.identifier());
|
||||
const exports = context.packageExport().map((exportContext) => {
|
||||
@@ -837,7 +894,7 @@ const lowerPackage = (
|
||||
packageId: capabilityId.package(stringValue(context.stringLiteral(0))),
|
||||
revisionId: state.packages.get(alias)!.revisionId,
|
||||
displayName: alias,
|
||||
source: lowerSource(context.sourceBlock()),
|
||||
source,
|
||||
exports,
|
||||
};
|
||||
};
|
||||
@@ -1064,7 +1121,7 @@ const lowerBoundDependencies = (
|
||||
binding: { kind: "state", slotId: attachment.attachment.id, ...(via ? { via } : {}) },
|
||||
}];
|
||||
}
|
||||
if (entry.EDGE(0)) {
|
||||
if (entry.EDGE(0) && !entry.INTERFACE()) {
|
||||
const attachmentName = identifier(entry.identifier(1));
|
||||
const projectionName = identifier(entry.identifier(2));
|
||||
const attachment = requireSymbol(
|
||||
@@ -1119,13 +1176,18 @@ const lowerBoundDependencies = (
|
||||
entry,
|
||||
"interface",
|
||||
);
|
||||
const via = entry.VIA()
|
||||
? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3)))
|
||||
: undefined;
|
||||
if (entry.VIA() && !via) return [];
|
||||
return interfaceSymbol
|
||||
? [
|
||||
{
|
||||
portId,
|
||||
binding: {
|
||||
kind: "receiver-interface",
|
||||
kind: "interface",
|
||||
interfaceRevisionId: interfaceSymbol.revisionId,
|
||||
...(via ? { via } : {}),
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1349,10 +1411,117 @@ const lowerConformance = (
|
||||
};
|
||||
};
|
||||
|
||||
const interfaceSymbolFor = (revision: InterfaceRevision): InterfaceSymbol => ({
|
||||
revisionId: revision.revisionId,
|
||||
contractAvailable: true,
|
||||
members: new Map(revision.members.map((member) => [
|
||||
member.displayName,
|
||||
{
|
||||
memberId: member.id,
|
||||
operations: new Map(member.operations.map((operation) => [
|
||||
operation.displayName,
|
||||
operation.id,
|
||||
])),
|
||||
},
|
||||
])),
|
||||
});
|
||||
|
||||
const packageSymbolFor = (revision: PackageRevision): PackageSymbol => ({
|
||||
revisionId: revision.revisionId,
|
||||
exports: new Map(revision.exports.map((entry) => [
|
||||
entry.displayName,
|
||||
{
|
||||
exportId: entry.id,
|
||||
ports: new Map(entry.dependencyPorts.map((port) => [
|
||||
port.displayName,
|
||||
port.id,
|
||||
])),
|
||||
},
|
||||
])),
|
||||
});
|
||||
|
||||
const resourceImport = (
|
||||
context: ResourceImportDeclContext,
|
||||
): CapabilityResourceImport => ({
|
||||
kind: context.INTERFACE() ? "interface" : "package",
|
||||
binding: identifier(context.identifier()),
|
||||
});
|
||||
|
||||
const registerImports = (
|
||||
state: LoweringState,
|
||||
contexts: readonly ResourceImportDeclContext[],
|
||||
environment: CapabilityImportEnvironment,
|
||||
) => contexts.map((context) => {
|
||||
const imported = resourceImport(context);
|
||||
if (imported.kind === "interface") {
|
||||
const revision = environment.interfaces?.get(imported.binding);
|
||||
if (!revision) {
|
||||
loweringIssue(
|
||||
state,
|
||||
context,
|
||||
"missing-import",
|
||||
`No resolved interface is available for lock binding ${imported.binding}`,
|
||||
);
|
||||
} else {
|
||||
declareSymbol(
|
||||
state,
|
||||
state.interfaces,
|
||||
imported.binding,
|
||||
interfaceSymbolFor(revision),
|
||||
context,
|
||||
"interface",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const revision = environment.packages?.get(imported.binding);
|
||||
if (!revision) {
|
||||
loweringIssue(
|
||||
state,
|
||||
context,
|
||||
"missing-import",
|
||||
`No resolved package is available for lock binding ${imported.binding}`,
|
||||
);
|
||||
} else {
|
||||
declareSymbol(
|
||||
state,
|
||||
state.packages,
|
||||
imported.binding,
|
||||
packageSymbolFor(revision),
|
||||
context,
|
||||
"package",
|
||||
);
|
||||
}
|
||||
}
|
||||
return imported;
|
||||
});
|
||||
|
||||
const uniqueExactRevisions = <Revision extends {
|
||||
revisionId: string;
|
||||
source: SourceRevision;
|
||||
}>(revisions: readonly Revision[]): Revision[] => {
|
||||
const seen = new Set<string>();
|
||||
return revisions.filter((revision) => {
|
||||
const key = `${revision.revisionId}\0${revision.source.repository}\0${revision.source.commit}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
const uniqueAtoms = (atoms: readonly AtomDefinition[]): AtomDefinition[] => {
|
||||
const seen = new Set<string>();
|
||||
return atoms.filter((atom) => {
|
||||
if (seen.has(atom.id)) return false;
|
||||
seen.add(atom.id);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
const lowerWorkspace = (
|
||||
state: LoweringState,
|
||||
context: WorkspaceDeclContext,
|
||||
): WorkspaceRevision => {
|
||||
environment: CapabilityImportEnvironment,
|
||||
): { workspace: WorkspaceRevision; imports: CapabilityResourceImport[] } => {
|
||||
const items = context.workspaceItem();
|
||||
|
||||
for (const item of items) {
|
||||
@@ -1368,41 +1537,14 @@ const lowerWorkspace = (
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const interfaceContext = item.interfaceDecl();
|
||||
if (interfaceContext) {
|
||||
declareSymbol(
|
||||
state,
|
||||
state.interfaces,
|
||||
identifier(interfaceContext.identifier()),
|
||||
{
|
||||
revisionId: capabilityId.interfaceRevision(
|
||||
stringValue(interfaceContext.stringLiteral(1)),
|
||||
),
|
||||
members: new Map(),
|
||||
},
|
||||
interfaceContext,
|
||||
"interface",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const packageContext = item.packageDecl();
|
||||
if (packageContext) {
|
||||
declareSymbol(
|
||||
state,
|
||||
state.packages,
|
||||
identifier(packageContext.identifier()),
|
||||
{
|
||||
revisionId: capabilityId.packageRevision(
|
||||
stringValue(packageContext.stringLiteral(1)),
|
||||
),
|
||||
exports: new Map(),
|
||||
},
|
||||
packageContext,
|
||||
"package",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const imports = registerImports(
|
||||
state,
|
||||
items.flatMap((item) => item.resourceImportDecl() ?? []),
|
||||
environment,
|
||||
);
|
||||
|
||||
const atoms = items.flatMap((item) => {
|
||||
const atom = item.atomDecl();
|
||||
if (!atom) {
|
||||
@@ -1422,15 +1564,15 @@ const lowerWorkspace = (
|
||||
: [];
|
||||
});
|
||||
|
||||
const interfaceImports = items.flatMap((item) => {
|
||||
const interfaceContext = item.interfaceDecl();
|
||||
return interfaceContext ? [lowerInterface(state, interfaceContext)] : [];
|
||||
});
|
||||
const interfaceImports = uniqueExactRevisions([
|
||||
...(environment.interfaceClosure ?? []),
|
||||
...[...(environment.interfaces?.values() ?? [])],
|
||||
]);
|
||||
|
||||
const packageImports = items.flatMap((item) => {
|
||||
const packageContext = item.packageDecl();
|
||||
return packageContext ? [lowerPackage(state, packageContext)] : [];
|
||||
});
|
||||
const packageImports = uniqueExactRevisions([
|
||||
...(environment.packageClosure ?? []),
|
||||
...[...(environment.packages?.values() ?? [])],
|
||||
]);
|
||||
|
||||
const sharedAttachments: PersistentAttachment[] = [];
|
||||
const privateAttachments = new Map<
|
||||
@@ -1520,23 +1662,26 @@ const lowerWorkspace = (
|
||||
});
|
||||
|
||||
return {
|
||||
id: capabilityId.workspaceRevision(stringValue(context.stringLiteral(1))),
|
||||
workspaceId: capabilityId.workspace(stringValue(context.stringLiteral(0))),
|
||||
parentRevisionIds: [],
|
||||
sourceRootCommit: stringValue(context.stringLiteral(2)),
|
||||
atoms,
|
||||
sharedAttachments,
|
||||
interfaceImports,
|
||||
packageImports,
|
||||
conformances,
|
||||
constructors,
|
||||
imports,
|
||||
workspace: {
|
||||
id: capabilityId.workspaceRevision(stringValue(context.stringLiteral(1))),
|
||||
workspaceId: capabilityId.workspace(stringValue(context.stringLiteral(0))),
|
||||
parentRevisionIds: [],
|
||||
sourceRootCommit: stringValue(context.stringLiteral(2)),
|
||||
atoms,
|
||||
sharedAttachments,
|
||||
interfaceImports,
|
||||
packageImports,
|
||||
conformances,
|
||||
constructors,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const compileCapabilitySource = (
|
||||
const parseDocument = (
|
||||
source: string,
|
||||
fileName = "<memory>",
|
||||
): CapabilitySourceCompileResult => {
|
||||
fileName: string,
|
||||
): { tree: DocumentContext; diagnostics: CapabilitySourceDiagnostic[] } => {
|
||||
const diagnostics: CapabilitySourceDiagnostic[] = [];
|
||||
const listener = new SyntaxErrorListener(fileName, diagnostics);
|
||||
const lexer = new QuixosCapabilityLexer(CharStream.fromString(source));
|
||||
@@ -1548,36 +1693,274 @@ export const compileCapabilitySource = (
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(listener);
|
||||
const tree = parser.document();
|
||||
return { tree, diagnostics };
|
||||
};
|
||||
|
||||
const newLoweringState = (
|
||||
fileName: string,
|
||||
diagnostics: CapabilitySourceDiagnostic[],
|
||||
): LoweringState => ({
|
||||
fileName,
|
||||
diagnostics,
|
||||
atoms: new Map(),
|
||||
interfaces: new Map(),
|
||||
packages: new Map(),
|
||||
attachments: new Map(),
|
||||
});
|
||||
|
||||
const validationDiagnostics = (
|
||||
fileName: string,
|
||||
issues: ReturnType<typeof compileWorkspaceRevision> extends infer _Result
|
||||
? Array<{ code: string; message: string; path: string }>
|
||||
: never,
|
||||
): CapabilitySourceDiagnostic[] => issues.map((entry) => ({
|
||||
phase: "validation",
|
||||
code: entry.code,
|
||||
message: entry.message,
|
||||
fileName,
|
||||
line: 0,
|
||||
column: 0,
|
||||
path: entry.path,
|
||||
}));
|
||||
|
||||
const externalAtomsFrom = (
|
||||
state: LoweringState,
|
||||
contexts: readonly ExternalAtomDeclContext[],
|
||||
): AtomDefinition[] => contexts.map((context) => {
|
||||
const atom: AtomDefinition = {
|
||||
id: capabilityId.atom(stringValue(context.stringLiteral())),
|
||||
displayName: identifier(context.identifier()),
|
||||
};
|
||||
declareSymbol(
|
||||
state,
|
||||
state.atoms,
|
||||
atom.displayName,
|
||||
atom.id,
|
||||
context,
|
||||
"external atom",
|
||||
);
|
||||
return atom;
|
||||
});
|
||||
|
||||
const externalInterfacesFrom = (
|
||||
state: LoweringState,
|
||||
contexts: readonly ExternalInterfaceDeclContext[],
|
||||
): CapabilityExternalInterface[] => contexts.map((context) => {
|
||||
const requirement: CapabilityExternalInterface = {
|
||||
binding: identifier(context.identifier()),
|
||||
revisionId: capabilityId.interfaceRevision(stringValue(context.stringLiteral())),
|
||||
};
|
||||
declareSymbol(
|
||||
state,
|
||||
state.interfaces,
|
||||
requirement.binding,
|
||||
{
|
||||
revisionId: requirement.revisionId,
|
||||
contractAvailable: false,
|
||||
members: new Map(),
|
||||
},
|
||||
context,
|
||||
"external interface",
|
||||
);
|
||||
return requirement;
|
||||
});
|
||||
|
||||
const resourcePreambleParts = (contexts: readonly ResourcePreambleContext[]) => ({
|
||||
imports: contexts.flatMap((context) => context.resourceImportDecl() ?? []),
|
||||
atoms: contexts.flatMap((context) => context.externalAtomDecl() ?? []),
|
||||
interfaces: contexts.flatMap((context) => context.externalInterfaceDecl() ?? []),
|
||||
});
|
||||
|
||||
const resourceValidationWorkspace = (
|
||||
resource: CapabilityResource,
|
||||
environment: CapabilityImportEnvironment,
|
||||
): WorkspaceRevision => {
|
||||
const resolvedInterfaceIds = new Set([
|
||||
...(environment.interfaceClosure ?? []).map((revision) => revision.revisionId),
|
||||
...[...(environment.interfaces?.values() ?? [])].map((revision) => revision.revisionId),
|
||||
...(resource.kind === "interface" ? [resource.revision.revisionId] : []),
|
||||
]);
|
||||
return ({
|
||||
id: capabilityId.workspaceRevision("workspace-revision:resource-validation"),
|
||||
workspaceId: capabilityId.workspace("workspace:resource-validation"),
|
||||
parentRevisionIds: [],
|
||||
sourceRootCommit: resource.revision.source.commit,
|
||||
atoms: uniqueAtoms([
|
||||
...(environment.externalAtoms ?? []),
|
||||
...resource.externalAtoms,
|
||||
]),
|
||||
sharedAttachments: [],
|
||||
interfaceImports: uniqueExactRevisions([
|
||||
...(environment.interfaceClosure ?? []),
|
||||
...[...(environment.interfaces?.values() ?? [])],
|
||||
...(resource.kind === "interface" ? [resource.revision] : []),
|
||||
...resource.externalInterfaces
|
||||
.filter((requirement) => !resolvedInterfaceIds.has(requirement.revisionId))
|
||||
.map((requirement): InterfaceRevision => ({
|
||||
interfaceId: capabilityId.interface(`external:${requirement.revisionId}`),
|
||||
revisionId: requirement.revisionId,
|
||||
displayName: requirement.binding,
|
||||
source: {
|
||||
repository: `https://external.invalid/${encodeURIComponent(requirement.revisionId)}.git`,
|
||||
commit: "0".repeat(40),
|
||||
},
|
||||
members: [],
|
||||
})),
|
||||
]),
|
||||
packageImports: uniqueExactRevisions([
|
||||
...(environment.packageClosure ?? []),
|
||||
...[...(environment.packages?.values() ?? [])],
|
||||
...(resource.kind === "package" ? [resource.revision] : []),
|
||||
]),
|
||||
conformances: [],
|
||||
constructors: [],
|
||||
});
|
||||
};
|
||||
|
||||
export const compileCapabilityResourceSource = (
|
||||
sourceText: string,
|
||||
options: {
|
||||
source: SourceRevision;
|
||||
fileName?: string;
|
||||
environment?: CapabilityImportEnvironment;
|
||||
},
|
||||
): CapabilityResourceCompileResult => {
|
||||
const fileName = options.fileName ?? "<memory>";
|
||||
const environment = options.environment ?? {};
|
||||
const { tree, diagnostics } = parseDocument(sourceText, fileName);
|
||||
if (diagnostics.length > 0) {
|
||||
return { ok: false, diagnostics };
|
||||
}
|
||||
|
||||
const state: LoweringState = {
|
||||
fileName,
|
||||
diagnostics,
|
||||
atoms: new Map(),
|
||||
interfaces: new Map(),
|
||||
packages: new Map(),
|
||||
attachments: new Map(),
|
||||
};
|
||||
const workspace = lowerWorkspace(state, tree.workspaceDecl());
|
||||
const interfaceContext = tree.interfaceResourceDecl();
|
||||
const packageContext = tree.packageResourceDecl();
|
||||
if (!interfaceContext && !packageContext) {
|
||||
return {
|
||||
ok: false,
|
||||
diagnostics: [{
|
||||
phase: "lowering",
|
||||
code: "expected-resource",
|
||||
message: "Expected a standalone interface or package resource document",
|
||||
fileName,
|
||||
line: 1,
|
||||
column: 0,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const state = newLoweringState(fileName, diagnostics);
|
||||
const preamble = resourcePreambleParts(
|
||||
(interfaceContext ?? packageContext)!.resourcePreamble(),
|
||||
);
|
||||
const externalAtoms = externalAtomsFrom(state, preamble.atoms);
|
||||
const imports = registerImports(state, preamble.imports, environment);
|
||||
const externalInterfaces = externalInterfacesFrom(state, preamble.interfaces);
|
||||
|
||||
let resource: CapabilityResource;
|
||||
if (interfaceContext) {
|
||||
const alias = identifier(interfaceContext.identifier());
|
||||
declareSymbol(
|
||||
state,
|
||||
state.interfaces,
|
||||
alias,
|
||||
{
|
||||
revisionId: capabilityId.interfaceRevision(
|
||||
stringValue(interfaceContext.stringLiteral(1)),
|
||||
),
|
||||
contractAvailable: true,
|
||||
members: new Map(),
|
||||
},
|
||||
interfaceContext,
|
||||
"interface",
|
||||
);
|
||||
resource = {
|
||||
kind: "interface",
|
||||
imports,
|
||||
externalAtoms,
|
||||
externalInterfaces,
|
||||
revision: lowerInterface(state, interfaceContext, options.source),
|
||||
};
|
||||
} else {
|
||||
const context = packageContext!;
|
||||
const alias = identifier(context.identifier());
|
||||
declareSymbol(
|
||||
state,
|
||||
state.packages,
|
||||
alias,
|
||||
{
|
||||
revisionId: capabilityId.packageRevision(
|
||||
stringValue(context.stringLiteral(1)),
|
||||
),
|
||||
exports: new Map(),
|
||||
},
|
||||
context,
|
||||
"package",
|
||||
);
|
||||
resource = {
|
||||
kind: "package",
|
||||
imports,
|
||||
externalAtoms,
|
||||
externalInterfaces,
|
||||
revision: lowerPackage(state, context, options.source),
|
||||
};
|
||||
}
|
||||
|
||||
if (diagnostics.length > 0) {
|
||||
return { ok: false, diagnostics };
|
||||
}
|
||||
const compiled = compileWorkspaceRevision(workspace);
|
||||
const compiled = compileWorkspaceRevision(
|
||||
resourceValidationWorkspace(resource, environment),
|
||||
);
|
||||
if (!compiled.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
diagnostics: compiled.issues.map((entry) => ({
|
||||
phase: "validation",
|
||||
code: entry.code,
|
||||
message: entry.message,
|
||||
fileName,
|
||||
line: 0,
|
||||
column: 0,
|
||||
path: entry.path,
|
||||
})),
|
||||
diagnostics: validationDiagnostics(fileName, compiled.issues),
|
||||
};
|
||||
}
|
||||
return { ok: true, workspace, plan: compiled.plan, diagnostics: [] };
|
||||
return { ok: true, resource, diagnostics: [] };
|
||||
};
|
||||
|
||||
export const compileCapabilitySource = (
|
||||
source: string,
|
||||
fileName = "<memory>",
|
||||
environment: CapabilityImportEnvironment = {},
|
||||
): CapabilitySourceCompileResult => {
|
||||
const { tree, diagnostics } = parseDocument(source, fileName);
|
||||
if (diagnostics.length > 0) {
|
||||
return { ok: false, diagnostics };
|
||||
}
|
||||
const workspaceContext = tree.workspaceDecl();
|
||||
if (!workspaceContext) {
|
||||
return {
|
||||
ok: false,
|
||||
diagnostics: [{
|
||||
phase: "lowering",
|
||||
code: "expected-workspace",
|
||||
message: "Expected a workspace document",
|
||||
fileName,
|
||||
line: 1,
|
||||
column: 0,
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
const state = newLoweringState(fileName, diagnostics);
|
||||
const lowered = lowerWorkspace(state, workspaceContext, environment);
|
||||
if (diagnostics.length > 0) {
|
||||
return { ok: false, diagnostics };
|
||||
}
|
||||
const compiled = compileWorkspaceRevision(lowered.workspace);
|
||||
if (!compiled.ok) {
|
||||
return {
|
||||
ok: false,
|
||||
diagnostics: validationDiagnostics(fileName, compiled.issues),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
workspace: lowered.workspace,
|
||||
plan: compiled.plan,
|
||||
imports: lowered.imports,
|
||||
diagnostics: [],
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user